From 8fcf5c33be290107868aff741257d9f2f55cb742 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 26 Oct 2017 14:52:36 -0600 Subject: [PATCH 0001/1694] Implement COR SEC Return File Processing (#141) * Allows for COR SEC returns and returnAddendums Thanks @ killianbrackey and @Sezzle for all the work in adding support. * Missing Batch.go changes to support returnAddenda * Adding ReturnCode validation against a returnCodeDictionary * Added support for writing a returnAddenda & test coverage --- addenda.go | 1 - batch.go | 29 +++--- batchCOR.go | 66 ++++++++++++++ entryDetail.go | 30 ++++++- file.go | 3 + reader.go | 12 +++ returnAddenda.go | 201 ++++++++++++++++++++++++++++++++++++++++++ returnAddenda_test.go | 152 ++++++++++++++++++++++++++++++++ validators.go | 2 + 9 files changed, 478 insertions(+), 18 deletions(-) create mode 100644 batchCOR.go create mode 100644 returnAddenda.go create mode 100644 returnAddenda_test.go diff --git a/addenda.go b/addenda.go index a818e6a59..9a8b01577 100644 --- a/addenda.go +++ b/addenda.go @@ -63,7 +63,6 @@ func (addenda *Addenda) Parse(record string) { addenda.SequenceNumber = addenda.parseNumField(record[83:87]) // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record addenda.EntryDetailSequenceNumber = addenda.parseNumField(record[87:94]) - } // String writes the Addenda struct to a 94 character string. diff --git a/batch.go b/batch.go index 2365585da..089e80cda 100644 --- a/batch.go +++ b/batch.go @@ -20,6 +20,8 @@ func NewBatch(bp BatchParam) (Batcher, error) { return NewBatchWEB(bp), nil case "CCD": return NewBatchCCD(bp), nil + case "COR": + return NewBatchCOR(bp), nil default: msg := fmt.Sprintf(msgFileNoneSEC, sec) return nil, &FileError{FieldName: "StandardEntryClassCode", Msg: msg} @@ -185,7 +187,7 @@ func (batch *batch) isFieldInclusion() error { func (batch *batch) isBatchEntryCount() error { entryCount := 0 for _, entry := range batch.entries { - entryCount = entryCount + 1 + len(entry.Addendum) + entryCount = entryCount + 1 + len(entry.Addendum) + len(entry.ReturnAddendum) } if entryCount != batch.control.EntryAddendaCount { msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.control.EntryAddendaCount) @@ -213,18 +215,10 @@ func (batch *batch) isBatchAmount() error { func (batch *batch) calculateBatchAmounts() (credit int, debit int) { for _, entry := range batch.entries { - if entry.TransactionCode == 22 || entry.TransactionCode == 23 { + if entry.TransactionCode == 21 || entry.TransactionCode == 22 || entry.TransactionCode == 23 || entry.TransactionCode == 32 || entry.TransactionCode == 33 { credit = credit + entry.Amount } - if entry.TransactionCode == 27 || entry.TransactionCode == 28 { - debit = debit + entry.Amount - } - // savings credit - if entry.TransactionCode == 32 || entry.TransactionCode == 33 { - credit = credit + entry.Amount - } - // savings debit - if entry.TransactionCode == 37 || entry.TransactionCode == 38 { + if entry.TransactionCode == 26 || entry.TransactionCode == 27 || entry.TransactionCode == 28 || entry.TransactionCode == 36 || entry.TransactionCode == 37 || entry.TransactionCode == 38 { debit = debit + entry.Amount } } @@ -323,9 +317,16 @@ func (batch *batch) isAddendaSequence() error { // "PPD", "WEB", "CCD", "CIE", "DNE", "MTE", "POS", "SHR" func (batch *batch) isAddendaCount(count int) error { for _, entry := range batch.entries { - if len(entry.Addendum) > count { - msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaCount", Msg: msg} + if !entry.HasReturnAddenda() { + if len(entry.Addendum) > count { + msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.header.StandardEntryClassCode) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaCount", Msg: msg} + } + } else { + if len(entry.ReturnAddendum) > count { + msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.ReturnAddendum), count, batch.header.StandardEntryClassCode) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "ReturnAddendaCount", Msg: msg} + } } } return nil diff --git a/batchCOR.go b/batchCOR.go new file mode 100644 index 000000000..6663b4089 --- /dev/null +++ b/batchCOR.go @@ -0,0 +1,66 @@ +package ach + +import ( + "fmt" +) + +// BatchCOR COR - Automated Notification of Change or Refused Notification of Change +// This Standard Entry Class Code is used by an RDFI or ODFI when originating a Notification of Change or Refused Notification of Change in automated format. +// It is also used by the ACH operator that converts paper Notifications of Change to automated format. +type BatchCOR struct { + batch +} + +// NewBatchCOR returns a *BatchCOR +func NewBatchCOR(params ...BatchParam) *BatchCOR { + batch := new(BatchCOR) + batch.SetControl(NewBatchControl()) + + if len(params) > 0 { + bh := NewBatchHeader(params[0]) + bh.StandardEntryClassCode = "COR" + batch.SetHeader(bh) + return batch + } + bh := NewBatchHeader() + bh.StandardEntryClassCode = "COR" + batch.SetHeader(bh) + return batch +} + +// Validate ensures the batch meets NACHA rules specific to this batch type. +func (batch *BatchCOR) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + // Web can have up to one addenda per entry record + if err := batch.isAddendaCount(1); err != nil { + return err + } + if err := batch.isTypeCode("05"); err != nil { + return err + } + + // Add type specific validation. + if batch.header.StandardEntryClassCode != "COR" { + msg := fmt.Sprintf(msgBatchSECType, batch.header.StandardEntryClassCode, "COR") + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + return nil +} + +// Create builds the batch sequence numbers and batch control. Additional creation +func (batch *BatchCOR) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + + if err := batch.Validate(); err != nil { + return err + } + return nil +} diff --git a/entryDetail.go b/entryDetail.go index 6fc6b26ef..d4b7915b2 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -76,6 +76,8 @@ type EntryDetail struct { // Addendum a list of Addenda for the Entry Detail Addendum []Addenda + // ReturnAddendum stores COR sec return types. These are processed separately + ReturnAddendum []ReturnAddenda // validator is composed for data validation validator // converters is composed for ACH to golang Converters @@ -217,9 +219,10 @@ func (ed *EntryDetail) fieldInclusion() error { if ed.DFIAccountNumber == "" { return &FieldError{FieldName: "DFIAccountNumber", Value: ed.DFIAccountNumber, Msg: msgFieldInclusion} } - if ed.Amount == 0 { + // TODO: amount can be 0 if it's COR, should probably be more specific... + /*if ed.Amount == 0 { return &FieldError{FieldName: "Amount", Value: ed.AmountField(), Msg: msgFieldInclusion} - } + }*/ if ed.IndividualName == "" { return &FieldError{FieldName: "IndividualName", Value: ed.IndividualName, Msg: msgFieldInclusion} } @@ -232,10 +235,25 @@ func (ed *EntryDetail) fieldInclusion() error { // AddAddenda appends an EntryDetail to the Addendum func (ed *EntryDetail) AddAddenda(addenda Addenda) []Addenda { ed.AddendaRecordIndicator = 1 + // checks to make sure that we only have either or, not both + if ed.ReturnAddendum != nil { + return nil + } ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum } +// AddReturnAddenda appends an ReturnAddendum to the entry +func (ed *EntryDetail) AddReturnAddenda(returnAddendum ReturnAddenda) []ReturnAddenda { + ed.AddendaRecordIndicator = 1 + // checks to make sure that we only have either or, not both + if ed.Addendum != nil { + return nil + } + ed.ReturnAddendum = append(ed.ReturnAddendum, returnAddendum) + return ed.ReturnAddendum +} + // SetRDFI takes the 9 digit RDFI account number and separates it for RDFIIdentification and CheckDigit func (ed *EntryDetail) SetRDFI(rdfi int) *EntryDetail { s := ed.numericField(rdfi, 9) @@ -280,6 +298,7 @@ func (ed *EntryDetail) ReceivingCompanyField() string { return ed.IndividualNameField() } +// SetReceivingCompany setter for CCD receiving company individual name func (ed *EntryDetail) SetReceivingCompany(s string) { ed.IndividualName = s } @@ -289,7 +308,7 @@ func (ed *EntryDetail) DiscretionaryDataField() string { return ed.alphaField(ed.DiscretionaryData, 2) } -// PaymentType returns the discretionary data field used in WEB batch files +// PaymentTypeField returns the discretionary data field used in WEB batch files func (ed *EntryDetail) PaymentTypeField() string { // because DiscretionaryData can be changed outside of PaymentType we reset the value for safety ed.SetPaymentType(ed.DiscretionaryData) @@ -310,3 +329,8 @@ func (ed *EntryDetail) SetPaymentType(t string) { func (ed *EntryDetail) TraceNumberField() string { return ed.numericField(ed.TraceNumber, 15) } + +// HasReturnAddenda returns true if entry has return addenda +func (ed *EntryDetail) HasReturnAddenda() bool { + return ed.ReturnAddendum != nil +} diff --git a/file.go b/file.go index 0da9082cd..b4c1a6eb1 100644 --- a/file.go +++ b/file.go @@ -30,6 +30,9 @@ const ( // currently supported SEC codes const ( ppd = "PPD" + web = "WEB" + ccd = "CCD" + cor = "COR" ) // Errors strings specific to parsing a Batch container diff --git a/reader.go b/reader.go index a99b9ead6..fb9861459 100644 --- a/reader.go +++ b/reader.go @@ -248,6 +248,18 @@ func (r *Reader) parseAddenda() error { msg := fmt.Sprintf(msgBatchAddendaIndicator) return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) } + case web, ccd, cor: // only care for returns + if entry.AddendaRecordIndicator == 1 { + returnAddenda := ReturnAddenda{} + returnAddenda.Parse(r.line) + if err := returnAddenda.Validate(); err != nil { + return r.error(err) + } + r.currentBatch.GetEntries()[entryIndex].AddReturnAddenda(returnAddenda) + } else { + msg := fmt.Sprintf(msgBatchAddendaIndicator) + return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) + } } return nil diff --git a/returnAddenda.go b/returnAddenda.go new file mode 100644 index 000000000..16b301d9b --- /dev/null +++ b/returnAddenda.go @@ -0,0 +1,201 @@ +package ach + +import ( + "fmt" + "strings" + "time" +) + +// When a Return Entry is prepared, the original Company/Batch Header Record, the original Entry Detail Record, +// and the Company/Batch Control Record are copied for return to the Originator. +// +// The Return Entry is a new Entry. These Entries must be assigned new batch and trace numbers, new identification numbers for the returning institution, +// appropriate transaction codes, etc., as required per format specifications. +// +// See Appendix Four: Return Entries in the NACHA Corporate + +var ( + returnCodeDict = map[string]*returnCode{} + + // Error messages specific to Return Addenda + msgReturnAddendaReturnCode = "found is not a valid return code" +) + +func init() { + // populate the returnCode map with lookup values + returnCodeDict = makeReturnCodeDict() +} + +// ReturnAddenda utilized for Notification of Change Entry (COR) and Return types. +type ReturnAddenda struct { + // RecordType defines the type of record in the block. entryAddendaPos 7 + recordType string + // TypeCode Addenda types code '99' + TypeCode string + // ReturnCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for returning an Entry. + // Must exist in returnCodeDict + ReturnCode string + // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. + // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, + // in the Addenda Record of an NOC, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. + OriginalTrace int + // DateOfDeath The field date of death is to be supplied on Entries being returned for reason of death (return reason codes R14 and R15). + DateOfDeath time.Time + // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. + OriginalDFI int + // AddendaInformation + AddendaInformation string + // TraceNumber matches the Entry Detail Trace Number of the entry being returned. + TraceNumber int + + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// returnCode holds a return Code, Reason/Title, and Description +// +// Table of return codes exists in Part 4.2 of the NACHA corporate rules and guidelines +type returnCode struct { + Code, Reason, Description string +} + +// NewReturnAddenda returns a new ReturnAddenda with default values for none exported fields +func NewReturnAddenda(params ...AddendaParam) ReturnAddenda { + rAddenda := ReturnAddenda{ + recordType: "7", + TypeCode: "99", + } + return rAddenda +} + +// Parse takes the input record string and parses the ReturnAddenda values +func (returnAddenda *ReturnAddenda) Parse(record string) { + // 1-1 Always "7" + returnAddenda.recordType = "7" + // 2-3 Defines the specific explanation and format for the addenda information contained in the same record + returnAddenda.TypeCode = record[1:3] + // 4-6 + returnAddenda.ReturnCode = record[3:6] + // 7-21 + returnAddenda.OriginalTrace = returnAddenda.parseNumField(record[6:21]) + // 22-27, might be a date or blank + returnAddenda.DateOfDeath = returnAddenda.parseSimpleDate(record[21:27]) + // 28-35 + returnAddenda.OriginalDFI = returnAddenda.parseNumField(record[27:35]) + // 36-79 + returnAddenda.AddendaInformation = strings.TrimSpace(record[35:79]) + // 80-94 + returnAddenda.TraceNumber = returnAddenda.parseNumField(record[79:94]) +} + +// String writes the ReturnAddenda struct to a 94 character string +func (returnAddenda *ReturnAddenda) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v", + returnAddenda.recordType, + returnAddenda.TypeCode, + returnAddenda.ReturnCode, + returnAddenda.OriginalTraceField(), + returnAddenda.DateOfDeathField(), + returnAddenda.OriginalDFIField(), + returnAddenda.AddendaInformationField(), + returnAddenda.TraceNumberField(), + ) +} + +// Validate verifies NACHA rules for ReturnAddenda +func (returnAddenda *ReturnAddenda) Validate() error { + + if returnAddenda.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: returnAddenda.recordType, Msg: msg} + } + // @TODO Type Code should be 99. + + _, ok := returnCodeDict[returnAddenda.ReturnCode] + if !ok { + // Return Addenda requires a valid ReturnCode + return &FieldError{FieldName: "ReturnCode", Value: returnAddenda.ReturnCode, Msg: msgReturnAddendaReturnCode} + } + return nil +} + +// OriginalTraceField returns a zero padded OriginalTrace string +func (returnAddenda *ReturnAddenda) OriginalTraceField() string { + return returnAddenda.numericField(returnAddenda.OriginalTrace, 15) +} + +// DateOfDeathField returns a space padded DateOfDeath string +func (returnAddenda *ReturnAddenda) DateOfDeathField() string { + // Return space padded 6 characters if it is a zero value of DateOfDeath + if returnAddenda.DateOfDeath.IsZero() { + return returnAddenda.alphaField("", 6) + } + // YYMMDD + return returnAddenda.formatSimpleDate(returnAddenda.DateOfDeath) +} + +// OriginalDFIField returns a zero padded OriginalDFI string +func (returnAddenda *ReturnAddenda) OriginalDFIField() string { + return returnAddenda.numericField(returnAddenda.OriginalDFI, 8) +} + +//AddendaInformationField returns a space padded AddendaInformation string +func (returnAddenda *ReturnAddenda) AddendaInformationField() string { + return returnAddenda.alphaField(returnAddenda.AddendaInformation, 44) +} + +// TraceNumberField returns a zero padded traceNumber string +func (returnAddenda *ReturnAddenda) TraceNumberField() string { + return returnAddenda.numericField(returnAddenda.TraceNumber, 15) +} + +func makeReturnCodeDict() map[string]*returnCode { + dict := make(map[string]*returnCode) + + codes := []returnCode{ + // Return Reason Codes for RDFIs + {"R01", "Insufficient Funds", "Available balance is not sufficient to cover the dollar value of the debit entry"}, + {"R02", "Account Closed", "Previously active account has been closed by customer or RDFI"}, + {"R03", "No Account/Unable to Locate Account", "Account number structure is valid and passes editing process, but does not correspond to individual or is not an open account"}, + {"R04", "Invalid Account Number", "Account number structure not valid; entry may fail check digit validation or may contain an incorrect number of digits."}, + {"R05", "Improper Debit to Consumer Account", "A CCD, CTX, or CBR debit entry was transmitted to a Consumer Account of the Receiver and was not authorized by the Receiver"}, + {"R06", "Returned per ODFI's Request", "ODFI has requested RDFI to return the ACH entry (optional to RDFI - ODFI indemnifies RDFI)}"}, + {"R07", "Authorization Revoked by Customer", "Consumer, who previously authorized ACH payment, has revoked authorization from Originator (must be returned no later than 60 days from settlement date and customer must sign affidavit)"}, + {"R08", "Payment Stopped", "Receiver of a recurring debit transaction has stopped payment to a specific ACH debit. RDFI should verify the Receiver's intent when a request for stop payment is made to insure this is not intended to be a revocation of authorization"}, + {"R09", "Uncollected Funds", "Sufficient book or ledger balance exists to satisfy dollar value of the transaction, but the dollar value of transaction is in process of collection (i.e., uncollected checks) or cash reserve balance below dollar value of the debit entry."}, + {"R10", "Customer Advises Not Authorized", "Consumer has advised RDFI that Originator of transaction is not authorized to debit account (must be returned no later than 60 days from settlement date of original entry and customer must sign affidavit)."}, + {"R11", "Check Truncation Entry Returned", "Used when returning a check safekeeping entry; RDFI should use appropriate field in addenda record to specify reason for return (i.e., 'exceeds dollar limit,' 'stale date,' etc.)."}, + {"R12", "Branch Sold to Another DFI", "Financial institution receives entry destined for an account at a branch that has been sold to another financial institution."}, + {"R13", "RDFI not qualified to participate", "Financial institution does not receive commercial ACH entries"}, + {"R14", "Representative payee deceased or unable to continue in that capacity", "The representative payee authorized to accept entries on behalf of a beneficiary is either deceased or unable to continue in that capacity"}, + {"R15", "Beneficiary or bank account holder", "(Other than representative payee) deceased* - (1) the beneficiary entitled to payments is deceased or (2) the bank account holder other than a representative payee is deceased"}, + {"R16", "Bank account frozen", "Funds in bank account are unavailable due to action by RDFI or legal order"}, + {"R17", "File record edit criteria", "Fields rejected by RDFI processing (identified in return addenda)"}, + {"R18", "Improper effective entry date", "Entries have been presented prior to the first available processing window for the effective date."}, + {"R19", "Amount field error", "Improper formatting of the amount field"}, + {"R20", "Non-payment bank account", "Entry destined for non-payment bank account defined by reg."}, + {"R21", "Invalid company ID number", "The company ID information not valid (normally CIE entries)"}, + {"R22", "Invalid individual ID number", "Individual id used by receiver is incorrect (CIE entries)"}, + {"R23", "Credit entry refused by receiver", "Receiver returned entry because minimum or exact amount not remitted, bank account is subject to litigation, or payment represents an overpayment, originator is not known to receiver or receiver has not authorized this credit entry to this bank account"}, + {"R24", "Duplicate entry", "RDFI has received a duplicate entry"}, + {"R25", "Addenda error", "Improper formatting of the addenda record information"}, + {"R26", "Mandatory field error", "Improper information in one of the mandatory fields"}, + {"R27", "Trace number error", "Original entry trace number is not valid for return entry; or addenda trace numbers do not correspond with entry detail record"}, + {"R28", "Transit routing number check digit error", "Check digit for the transit routing number is incorrect"}, + {"R29", "Corporate customer advises not authorized", "RDFI has bee notified by corporate receiver that debit entry of originator is not authorized"}, + {"R30", "RDFI not participant in check truncation program", "Financial institution not participating in automated check safekeeping application"}, + {"R31", "Permissible return entry (CCD and CTX only)", "RDFI has been notified by the ODFI that it agrees to accept a CCD or CTX return entry"}, + {"R32", "RDFI non-settlement", "RDFI is not able to settle the entry"}, + {"R33", "Return of XCK entry", "RDFI determines at its sole discretion to return an XCK entry; an XCK return entry may be initiated by midnight of the sixtieth day following the settlement date if the XCK entry"}, + {"R34", "Limited participation RDFI", "RDFI participation has been limited by a federal or state supervisor"}, + {"R35", "Return of improper debit entry", "ACH debit not permitted for use with the CIE standard entry class code (except for reversals)"}, + // More return codes will be added when more SEC types are added to the library. + } + // populate the map + for _, code := range codes { + dict[code.Code] = &code + } + return dict +} diff --git a/returnAddenda_test.go b/returnAddenda_test.go new file mode 100644 index 000000000..7f5b66bd4 --- /dev/null +++ b/returnAddenda_test.go @@ -0,0 +1,152 @@ +// Copyright 2017 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" + "time" +) + +func mockReturnAddenda() ReturnAddenda { + rAddenda := NewReturnAddenda() + rAddenda.TypeCode = "R07" + rAddenda.AddendaInformation = "Authorization Revoked" + + return rAddenda +} + +func TestMockReturnAddenda(t *testing.T) { + // TODO: build a mock addenda +} + +func TestReturnAddendaParse(t *testing.T) { + rAddenda := NewReturnAddenda() + line := "799R07099912340000015 09101298Authorization revoked 091012980000066" + rAddenda.Parse(line) + // walk the returnAddenda struct + if rAddenda.recordType != "7" { + t.Errorf("expected %v got %v", "7", rAddenda.recordType) + } + if rAddenda.TypeCode != "99" { + t.Errorf("expected %v got %v", "99", rAddenda.TypeCode) + } + if rAddenda.ReturnCode != "R07" { + t.Errorf("expected %v got %v", "R07", rAddenda.ReturnCode) + } + if rAddenda.OriginalTrace != 99912340000015 { + t.Errorf("expected: %v got: %v", 99912340000015, rAddenda.OriginalTrace) + } + if rAddenda.DateOfDeath.IsZero() != true { + t.Errorf("expected: %v got: %v", time.Time{}, rAddenda.DateOfDeath) + } + if rAddenda.OriginalDFI != 9101298 { + t.Errorf("expected: %v got: %v", 9101298, rAddenda.OriginalDFI) + } + if rAddenda.AddendaInformation != "Authorization revoked" { + t.Errorf("expected: %v got: %v", "Authorization revoked", rAddenda.AddendaInformation) + } + if rAddenda.TraceNumber != 91012980000066 { + t.Errorf("expected: %v got: %v", 91012980000066, rAddenda.TraceNumber) + } +} + +func TestReturnAddendaString(t *testing.T) { + rAddenda := NewReturnAddenda() + line := "799R07099912340000015 09101298Authorization revoked 091012980000066" + rAddenda.Parse(line) + + if rAddenda.String() != line { + t.Errorf("\n expected: %v\n got : %v", line, rAddenda.String()) + } +} + +// This is not an exported function but utilized for validation +func TestReturnAddendaMakeReturnCodeDict(t *testing.T) { + codes := makeReturnCodeDict() + // check if known code is present + _, prs := codes["R01"] + if !prs { + t.Error("Return Code R01 was not found in the ReturnCodeDict") + } + // check if invalid code is present + _, prs = codes["ABC"] + if prs { + t.Error("Valid return for an invalid return code key") + } +} + +func TestReturnAddendaValidateTrue(t *testing.T) { + rAddenda := mockReturnAddenda() + rAddenda.ReturnCode = "R13" + if err := rAddenda.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReturnCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestReturnAddendaValidateReturnCodeFalse(t *testing.T) { + rAddenda := mockReturnAddenda() + rAddenda.ReturnCode = "" + if err := rAddenda.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReturnCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestReturnAddendaOriginalTraceField(t *testing.T) { + rAddenda := mockReturnAddenda() + rAddenda.OriginalTrace = 12345 + if rAddenda.OriginalTraceField() != "000000000012345" { + t.Errorf("expected %v received %v", "000000000012345", rAddenda.OriginalTraceField()) + } +} + +func TestReturnAddendaDateOfDeathField(t *testing.T) { + rAddenda := mockReturnAddenda() + // Check for all zeros + if rAddenda.DateOfDeathField() != " " { + t.Errorf("expected %v received %v", " ", rAddenda.DateOfDeathField()) + } + // Year: 1978 Month: October Day: 23 + rAddenda.DateOfDeath = time.Date(1978, time.October, 23, 0, 0, 0, 0, time.UTC) + if rAddenda.DateOfDeathField() != "781023" { + t.Errorf("expected %v received %v", "781023", rAddenda.DateOfDeathField()) + } +} + +func TestReturnAddendaOriginalDFIField(t *testing.T) { + rAddenda := mockReturnAddenda() + exp := "00000000" + if rAddenda.OriginalDFIField() != exp { + t.Errorf("expected %v received %v", exp, rAddenda.OriginalDFIField()) + } +} + +func TestReturnAddendaAddendaInformationField(t *testing.T) { + rAddenda := mockReturnAddenda() + exp := "Authorization Revoked " + if rAddenda.AddendaInformationField() != exp { + t.Errorf("expected %v received %v", exp, rAddenda.AddendaInformationField()) + } +} + +func TestReturnAddendaTraceNumberField(t *testing.T) { + rAddenda := mockReturnAddenda() + rAddenda.TraceNumber = 91012980000066 + exp := "091012980000066" + if rAddenda.TraceNumberField() != exp { + t.Errorf("expected %v received %v", exp, rAddenda.TraceNumberField()) + } +} diff --git a/validators.go b/validators.go index 597d940e5..a5a167c69 100644 --- a/validators.go +++ b/validators.go @@ -107,6 +107,8 @@ func (v *validator) isTransactionCode(code int) error { 23, // Zero dollar with remittance data (CCD/CTX only) 24, + // Automated Return or Notification of Change for original transaction code 27, 28, or 29 + 26, // Debit (withdrawal) to checking account ‘27’ 27, // Prenote for debit to checking account ‘28’ From 41cffc2374b3cf0f4171020279472714fae663dd Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 26 Oct 2017 17:31:00 -0600 Subject: [PATCH 0002/1694] Addendumer works for all previous Addenda concrete types --- addenda.go | 57 +++++++++++++++++++++++++++++----------- addenda_internal_test.go | 19 +++++++------- addendumer.go | 10 +++++++ batch.go | 33 ++++++++++++++--------- batchCCD_test.go | 2 +- batchCOR.go | 4 ++- batchCOR_test.go | 56 +++++++++++++++++++++++++++++++++++++++ batchPPD_test.go | 10 +++---- batchWEB_test.go | 2 +- entryDetail.go | 10 +++---- reader.go | 38 ++++++++++----------------- recordParams_test.go | 6 ++--- 12 files changed, 170 insertions(+), 77 deletions(-) create mode 100644 addendumer.go create mode 100644 batchCOR_test.go diff --git a/addenda.go b/addenda.go index 9a8b01577..c1dadee96 100644 --- a/addenda.go +++ b/addenda.go @@ -11,7 +11,7 @@ type Addenda struct { // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string // TypeCode Addenda types code '05' - TypeCode string + typeCode string // PaymentRelatedInformation PaymentRelatedInformation string // SequenceNumber is consecutively assigned to each Addenda Record following @@ -31,23 +31,45 @@ type Addenda struct { // AddendaParam is the minimal fields required to make a ach addenda type AddendaParam struct { - PaymentRelatedInfo string `json:"payment_related_info"` + TypeCode string `json:"type_code,omitempty"` + PaymentRelatedInfo string `json:"payment_related_info,omitempty"` } // NewAddenda returns a new Addenda with default values for none exported fields -func NewAddenda(params ...AddendaParam) Addenda { +// TypeCode in AddendaParam for none ACK, ATX, CCD, CIE, CTX, DNE, ENR, PPD, TRX and WEB Entries +func NewAddenda(params ...AddendaParam) (Addendumer, error) { + if (len(params)) > 0 { + // most common use case is 05 ACK, ATX, CCD, CIE, CTX, DNE, ENR, PPD, TRX and WEB Entries + if params[0].TypeCode == "" { + params[0].TypeCode = "05" + } + switch typeCode := params[0].TypeCode; typeCode { + case "99": + return nil, nil + //return NewReturnAddenda(), nil + case "05": + addenda := Addenda{ + recordType: "7", + typeCode: "05", + SequenceNumber: 1, + EntryDetailSequenceNumber: 1, + } + addenda.PaymentRelatedInformation = params[0].PaymentRelatedInfo + return &addenda, nil + default: + msg := fmt.Sprintf("Addenda Type Code %v is not supported", typeCode) + return nil, &FileError{FieldName: "TypeCode", Msg: msg} + } + } + // TODO think about renaming Addenda to something for its TypeCode NewAddenda05 addenda := Addenda{ recordType: "7", - TypeCode: "05", + typeCode: "05", SequenceNumber: 1, EntryDetailSequenceNumber: 1, } + return &addenda, nil - if len(params) > 0 { - addenda.PaymentRelatedInformation = params[0].PaymentRelatedInfo - return addenda - } - return addenda } // Parse takes the input record string and parses the Addenda values @@ -55,7 +77,7 @@ func (addenda *Addenda) Parse(record string) { // 1-1 Always "7" addenda.recordType = "7" // 2-3 Defines the specific explanation and format for the addenda information contained in the same record - addenda.TypeCode = record[1:3] + addenda.typeCode = record[1:3] // 4-83 Based on the information entered (04-83) 80 alphanumeric addenda.PaymentRelatedInformation = strings.TrimSpace(record[3:83]) // 84-87 SequenceNumber is consecutively assigned to each Addenda Record following @@ -69,7 +91,7 @@ func (addenda *Addenda) Parse(record string) { func (addenda *Addenda) String() string { return fmt.Sprintf("%v%v%v%v%v", addenda.recordType, - addenda.TypeCode, + addenda.typeCode, addenda.PaymentRelatedInformationField(), addenda.SequenceNumberField(), addenda.EntryDetailSequenceNumberField()) @@ -85,8 +107,8 @@ func (addenda *Addenda) Validate() error { msg := fmt.Sprintf(msgRecordType, 7) return &FieldError{FieldName: "recordType", Value: addenda.recordType, Msg: msg} } - if err := addenda.isTypeCode(addenda.TypeCode); err != nil { - return &FieldError{FieldName: "TypeCode", Value: addenda.TypeCode, Msg: err.Error()} + if err := addenda.isTypeCode(addenda.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda.typeCode, Msg: err.Error()} } if err := addenda.isAlphanumeric(addenda.PaymentRelatedInformation); err != nil { return &FieldError{FieldName: "PaymentRelatedInformation", Value: addenda.PaymentRelatedInformation, Msg: err.Error()} @@ -101,8 +123,8 @@ func (addenda *Addenda) fieldInclusion() error { if addenda.recordType == "" { return &FieldError{FieldName: "recordType", Value: addenda.recordType, Msg: msgFieldInclusion} } - if addenda.TypeCode == "" { - return &FieldError{FieldName: "TypeCode", Value: addenda.TypeCode, Msg: msgFieldInclusion} + if addenda.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda.typeCode, Msg: msgFieldInclusion} } if addenda.SequenceNumber == 0 { return &FieldError{FieldName: "SequenceNumber", Value: addenda.SequenceNumberField(), Msg: msgFieldInclusion} @@ -127,3 +149,8 @@ func (addenda *Addenda) SequenceNumberField() string { func (addenda *Addenda) EntryDetailSequenceNumberField() string { return addenda.numericField(addenda.EntryDetailSequenceNumber, 7) } + +// TypeCode Defines the specific explanation and format for the addenda information +func (addenda *Addenda) TypeCode() string { + return addenda.typeCode +} diff --git a/addenda_internal_test.go b/addenda_internal_test.go index 82425c83d..deb0a5e9c 100644 --- a/addenda_internal_test.go +++ b/addenda_internal_test.go @@ -9,8 +9,9 @@ import ( "testing" ) -func mockAddenda() Addenda { - addenda := NewAddenda() +func mockAddenda() *Addenda { + a, _ := NewAddenda() + addenda := a.(*Addenda) addenda.EntryDetailSequenceNumber = 1234567 return addenda } @@ -26,7 +27,7 @@ func TestMockAddenda(t *testing.T) { } func TestParseAddenda(t *testing.T) { - var line = "710WEB DIEGO MAY 00010000001" + var line = "705WEB DIEGO MAY 00010000001" r := NewReader(strings.NewReader(line)) r.addCurrentBatch(NewBatchPPD()) @@ -37,13 +38,13 @@ func TestParseAddenda(t *testing.T) { if err != nil { t.Errorf("%T: %s", err, err) } - record := r.currentBatch.GetEntries()[0].Addendum[0] + record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda) if record.recordType != "7" { t.Errorf("RecordType Expected '7' got: %v", record.recordType) } - if record.TypeCode != "10" { - t.Errorf("TypeCode Expected 10 got: %v", record.TypeCode) + if record.TypeCode() != "05" { + t.Errorf("TypeCode Expected 10 got: %v", record.TypeCode()) } if record.PaymentRelatedInformationField() != "WEB DIEGO MAY " { t.Errorf("PaymentRelatedInformation Expected 'WEB DIEGO MAY ' got: %v", record.PaymentRelatedInformationField()) @@ -58,7 +59,7 @@ func TestParseAddenda(t *testing.T) { // TestAddendaString validats that a known parsed file can be return to a string of the same value func TestAddendaString(t *testing.T) { - var line = "710WEB DIEGO MAY 00010000001" + var line = "705WEB DIEGO MAY 00010000001" r := NewReader(strings.NewReader(line)) r.addCurrentBatch(NewBatchPPD()) r.currentBatch.GetHeader().StandardEntryClassCode = "PPD" @@ -88,7 +89,7 @@ func TestValidateAddendaRecordType(t *testing.T) { func TestValidateAddendaTypeCode(t *testing.T) { addenda := mockAddenda() - addenda.TypeCode = "23" + addenda.typeCode = "23" if err := addenda.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.FieldName != "TypeCode" { @@ -136,7 +137,7 @@ func TestAddendaPaymentRelatedInformationAlphaNumeric(t *testing.T) { func TestAddendaTyeCodeNil(t *testing.T) { addenda := mockAddenda() - addenda.TypeCode = "" + addenda.typeCode = "" if err := addenda.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.FieldName != "TypeCode" { diff --git a/addendumer.go b/addendumer.go new file mode 100644 index 000000000..47752a148 --- /dev/null +++ b/addendumer.go @@ -0,0 +1,10 @@ +package ach + +// Addendumer abstracts the different ACH addendum types that can be added to an EntryDetail record +type Addendumer interface { + Parse(string) + //TypeCode Defines the specific explanation and format for the addenda information + TypeCode() string + String() string + Validate() error +} diff --git a/batch.go b/batch.go index 089e80cda..e53ed8d2a 100644 --- a/batch.go +++ b/batch.go @@ -84,7 +84,7 @@ func (batch *batch) verify() error { if err := batch.isTraceNumberODFI(); err != nil { return err } - + // TODO this is specific to batch SEC types and should be called by that validator if err := batch.isAddendaSequence(); err != nil { return err } @@ -110,8 +110,11 @@ func (batch *batch) build() error { seq++ addendaSeq := 1 for x := range entry.Addendum { - batch.entries[i].Addendum[x].SequenceNumber = addendaSeq - batch.entries[i].Addendum[x].EntryDetailSequenceNumber = batch.parseNumField(batch.entries[i].TraceNumberField()[8:]) + // sequences don't exist in NOC or Return addenda + if a, ok := batch.entries[i].Addendum[x].(*Addenda); ok { + a.SequenceNumber = addendaSeq + a.EntryDetailSequenceNumber = batch.parseNumField(batch.entries[i].TraceNumberField()[8:]) + } addendaSeq++ } } @@ -296,15 +299,19 @@ func (batch *batch) isAddendaSequence() error { lastSeq := -1 // check if sequence is assending for _, addenda := range entry.Addendum { - if addenda.SequenceNumber < lastSeq { - msg := fmt.Sprintf(msgBatchAscending, addenda.SequenceNumber, lastSeq) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} - } - lastSeq = addenda.SequenceNumber - // check that we are in the correct Entry Detail - if !(addenda.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) { - msg := fmt.Sprintf(msgBatchAddendaTraceNumber, addenda.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + // sequences don't exist in NOC or Return addenda + if a, ok := addenda.(*Addenda); ok { + + if a.SequenceNumber < lastSeq { + msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastSeq) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} + } + lastSeq = a.SequenceNumber + // check that we are in the correct Entry Detail + if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) { + msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + } } } } @@ -336,7 +343,7 @@ func (batch *batch) isAddendaCount(count int) error { func (batch *batch) isTypeCode(typeCode string) error { for _, entry := range batch.entries { for _, addenda := range entry.Addendum { - if addenda.TypeCode != typeCode { + if addenda.TypeCode() != typeCode { msg := fmt.Sprintf(msgBatchTypeCode, addenda.TypeCode, typeCode, batch.header.StandardEntryClassCode) return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TypeCode", Msg: msg} } diff --git a/batchCCD_test.go b/batchCCD_test.go index b3292e9e8..ea63bdf20 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -74,7 +74,7 @@ func TestBatchCCDReceivingCompanyName(t *testing.T) { // verify addenda type code is 05 func TestBatchCCDAddendaTypeCode(t *testing.T) { mockBatch := mockBatchCCD() - mockBatch.GetEntries()[0].Addendum[0].TypeCode = "07" + mockBatch.GetEntries()[0].Addendum[0].(*Addenda).typeCode = "07" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "TypeCode" { diff --git a/batchCOR.go b/batchCOR.go index 6663b4089..ef4318755 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -4,7 +4,7 @@ import ( "fmt" ) -// BatchCOR COR - Automated Notification of Change or Refused Notification of Change +// BatchCOR COR - Automated Notification of Change (NOC) or Refused Notification of Change // This Standard Entry Class Code is used by an RDFI or ODFI when originating a Notification of Change or Refused Notification of Change in automated format. // It is also used by the ACH operator that converts paper Notifications of Change to automated format. type BatchCOR struct { @@ -49,6 +49,8 @@ func (batch *BatchCOR) Validate() error { return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } + // TODO check that addenda is type AddendaNOC + return nil } diff --git a/batchCOR_test.go b/batchCOR_test.go new file mode 100644 index 000000000..675e4119e --- /dev/null +++ b/batchCOR_test.go @@ -0,0 +1,56 @@ +package ach + +import "testing" + +// TODO make all the mock values cor fields + +func mockBatchCORHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "COR" + bh.CompanyName = "Your Company, inc" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "Vndr Pay" + bh.ODFIIdentification = 6200001 + return bh +} + +func mockCOREntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI(9101298) + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 5000000 + entry.IdentificationNumber = "location #23" + entry.SetReceivingCompany("Best Co. #23") + entry.TraceNumber = 123456789 + entry.DiscretionaryData = "S" + return entry +} + +// TODO make a addendaNOC for COR batches + +func mockBatchCOR() *BatchCOR { + mockBatch := NewBatchCOR() + mockBatch.SetHeader(mockBatchCORHeader()) + mockBatch.AddEntry(mockCOREntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +func TestBatchCORSEC(t *testing.T) { + mockBatch := mockBatchCOR() + mockBatch.header.StandardEntryClassCode = "RCK" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} diff --git a/batchPPD_test.go b/batchPPD_test.go index 20a741cf1..ca7a531bc 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -60,7 +60,7 @@ func TestBatchPPDTypeCode(t *testing.T) { mockBatch := mockBatchPPD() // change an addendum to an invalid type code a := mockAddenda() - a.TypeCode = "63" + a.typeCode = "63" mockBatch.GetEntries()[0].AddAddenda(a) mockBatch.Create() if err := mockBatch.Validate(); err != nil { @@ -268,8 +268,8 @@ func TestBatchIsAddendaSeqAscending(t *testing.T) { mockBatch.AddEntry(ed) mockBatch.Create() - mockBatch.GetEntries()[0].Addendum[0].SequenceNumber = 2 - mockBatch.GetEntries()[0].Addendum[1].SequenceNumber = 1 + mockBatch.GetEntries()[0].Addendum[0].(*Addenda).SequenceNumber = 2 + mockBatch.GetEntries()[0].Addendum[1].(*Addenda).SequenceNumber = 1 if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "SequenceNumber" { @@ -305,7 +305,7 @@ func TestBatchAddendaTraceNumber(t *testing.T) { t.Errorf("%T: %s", err, err) } - mockBatch.GetEntries()[0].Addendum[0].EntryDetailSequenceNumber = 99 + mockBatch.GetEntries()[0].Addendum[0].(*Addenda).EntryDetailSequenceNumber = 99 if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "TraceNumber" { @@ -338,7 +338,7 @@ func TestBatchBuild(t *testing.T) { entry.IdentificationNumber = "658-888-2468" // Unique ID for payment entry.IndividualName = "Wade Arnold" entry.setTraceNumber(header.ODFIIdentification, 1) - a1 := NewAddenda() + a1, _ := NewAddenda() entry.AddAddenda(a1) mockBatch.AddEntry(entry) if err := mockBatch.Create(); err != nil { diff --git a/batchWEB_test.go b/batchWEB_test.go index 50301d712..3d130efbf 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -88,7 +88,7 @@ func TestBatchWebIndividualNameRequired(t *testing.T) { // verify addenda type code is 05 func TestBatchWEBAddendaTypeCode(t *testing.T) { mockBatch := mockBatchWEB() - mockBatch.GetEntries()[0].Addendum[0].TypeCode = "07" + mockBatch.GetEntries()[0].Addendum[0].(*Addenda).typeCode = "07" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "TypeCode" { diff --git a/entryDetail.go b/entryDetail.go index d4b7915b2..f6cd900ae 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -75,8 +75,8 @@ type EntryDetail struct { TraceNumber int // Addendum a list of Addenda for the Entry Detail - Addendum []Addenda - // ReturnAddendum stores COR sec return types. These are processed separately + Addendum []Addendumer + // ReturnAddendum stores return types. These are processed separately ReturnAddendum []ReturnAddenda // validator is composed for data validation validator @@ -150,7 +150,7 @@ func (ed *EntryDetail) Parse(record string) { // 79-79 1 if addenda exists 0 if it does not ed.AddendaRecordIndicator = ed.parseNumField(record[78:79]) // 80-84 An internal identification (alphanumeric) that you use to uniquely identify - // this Entry Detail Recor This number should be unique to the transaction and will help identify the transaction in case of an inquiry + // this Entry Detail Record This number should be unique to the transaction and will help identify the transaction in case of an inquiry ed.TraceNumber = ed.parseNumField(record[79:94]) } @@ -232,8 +232,8 @@ func (ed *EntryDetail) fieldInclusion() error { return nil } -// AddAddenda appends an EntryDetail to the Addendum -func (ed *EntryDetail) AddAddenda(addenda Addenda) []Addenda { +// AddAddenda appends an Addendumer to the EntryDetail +func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.AddendaRecordIndicator = 1 // checks to make sure that we only have either or, not both if ed.ReturnAddendum != nil { diff --git a/reader.go b/reader.go index fb9861459..9c8b9c0b1 100644 --- a/reader.go +++ b/reader.go @@ -221,7 +221,7 @@ func (r *Reader) parseEntryDetail() error { return nil } -// parseAddendaRecord takes the input record string and parses the AddendaRecord values +// parseAddendaRecord takes the input record string and create an Addenda Type appended to the last EntryDetail func (r *Reader) parseAddenda() error { r.recordName = "Addenda" @@ -235,31 +235,21 @@ func (r *Reader) parseAddenda() error { entryIndex := len(r.currentBatch.GetEntries()) - 1 entry := r.currentBatch.GetEntries()[entryIndex] - switch sec := r.currentBatch.GetHeader().StandardEntryClassCode; sec { - case ppd: - if entry.AddendaRecordIndicator == 1 { - addenda := Addenda{} - addenda.Parse(r.line) - if err := addenda.Validate(); err != nil { - return r.error(err) - } - r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda) - } else { - msg := fmt.Sprintf(msgBatchAddendaIndicator) - return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) + if entry.AddendaRecordIndicator == 1 { + // Passing TypeCode type into NewAddenda creates a Addendumer of type copde type. + addenda, err := NewAddenda(AddendaParam{ + TypeCode: r.line[1:3]}) + if err != nil { + return r.error(err) } - case web, ccd, cor: // only care for returns - if entry.AddendaRecordIndicator == 1 { - returnAddenda := ReturnAddenda{} - returnAddenda.Parse(r.line) - if err := returnAddenda.Validate(); err != nil { - return r.error(err) - } - r.currentBatch.GetEntries()[entryIndex].AddReturnAddenda(returnAddenda) - } else { - msg := fmt.Sprintf(msgBatchAddendaIndicator) - return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) + addenda.Parse(r.line) + if err := addenda.Validate(); err != nil { + return r.error(err) } + r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda) + } else { + msg := fmt.Sprintf(msgBatchAddendaIndicator) + return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) } return nil diff --git a/recordParams_test.go b/recordParams_test.go index ad2aac61d..c5c23d155 100644 --- a/recordParams_test.go +++ b/recordParams_test.go @@ -85,7 +85,7 @@ func TestEntryParamReceivingCompany(t *testing.T) { } func TestAddendaParam(t *testing.T) { - addenda := NewAddenda(AddendaParam{ + addenda, _ := NewAddenda(AddendaParam{ PaymentRelatedInfo: "Currently string needs ASC X12 Interchange Control Structures", }) if err := addenda.Validate(); err != nil { @@ -124,7 +124,7 @@ func TestBuildFileParam(t *testing.T) { // To add one or more optional addenda records for an entry - addenda := NewAddenda(AddendaParam{ + addenda, _ := NewAddenda(AddendaParam{ PaymentRelatedInfo: "bonus pay for amazing work on #OSS"}) entry.AddAddenda(addenda) @@ -165,7 +165,7 @@ func TestBuildFileParam(t *testing.T) { IndividualName: "Wade Arnold", PaymentType: "R"}) - addenda = NewAddenda(AddendaParam{ + addenda, _ = NewAddenda(AddendaParam{ PaymentRelatedInfo: "Monthly Membership Subscription"}) // add the entry to the batch From 8a5648a023b039d692043092c7d039a97263bf08 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 26 Oct 2017 21:58:51 -0600 Subject: [PATCH 0003/1694] ReturnAddenda as an option in NewAddenda from reader --- addenda.go | 3 +-- returnAddenda.go | 17 +++++++++++------ returnAddenda_test.go | 9 +++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/addenda.go b/addenda.go index c1dadee96..0be8b31f2 100644 --- a/addenda.go +++ b/addenda.go @@ -45,8 +45,7 @@ func NewAddenda(params ...AddendaParam) (Addendumer, error) { } switch typeCode := params[0].TypeCode; typeCode { case "99": - return nil, nil - //return NewReturnAddenda(), nil + return NewReturnAddenda(), nil case "05": addenda := Addenda{ recordType: "7", diff --git a/returnAddenda.go b/returnAddenda.go index 16b301d9b..31ff57b14 100644 --- a/returnAddenda.go +++ b/returnAddenda.go @@ -31,7 +31,7 @@ type ReturnAddenda struct { // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string // TypeCode Addenda types code '99' - TypeCode string + typeCode string // ReturnCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for returning an Entry. // Must exist in returnCodeDict ReturnCode string @@ -62,10 +62,10 @@ type returnCode struct { } // NewReturnAddenda returns a new ReturnAddenda with default values for none exported fields -func NewReturnAddenda(params ...AddendaParam) ReturnAddenda { - rAddenda := ReturnAddenda{ +func NewReturnAddenda(params ...AddendaParam) *ReturnAddenda { + rAddenda := &ReturnAddenda{ recordType: "7", - TypeCode: "99", + typeCode: "99", } return rAddenda } @@ -75,7 +75,7 @@ func (returnAddenda *ReturnAddenda) Parse(record string) { // 1-1 Always "7" returnAddenda.recordType = "7" // 2-3 Defines the specific explanation and format for the addenda information contained in the same record - returnAddenda.TypeCode = record[1:3] + returnAddenda.typeCode = record[1:3] // 4-6 returnAddenda.ReturnCode = record[3:6] // 7-21 @@ -94,7 +94,7 @@ func (returnAddenda *ReturnAddenda) Parse(record string) { func (returnAddenda *ReturnAddenda) String() string { return fmt.Sprintf("%v%v%v%v%v%v%v%v", returnAddenda.recordType, - returnAddenda.TypeCode, + returnAddenda.TypeCode(), returnAddenda.ReturnCode, returnAddenda.OriginalTraceField(), returnAddenda.DateOfDeathField(), @@ -121,6 +121,11 @@ func (returnAddenda *ReturnAddenda) Validate() error { return nil } +// TypeCode defines the format of the underlying addenda record +func (returnAddenda *ReturnAddenda) TypeCode() string { + return returnAddenda.typeCode +} + // OriginalTraceField returns a zero padded OriginalTrace string func (returnAddenda *ReturnAddenda) OriginalTraceField() string { return returnAddenda.numericField(returnAddenda.OriginalTrace, 15) diff --git a/returnAddenda_test.go b/returnAddenda_test.go index 7f5b66bd4..f8fa77931 100644 --- a/returnAddenda_test.go +++ b/returnAddenda_test.go @@ -9,9 +9,10 @@ import ( "time" ) -func mockReturnAddenda() ReturnAddenda { +func mockReturnAddenda() *ReturnAddenda { rAddenda := NewReturnAddenda() - rAddenda.TypeCode = "R07" + rAddenda.typeCode = "99" + rAddenda.ReturnCode = "R07" rAddenda.AddendaInformation = "Authorization Revoked" return rAddenda @@ -29,8 +30,8 @@ func TestReturnAddendaParse(t *testing.T) { if rAddenda.recordType != "7" { t.Errorf("expected %v got %v", "7", rAddenda.recordType) } - if rAddenda.TypeCode != "99" { - t.Errorf("expected %v got %v", "99", rAddenda.TypeCode) + if rAddenda.typeCode != "99" { + t.Errorf("expected %v got %v", "99", rAddenda.typeCode) } if rAddenda.ReturnCode != "R07" { t.Errorf("expected %v got %v", "R07", rAddenda.ReturnCode) From cf2257e30680e1a56c83df3dc9c834d9c649c942 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 27 Oct 2017 12:35:37 -0600 Subject: [PATCH 0004/1694] Adding AddendaNOC for notification of change addenda. This is just adding the Struct with test coverage. The library does not use the AddendaStruct type code yet. --- addendaNOC.go | 159 ++++++++++++++++++++++++++++++++++++++++++ addendaNOC_test.go | 112 +++++++++++++++++++++++++++++ returnAddenda_test.go | 3 +- 3 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 addendaNOC.go create mode 100644 addendaNOC_test.go diff --git a/addendaNOC.go b/addendaNOC.go new file mode 100644 index 000000000..ac51a5fd7 --- /dev/null +++ b/addendaNOC.go @@ -0,0 +1,159 @@ +package ach + +import ( + "fmt" + "strings" +) + +// AddendaNOC is a Addendumer addenda record format for Notification OF Change(NOC) +// The field contents for Notification of Change Entries must match the field contents of the original Entries +type AddendaNOC struct { + // RecordType defines the type of record in the block. entryAddendaPos 7 + recordType string + // TypeCode Addenda types code '98' + typeCode string + // ChangeCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for a change Entry. + // Must exist in changeCodeDict + ChangeCode string + // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. + // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, + // in the Addenda Record of an NOC, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. + OriginalTrace int + // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. + OriginalDFI int + // CorrectedData + CorrectedData string + // TraceNumber matches the Entry Detail Trace Number of the entry being returned. + TraceNumber int + + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +var ( + changeCodeDict = map[string]*changeCode{} + + // Error messages specific to AddendaNOC + msgAddendaNOCChangeCode = "found is not a valid addenda Change Code" +) + +func init() { + // populate the changeCode map with lookup values + changeCodeDict = makeChangeCodeDict() +} + +// changeCode holds a change Code, Reason/Title, and Description +// table of return codes exists in Part 4.2 of the NACHA corporate rules and guidelines +type changeCode struct { + Code, Reason, Description string +} + +// NewAddendaNOC returns an reference to an instantiated AddendaNOC with default values +func NewAddendaNOC(params ...AddendaParam) *AddendaNOC { + addendaNOC := &AddendaNOC{ + recordType: "7", + typeCode: "99", + } + return addendaNOC +} + +// Parse takes the input record string and parses the AddendaNOC values +func (addendaNOC *AddendaNOC) Parse(record string) { + // 1-1 Always "7" + addendaNOC.recordType = "7" + // 2-3 Always "98" + addendaNOC.typeCode = record[1:3] + // 4-6 + addendaNOC.ChangeCode = record[3:6] + // 7-21 + addendaNOC.OriginalTrace = addendaNOC.parseNumField(record[6:21]) + // 28-35 + addendaNOC.OriginalDFI = addendaNOC.parseNumField(record[27:35]) + // 36-64 + addendaNOC.CorrectedData = strings.TrimSpace(record[35:64]) + // 80-94 + addendaNOC.TraceNumber = addendaNOC.parseNumField(record[79:94]) +} + +// String writes the AddendaNOC struct to a 94 character string +func (addendaNOC *AddendaNOC) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v%v", + addendaNOC.recordType, + addendaNOC.TypeCode(), + addendaNOC.ChangeCode, + addendaNOC.OriginalTraceField(), + " ", //6 char reserved field + addendaNOC.OriginalDFIField(), + addendaNOC.CorrectedDataField(), + " ", // 15 char reserved field + addendaNOC.TraceNumberField(), + ) +} + +// Validate verifies NACHA rules for AddendaNOC +func (addendaNOC *AddendaNOC) Validate() error { + + if addendaNOC.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addendaNOC.recordType, Msg: msg} + } + // @TODO Type Code should be 99. + + _, ok := changeCodeDict[addendaNOC.ChangeCode] + if !ok { + // TODO update for ChangeCodeDict + // Return Addenda requires a valid ReturnCode + return &FieldError{FieldName: "ChangeCode", Value: addendaNOC.ChangeCode, Msg: msgAddendaNOCChangeCode} + } + return nil +} + +// TypeCode defines the format of the underlying addenda record +func (addendaNOC *AddendaNOC) TypeCode() string { + return addendaNOC.typeCode +} + +// OriginalTraceField returns a zero padded OriginalTrace string +func (addendaNOC *AddendaNOC) OriginalTraceField() string { + return addendaNOC.numericField(addendaNOC.OriginalTrace, 15) +} + +// OriginalDFIField returns a zero padded OriginalDFI string +func (addendaNOC *AddendaNOC) OriginalDFIField() string { + return addendaNOC.numericField(addendaNOC.OriginalDFI, 8) +} + +//CorrectedDataField returns a space padded CorrectedData string +func (addendaNOC *AddendaNOC) CorrectedDataField() string { + return addendaNOC.alphaField(addendaNOC.CorrectedData, 29) +} + +// TraceNumberField returns a zero padded traceNumber string +func (addendaNOC *AddendaNOC) TraceNumberField() string { + return addendaNOC.numericField(addendaNOC.TraceNumber, 15) +} + +func makeChangeCodeDict() map[string]*changeCode { + dict := make(map[string]*changeCode) + + codes := []changeCode{ + {"C01", "Incorrect bank account number", "Bank account number incorrect or formatted incorrectly"}, + {"C02", "Incorrect transit/routing number", "Once valid transit/routing number must be changed"}, + {"C03", "Incorrect transit/routing number and bank account number", "Once valid transit/routing number must be changed and causes a change to bank account number structure"}, + {"C04", "Bank account name change", "Customer has changed name or ODFI submitted name incorrectly"}, + {"C05", "Incorrect payment code", "Entry posted to demand account should contain savings payment codes or vice versa"}, + {"C06", "Incorrect bank account number and transit code", "Bank account number must be changed and payment code should indicate posting to another account type (demand/savings)"}, + {"C07", "Incorrect transit/routing number, bank account number and payment code", "Changes required in three fields indicated"}, + {"C09", "Incorrect individual ID number", "Individual's ID number is incorrect"}, + {"C10", "Incorrect company name", "Company name is no longer valid and should be changed."}, + {"C11", "Incorrect company identification", "Company ID is no longer valid and should be changed"}, + {"C12", "Incorrect company name and company ID", "Both the company name and company id are no longer valid and must be changed"}, + } + // populate the map + for _, code := range codes { + dict[code.Code] = &code + } + return dict +} diff --git a/addendaNOC_test.go b/addendaNOC_test.go new file mode 100644 index 000000000..045441011 --- /dev/null +++ b/addendaNOC_test.go @@ -0,0 +1,112 @@ +package ach + +import "testing" + +func mockAddendaNOC() *AddendaNOC { + aNOC := NewAddendaNOC() + aNOC.ChangeCode = "C01" + aNOC.OriginalTrace = 12345 + aNOC.OriginalDFI = 9101298 + aNOC.CorrectedData = "1918171614" + aNOC.TraceNumber = 91012980000088 + + return aNOC +} + +func TestAddendaNOCParse(t *testing.T) { + aNOC := NewAddendaNOC() + line := "798C01099912340000015 091012981918171614 091012980000088" + aNOC.Parse(line) + // walk the AddendaNOC struct + if aNOC.recordType != "7" { + t.Errorf("expected %v got %v", "7", aNOC.recordType) + } + if aNOC.typeCode != "98" { + t.Errorf("expected %v got %v", "98", aNOC.typeCode) + } + if aNOC.ChangeCode != "C01" { + t.Errorf("expected %v got %v", "C01", aNOC.ChangeCode) + } + if aNOC.OriginalTrace != 99912340000015 { + t.Errorf("expected %v got %v", 99912340000015, aNOC.OriginalTrace) + } + if aNOC.OriginalDFI != 9101298 { + t.Errorf("expected %v got %v", 9101298, aNOC.OriginalDFI) + } + if aNOC.CorrectedData != "1918171614" { + t.Errorf("expected %v got %v", "1918171614", aNOC.CorrectedData) + } + if aNOC.TraceNumber != 91012980000088 { + t.Errorf("expected %v got %v", 91012980000088, aNOC.TraceNumber) + } +} + +func TestAddendaNOCString(t *testing.T) { + aNOC := NewAddendaNOC() + line := "798C01099912340000015 091012981918171614 091012980000088" + aNOC.Parse(line) + + if aNOC.String() != line { + t.Errorf("\n expected: %v\n got : %v", line, aNOC.String()) + } +} + +func TestNOCAddendaValidateTrue(t *testing.T) { + aNOC := mockAddendaNOC() + aNOC.ChangeCode = "C11" + if err := aNOC.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ChangeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddendaNOCValidateChangeCodeFalse(t *testing.T) { + aNOC := mockAddendaNOC() + aNOC.ChangeCode = "C63" + if err := aNOC.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ChangeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddendaNOCOriginalTraceField(t *testing.T) { + aNOC := mockAddendaNOC() + exp := "000000000012345" + if aNOC.OriginalTraceField() != exp { + t.Errorf("expected %v received %v", exp, aNOC.OriginalTraceField()) + } +} + +func TestAddendaNOCOriginalDFIField(t *testing.T) { + aNOC := mockAddendaNOC() + exp := "09101298" + if aNOC.OriginalDFIField() != exp { + t.Errorf("expected %v received %v", exp, aNOC.OriginalDFIField()) + } +} + +func TestAddendaNOCCorrectedDataField(t *testing.T) { + aNOC := mockAddendaNOC() + exp := "1918171614 " // 29 char + if aNOC.CorrectedDataField() != exp { + t.Errorf("expected %v received %v", exp, aNOC.CorrectedDataField()) + } +} + +func TestAddendaNOCTraceNumberField(t *testing.T) { + aNOC := mockAddendaNOC() + exp := "091012980000088" + if aNOC.TraceNumberField() != exp { + t.Errorf("expected %v received %v", exp, aNOC.TraceNumberField()) + } +} diff --git a/returnAddenda_test.go b/returnAddenda_test.go index f8fa77931..1b465e8c7 100644 --- a/returnAddenda_test.go +++ b/returnAddenda_test.go @@ -14,6 +14,7 @@ func mockReturnAddenda() *ReturnAddenda { rAddenda.typeCode = "99" rAddenda.ReturnCode = "R07" rAddenda.AddendaInformation = "Authorization Revoked" + rAddenda.OriginalDFI = 9101298 return rAddenda } @@ -129,7 +130,7 @@ func TestReturnAddendaDateOfDeathField(t *testing.T) { func TestReturnAddendaOriginalDFIField(t *testing.T) { rAddenda := mockReturnAddenda() - exp := "00000000" + exp := "09101298" if rAddenda.OriginalDFIField() != exp { t.Errorf("expected %v received %v", exp, rAddenda.OriginalDFIField()) } From 2194d2bd0636e39676293db6e90953be1efc11c4 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 27 Oct 2017 13:27:08 -0600 Subject: [PATCH 0005/1694] Return a returnAddenda via addendaParam into NewAddenda() --- addenda.go | 14 ++++++++++++-- returnAddenda.go | 7 +++++++ returnAddenda_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/addenda.go b/addenda.go index 0be8b31f2..d2b167091 100644 --- a/addenda.go +++ b/addenda.go @@ -33,6 +33,14 @@ type Addenda struct { type AddendaParam struct { TypeCode string `json:"type_code,omitempty"` PaymentRelatedInfo string `json:"payment_related_info,omitempty"` + TraceNumber string `json:"trace_number,omitempty"` + // Following Fields are used for Return addenda + ReturnCode string `json:"return_code,omitempty"` + OriginalTrace string `json:"original_trace,omitempty"` + AddendaInfo string `json:"addenda_info,omitempty"` + OriginalDFI string `json:"original_dfi,omitempty"` + // Following fields are used for NOC(notification of change) addenda w/ return fields + ChangeCode string `json:"return_code,omitempty"` } // NewAddenda returns a new Addenda with default values for none exported fields @@ -44,8 +52,6 @@ func NewAddenda(params ...AddendaParam) (Addendumer, error) { params[0].TypeCode = "05" } switch typeCode := params[0].TypeCode; typeCode { - case "99": - return NewReturnAddenda(), nil case "05": addenda := Addenda{ recordType: "7", @@ -55,6 +61,10 @@ func NewAddenda(params ...AddendaParam) (Addendumer, error) { } addenda.PaymentRelatedInformation = params[0].PaymentRelatedInfo return &addenda, nil + case "98": + return NewAddendaNOC(params[0]), nil + case "99": + return NewReturnAddenda(params[0]), nil default: msg := fmt.Sprintf("Addenda Type Code %v is not supported", typeCode) return nil, &FileError{FieldName: "TypeCode", Msg: msg} diff --git a/returnAddenda.go b/returnAddenda.go index 31ff57b14..75ba91ae1 100644 --- a/returnAddenda.go +++ b/returnAddenda.go @@ -67,6 +67,13 @@ func NewReturnAddenda(params ...AddendaParam) *ReturnAddenda { recordType: "7", typeCode: "99", } + if len(params) > 0 { + rAddenda.ReturnCode = params[0].ReturnCode + rAddenda.OriginalTrace = rAddenda.parseNumField(params[0].OriginalTrace) + rAddenda.OriginalDFI = rAddenda.parseNumField(params[0].OriginalDFI) + rAddenda.AddendaInformation = params[0].AddendaInfo + rAddenda.TraceNumber = rAddenda.parseNumField(params[0].TraceNumber) + } return rAddenda } diff --git a/returnAddenda_test.go b/returnAddenda_test.go index 1b465e8c7..b760109af 100644 --- a/returnAddenda_test.go +++ b/returnAddenda_test.go @@ -5,6 +5,7 @@ package ach import ( + "strings" "testing" "time" ) @@ -13,6 +14,7 @@ func mockReturnAddenda() *ReturnAddenda { rAddenda := NewReturnAddenda() rAddenda.typeCode = "99" rAddenda.ReturnCode = "R07" + rAddenda.OriginalTrace = 99912340000015 rAddenda.AddendaInformation = "Authorization Revoked" rAddenda.OriginalDFI = 9101298 @@ -152,3 +154,41 @@ func TestReturnAddendaTraceNumberField(t *testing.T) { t.Errorf("expected %v received %v", exp, rAddenda.TraceNumberField()) } } + +func TestReturnAddendaNewAddendaParam(t *testing.T) { + aParam := AddendaParam{ + TypeCode: "99", + ReturnCode: "R07", + OriginalTrace: "99912340000015", + OriginalDFI: "09101298", + AddendaInfo: "Authorization Revoked", + TraceNumber: "091012980000066", + } + + a, err := NewAddenda(aParam) + if err != nil { + t.Errorf("returnAddenda from NewAddeda: %v", err) + } + rAddenda, ok := a.(*ReturnAddenda) + if !ok { + t.Errorf("expecting *ReturnAddenda received %T ", a) + } + if rAddenda.TypeCode() != aParam.TypeCode { + t.Errorf("expected %v got %v", aParam.TypeCode, rAddenda.TypeCode()) + } + if rAddenda.ReturnCode != aParam.ReturnCode { + t.Errorf("expected %v got %v", aParam.ReturnCode, rAddenda.ReturnCode) + } + if !strings.Contains(rAddenda.OriginalTraceField(), aParam.OriginalTrace) { + t.Errorf("expected %v got %v", aParam.OriginalTrace, rAddenda.OriginalTrace) + } + if !strings.Contains(rAddenda.OriginalDFIField(), aParam.OriginalDFI) { + t.Errorf("expected %v got %v", aParam.OriginalDFI, rAddenda.OriginalDFI) + } + if rAddenda.AddendaInformation != aParam.AddendaInfo { + t.Errorf("expected %v got %v", aParam.AddendaInfo, rAddenda.AddendaInformation) + } + if !strings.Contains(rAddenda.TraceNumberField(), aParam.TraceNumber) { + t.Errorf("expected %v got %v", aParam.TraceNumber, rAddenda.TraceNumber) + } +} From 9b1e61b06ed9906880d44055c1f2421dc57a4026 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 27 Oct 2017 13:38:05 -0600 Subject: [PATCH 0006/1694] Return a AddendaNOC via addendaParam into NewAddenda() --- addenda.go | 3 ++- addendaNOC.go | 9 ++++++++- addendaNOC_test.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/addenda.go b/addenda.go index d2b167091..5617a4bfa 100644 --- a/addenda.go +++ b/addenda.go @@ -40,7 +40,8 @@ type AddendaParam struct { AddendaInfo string `json:"addenda_info,omitempty"` OriginalDFI string `json:"original_dfi,omitempty"` // Following fields are used for NOC(notification of change) addenda w/ return fields - ChangeCode string `json:"return_code,omitempty"` + ChangeCode string `json:"return_code,omitempty"` + CorrectedData string `json:"corrected_data,omitempty"` } // NewAddenda returns a new Addenda with default values for none exported fields diff --git a/addendaNOC.go b/addendaNOC.go index ac51a5fd7..e410225be 100644 --- a/addendaNOC.go +++ b/addendaNOC.go @@ -54,7 +54,14 @@ type changeCode struct { func NewAddendaNOC(params ...AddendaParam) *AddendaNOC { addendaNOC := &AddendaNOC{ recordType: "7", - typeCode: "99", + typeCode: "98", + } + if len(params) > 0 { + addendaNOC.ChangeCode = params[0].ChangeCode + addendaNOC.OriginalTrace = addendaNOC.parseNumField(params[0].OriginalTrace) + addendaNOC.OriginalDFI = addendaNOC.parseNumField(params[0].OriginalDFI) + addendaNOC.CorrectedData = params[0].CorrectedData + addendaNOC.TraceNumber = addendaNOC.parseNumField(params[0].TraceNumber) } return addendaNOC } diff --git a/addendaNOC_test.go b/addendaNOC_test.go index 045441011..5425e29ea 100644 --- a/addendaNOC_test.go +++ b/addendaNOC_test.go @@ -1,6 +1,9 @@ package ach -import "testing" +import ( + "strings" + "testing" +) func mockAddendaNOC() *AddendaNOC { aNOC := NewAddendaNOC() @@ -110,3 +113,41 @@ func TestAddendaNOCTraceNumberField(t *testing.T) { t.Errorf("expected %v received %v", exp, aNOC.TraceNumberField()) } } + +func TestAddendaNOCNewAddendaParam(t *testing.T) { + aParam := AddendaParam{ + TypeCode: "98", + ChangeCode: "C01", + OriginalTrace: "12345", + OriginalDFI: "9101298", + CorrectedData: "1918171614", + TraceNumber: "91012980000088", + } + + a, err := NewAddenda(aParam) + if err != nil { + t.Errorf("AddendaNOC from NewAddenda: %v", err) + } + aNOC, ok := a.(*AddendaNOC) + if !ok { + t.Errorf("expecting *AddendaNOC received %T ", a) + } + if aNOC.TypeCode() != aParam.TypeCode { + t.Errorf("expected %v got %v", aParam.TypeCode, aNOC.TypeCode()) + } + if aNOC.ChangeCode != aParam.ChangeCode { + t.Errorf("expected %v got %v", aParam.ChangeCode, aNOC.ChangeCode) + } + if !strings.Contains(aNOC.OriginalTraceField(), aParam.OriginalTrace) { + t.Errorf("expected %v got %v", aParam.OriginalTrace, aNOC.OriginalTrace) + } + if !strings.Contains(aNOC.OriginalDFIField(), aParam.OriginalDFI) { + t.Errorf("expected %v got %v", aParam.OriginalDFI, aNOC.OriginalDFI) + } + if aNOC.CorrectedData != aParam.CorrectedData { + t.Errorf("expected %v got %v", aParam.CorrectedData, aNOC.CorrectedData) + } + if !strings.Contains(aNOC.TraceNumberField(), aParam.TraceNumber) { + t.Errorf("expected %v got %v", aParam.TraceNumber, aNOC.TraceNumber) + } +} From fd959f36b15bedd6a01c8c0fb52f5fcc66e1c8bc Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 27 Oct 2017 13:54:49 -0600 Subject: [PATCH 0007/1694] Resolved BatchCOR validator failing for TypeCode(98) with proper addendaNOC --- batch.go | 2 +- batchCOR.go | 2 +- batchCOR_test.go | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/batch.go b/batch.go index e53ed8d2a..e54c593e5 100644 --- a/batch.go +++ b/batch.go @@ -344,7 +344,7 @@ func (batch *batch) isTypeCode(typeCode string) error { for _, entry := range batch.entries { for _, addenda := range entry.Addendum { if addenda.TypeCode() != typeCode { - msg := fmt.Sprintf(msgBatchTypeCode, addenda.TypeCode, typeCode, batch.header.StandardEntryClassCode) + msg := fmt.Sprintf(msgBatchTypeCode, addenda.TypeCode(), typeCode, batch.header.StandardEntryClassCode) return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TypeCode", Msg: msg} } } diff --git a/batchCOR.go b/batchCOR.go index ef4318755..5d38ad358 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -39,7 +39,7 @@ func (batch *BatchCOR) Validate() error { if err := batch.isAddendaCount(1); err != nil { return err } - if err := batch.isTypeCode("05"); err != nil { + if err := batch.isTypeCode("98"); err != nil { return err } diff --git a/batchCOR_test.go b/batchCOR_test.go index 675e4119e..6bc02d8ce 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -28,13 +28,11 @@ func mockCOREntryDetail() *EntryDetail { return entry } -// TODO make a addendaNOC for COR batches - func mockBatchCOR() *BatchCOR { mockBatch := NewBatchCOR() mockBatch.SetHeader(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddendaNOC()) if err := mockBatch.Create(); err != nil { panic(err) } From c46e9365cc2c9f004632b1d2f239ef3b9f9d9651 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 29 Oct 2017 14:07:08 -0600 Subject: [PATCH 0008/1694] AddendaParam ChangeCode JSON struct hint was wrong --- addenda.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addenda.go b/addenda.go index 5617a4bfa..f236238fc 100644 --- a/addenda.go +++ b/addenda.go @@ -40,7 +40,7 @@ type AddendaParam struct { AddendaInfo string `json:"addenda_info,omitempty"` OriginalDFI string `json:"original_dfi,omitempty"` // Following fields are used for NOC(notification of change) addenda w/ return fields - ChangeCode string `json:"return_code,omitempty"` + ChangeCode string `json:"change_code,omitempty"` CorrectedData string `json:"corrected_data,omitempty"` } From f3c2d1895f477734c223a30aa8ebd4c4d2243e30 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 29 Oct 2017 14:07:43 -0600 Subject: [PATCH 0009/1694] Just use type *Addenda in mock rather than a type conversion --- addenda_internal_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/addenda_internal_test.go b/addenda_internal_test.go index deb0a5e9c..ac5f9437d 100644 --- a/addenda_internal_test.go +++ b/addenda_internal_test.go @@ -10,10 +10,13 @@ import ( ) func mockAddenda() *Addenda { - a, _ := NewAddenda() - addenda := a.(*Addenda) - addenda.EntryDetailSequenceNumber = 1234567 - return addenda + addenda := Addenda{ + recordType: "7", + typeCode: "05", + SequenceNumber: 1, + EntryDetailSequenceNumber: 1234567, + } + return &addenda } func TestMockAddenda(t *testing.T) { From f9869a8e0518dc8f0e33b82dafdfb68490fbe218 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 29 Oct 2017 14:09:45 -0600 Subject: [PATCH 0010/1694] BatchHeader.OriginatorStatusCode can be zero. Validation does not need to exist for default value 0 --- batchHeader.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/batchHeader.go b/batchHeader.go index 8d14d54be..6c9f60982 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -99,7 +99,7 @@ type BatchHeader struct { func NewBatchHeader(params ...BatchParam) *BatchHeader { bh := &BatchHeader{ recordType: "5", - OriginatorStatusCode: 1, + OriginatorStatusCode: 0, //Prepared by an Originator BatchNumber: 1, } if len(params) > 0 { @@ -229,9 +229,6 @@ func (bh *BatchHeader) fieldInclusion() error { if bh.CompanyEntryDescription == "" { return &FieldError{FieldName: "CompanyEntryDescription", Value: bh.CompanyEntryDescription, Msg: msgFieldInclusion} } - if bh.OriginatorStatusCode == 0 { - return &FieldError{FieldName: "OriginatorStatusCode", Value: strconv.Itoa(bh.OriginatorStatusCode), Msg: msgFieldInclusion} - } if bh.ODFIIdentification == 0 { return &FieldError{FieldName: "ODFIIdentification", Value: bh.ODFIIdentificationField(), Msg: msgFieldInclusion} } From 722065fdb5725e3d5875ee6334bd847983e32c9b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 09:57:01 -0600 Subject: [PATCH 0011/1694] BatchCOR increase code coverage of existing functionality --- batchCOR.go | 2 +- batchCOR_test.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++- batchHeader.go | 7 ++--- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/batchCOR.go b/batchCOR.go index 5d38ad358..f474a03b0 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -6,7 +6,7 @@ import ( // BatchCOR COR - Automated Notification of Change (NOC) or Refused Notification of Change // This Standard Entry Class Code is used by an RDFI or ODFI when originating a Notification of Change or Refused Notification of Change in automated format. -// It is also used by the ACH operator that converts paper Notifications of Change to automated format. +// A Notification of Change may be created by an RDFI to notify the ODFI that a posted Entry or Prenotification Entry contains invalid or erroneous information and should be changed. type BatchCOR struct { batch } diff --git a/batchCOR_test.go b/batchCOR_test.go index 6bc02d8ce..4f95c75db 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -1,6 +1,8 @@ package ach -import "testing" +import ( + "testing" +) // TODO make all the mock values cor fields @@ -52,3 +54,66 @@ func TestBatchCORSEC(t *testing.T) { } } } + +func TestBatchCORParam(t *testing.T) { + + batch, _ := NewBatch(BatchParam{ + ServiceClassCode: "220", + CompanyName: "Your Company, inc", + StandardEntryClass: "COR", + CompanyIdentification: "123456789", + CompanyEntryDescription: "Vndr Pay", + CompanyDescriptiveDate: "Oct 23", + ODFIIdentification: "123456789"}) + + _, ok := batch.(*BatchCOR) + if !ok { + t.Error("Expecting BachCOR") + } +} + +func TestBatchCORAddendumCount(t *testing.T) { + mockBatch := mockBatchCOR() + // Adding a second addenda to the mock entry + mockBatch.GetEntries()[0].AddAddenda(mockAddendaNOC()) + + if err := mockBatch.Create(); err != nil { + // fmt.Printf("err: %v \n", err) + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchCORAddendaTypeCode(t *testing.T) { + mockBatch := mockBatchCOR() + mockBatch.GetEntries()[0].Addendum[0].(*AddendaNOC).typeCode = "07" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchCORCreate(t *testing.T) { + mockBatch := mockBatchCOR() + // Must have valid batch header to create a batch + mockBatch.GetHeader().ServiceClassCode = 63 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} diff --git a/batchHeader.go b/batchHeader.go index 6c9f60982..39464ebdf 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -75,7 +75,10 @@ type BatchHeader struct { // SettlementDate Leave blank, this field is inserted by the ACH operator settlementDate string - // OriginatorStatusCode '1' + // OriginatorStatusCode refers to the ODFI initiating the Entry. + // 0 ADV File prepared by an ACH Operator. + // 1 This code identifies the Originator as a depository financial institution. + // 2 This code identifies the Originator as a Federal Government entity or agency. OriginatorStatusCode int //ODFIIdentification First 8 digits of the originating DFI transit routing number @@ -204,7 +207,6 @@ func (bh *BatchHeader) Validate() error { if err := bh.isAlphanumeric(bh.CompanyEntryDescription); err != nil { return &FieldError{FieldName: "CompanyEntryDescription", Value: bh.CompanyEntryDescription, Msg: err.Error()} } - return nil } @@ -232,7 +234,6 @@ func (bh *BatchHeader) fieldInclusion() error { if bh.ODFIIdentification == 0 { return &FieldError{FieldName: "ODFIIdentification", Value: bh.ODFIIdentificationField(), Msg: msgFieldInclusion} } - return nil } From a0504d6fcd1d8f2253497b00946e8c0fd9e43835 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 09:57:25 -0600 Subject: [PATCH 0012/1694] Test is not used - dead code --- batchWEB_test.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/batchWEB_test.go b/batchWEB_test.go index 3d130efbf..00a49587c 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -36,23 +36,6 @@ func mockBatchWEB() *BatchWEB { return mockBatch } -// A Batch web can only have one addendum per entry detail -func TestBatchWEBAddendumCount(t *testing.T) { - mockBatch := mockBatchWEB() - // Adding a second addenda to the mock entry - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) - - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "EntryAddendaCount" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - // No more than 1 batch per entry detail record can exist func TestBatchWebAddenda(t *testing.T) { mockBatch := mockBatchWEB() From 43b7116c831f998867dc465c9e2f22c17230b952 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 10:40:14 -0600 Subject: [PATCH 0013/1694] BatchNOC entry's must have an amount field of zero. --- batchCOR.go | 17 ++++++++++++++++- batchCOR_test.go | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/batchCOR.go b/batchCOR.go index f474a03b0..cb471c1b3 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -11,6 +11,8 @@ type BatchCOR struct { batch } +var msgBatchCORAmount = "debit:%v credit:%v entry detail amount fields must be zero for SEC type COR" + // NewBatchCOR returns a *BatchCOR func NewBatchCOR(params ...BatchParam) *BatchCOR { batch := new(BatchCOR) @@ -49,7 +51,20 @@ func (batch *BatchCOR) Validate() error { return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } - // TODO check that addenda is type AddendaNOC + // The Amount field must be zero + // batch.verify calls batch.isBatchAmount which ensures the batch.Control values are accurate. + if batch.control.TotalCreditEntryDollarAmount != 0 || batch.control.TotalDebitEntryDollarAmount != 0 { + msg := fmt.Sprintf(msgBatchCORAmount, batch.control.TotalCreditEntryDollarAmount, batch.control.TotalDebitEntryDollarAmount) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Amount", Msg: msg} + + } + + // TODO the Addenda Record must exist: + // - type NOC, + // - type code 98 + // - validated change code + // - amount zero + // - and Corrected information must not be null return nil } diff --git a/batchCOR_test.go b/batchCOR_test.go index 4f95c75db..b9894d246 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -22,7 +22,7 @@ func mockCOREntryDetail() *EntryDetail { entry.TransactionCode = 27 entry.SetRDFI(9101298) entry.DFIAccountNumber = "744-5678-99" - entry.Amount = 5000000 + entry.Amount = 0 entry.IdentificationNumber = "location #23" entry.SetReceivingCompany("Best Co. #23") entry.TraceNumber = 123456789 @@ -103,6 +103,20 @@ func TestBatchCORAddendaTypeCode(t *testing.T) { } } +func TestBatchCORAmount(t *testing.T) { + mockBatch := mockBatchCOR() + mockBatch.GetEntries()[0].Amount = 9999 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Amount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + func TestBatchCORCreate(t *testing.T) { mockBatch := mockBatchCOR() // Must have valid batch header to create a batch From e5cfac21ccfaea639a40950cc1da9e867ecb7217 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 11:22:57 -0600 Subject: [PATCH 0014/1694] Additional AddendaNOC validation based on Appendix Five of NACH corporate Rules --- addendaNOC.go | 19 ++++++++++++++----- addendaNOC_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/addendaNOC.go b/addendaNOC.go index e410225be..b6a8c9883 100644 --- a/addendaNOC.go +++ b/addendaNOC.go @@ -36,7 +36,9 @@ var ( changeCodeDict = map[string]*changeCode{} // Error messages specific to AddendaNOC - msgAddendaNOCChangeCode = "found is not a valid addenda Change Code" + msgAddendaNOCChangeCode = "found is not a valid addenda Change Code" + msgAddendaNOCTypeCode = "is not AddendaNOC type code of 98" + msgAddendaNOCCorrectedData = "must contain the corrected information corresponding to the Change Code" ) func init() { @@ -101,19 +103,26 @@ func (addendaNOC *AddendaNOC) String() string { // Validate verifies NACHA rules for AddendaNOC func (addendaNOC *AddendaNOC) Validate() error { - if addendaNOC.recordType != "7" { msg := fmt.Sprintf(msgRecordType, 7) return &FieldError{FieldName: "recordType", Value: addendaNOC.recordType, Msg: msg} } - // @TODO Type Code should be 99. + // Type Code must be 98 + if addendaNOC.typeCode != "98" { + return &FieldError{FieldName: "TypeCode", Value: addendaNOC.typeCode, Msg: msgAddendaTypeCode} + } + // AddendaNOC requires a valid ChangeCode _, ok := changeCodeDict[addendaNOC.ChangeCode] if !ok { - // TODO update for ChangeCodeDict - // Return Addenda requires a valid ReturnCode return &FieldError{FieldName: "ChangeCode", Value: addendaNOC.ChangeCode, Msg: msgAddendaNOCChangeCode} } + + // AddendaNOC Record must contain the corrected information corresponding to the Change Code used + if addendaNOC.CorrectedData == "" { + return &FieldError{FieldName: "CorrectedData", Value: addendaNOC.CorrectedData, Msg: msgAddendaNOCCorrectedData} + } + return nil } diff --git a/addendaNOC_test.go b/addendaNOC_test.go index 5425e29ea..48ef5db70 100644 --- a/addendaNOC_test.go +++ b/addendaNOC_test.go @@ -54,7 +54,49 @@ func TestAddendaNOCString(t *testing.T) { } } -func TestNOCAddendaValidateTrue(t *testing.T) { +func TestAddendaNOCValidRecordType(t *testing.T) { + aNOC := mockAddendaNOC() + aNOC.recordType = "63" + if err := aNOC.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddendaNOCValidTypeCode(t *testing.T) { + aNOC := mockAddendaNOC() + aNOC.typeCode = "05" + if err := aNOC.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddendaNOCValidCorrectedData(t *testing.T) { + aNOC := mockAddendaNOC() + aNOC.CorrectedData = "" + if err := aNOC.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CorrectedData" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddendaNOCValidateTrue(t *testing.T) { aNOC := mockAddendaNOC() aNOC.ChangeCode = "C11" if err := aNOC.Validate(); err != nil { @@ -67,7 +109,6 @@ func TestNOCAddendaValidateTrue(t *testing.T) { } } } - func TestAddendaNOCValidateChangeCodeFalse(t *testing.T) { aNOC := mockAddendaNOC() aNOC.ChangeCode = "C63" From 9f978f0577299fab48bdbba27a318d7deaed811c Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 12:25:59 -0600 Subject: [PATCH 0015/1694] Validate Addendum as type AddendaNOC and validate each addenda record --- batchCOR.go | 39 +++++++++++++++++++++++++++------------ batchCOR_test.go | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/batchCOR.go b/batchCOR.go index cb471c1b3..35a6979a6 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -12,6 +12,8 @@ type BatchCOR struct { } var msgBatchCORAmount = "debit:%v credit:%v entry detail amount fields must be zero for SEC type COR" +var msgBatchCORAddenda = "found and 1 AddendaNOC is required for SEC Type COR" +var msgBatchCORAddendaType = "%T found where AddendaNOC is required for SEC type NOC" // NewBatchCOR returns a *BatchCOR func NewBatchCOR(params ...BatchParam) *BatchCOR { @@ -38,10 +40,7 @@ func (batch *BatchCOR) Validate() error { } // Add configuration based validation for this type. // Web can have up to one addenda per entry record - if err := batch.isAddendaCount(1); err != nil { - return err - } - if err := batch.isTypeCode("98"); err != nil { + if err := batch.isAddendaNOC(); err != nil { return err } @@ -56,16 +55,8 @@ func (batch *BatchCOR) Validate() error { if batch.control.TotalCreditEntryDollarAmount != 0 || batch.control.TotalDebitEntryDollarAmount != 0 { msg := fmt.Sprintf(msgBatchCORAmount, batch.control.TotalCreditEntryDollarAmount, batch.control.TotalDebitEntryDollarAmount) return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Amount", Msg: msg} - } - // TODO the Addenda Record must exist: - // - type NOC, - // - type code 98 - // - validated change code - // - amount zero - // - and Corrected information must not be null - return nil } @@ -81,3 +72,27 @@ func (batch *BatchCOR) Create() error { } return nil } + +// isAddendaNOC verifies that a AddendaNoc exists for each EntryDetail and is Validated +func (batch *BatchCOR) isAddendaNOC() error { + for _, entry := range batch.entries { + // Addenda type must be equal to 1 + if len(entry.Addendum) != 1 { + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Addendum", Msg: msgBatchCORAddenda} + } + // Addenda type assertion must be AddendaNOC + aNOC, ok := entry.Addendum[0].(*AddendaNOC) + if !ok { + msg := fmt.Sprintf(msgBatchCORAddendaType, entry.Addendum[0]) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Addendum", Msg: msg} + } + // AddendaNOC must be Validated + if err := aNOC.Validate(); err != nil { + // convert the field error in to a batch error for a consistent api + if e, ok := err.(*FieldError); ok { + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} + } + } + } + return nil +} diff --git a/batchCOR_test.go b/batchCOR_test.go index b9894d246..504325b15 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -72,7 +72,7 @@ func TestBatchCORParam(t *testing.T) { } } -func TestBatchCORAddendumCount(t *testing.T) { +func TestBatchCORAddendumCountTwo(t *testing.T) { mockBatch := mockBatchCOR() // Adding a second addenda to the mock entry mockBatch.GetEntries()[0].AddAddenda(mockAddendaNOC()) @@ -80,7 +80,39 @@ func TestBatchCORAddendumCount(t *testing.T) { if err := mockBatch.Create(); err != nil { // fmt.Printf("err: %v \n", err) if e, ok := err.(*BatchError); ok { - if e.FieldName != "AddendaCount" { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchCORAddendaCountZero(t *testing.T) { + mockBatch := NewBatchCOR() + mockBatch.SetHeader(mockBatchCORHeader()) + mockBatch.AddEntry(mockCOREntryDetail()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// check that Addendum is of type AddendaNOC +func TestBatchCORAddendaType(t *testing.T) { + mockBatch := NewBatchCOR() + mockBatch.SetHeader(mockBatchCORHeader()) + mockBatch.AddEntry(mockCOREntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { t.Errorf("%T: %s", err, err) } } else { From fda74687c6cdb809aedcceb62c7097c60d2bf419 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 14:18:36 -0600 Subject: [PATCH 0016/1694] Change spelling mistake for fieldName --- file_test.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/file_test.go b/file_test.go index ea07c1ba8..fb04e40b1 100644 --- a/file_test.go +++ b/file_test.go @@ -155,7 +155,7 @@ func TestFileBuildNoBatch(t *testing.T) { file := NewFile().SetHeader(mockFileHeader()) if err := file.Create(); err != nil { if e, ok := err.(*FileError); ok { - if e.FieldName != "Batchs" { + if e.FieldName != "Batches" { t.Errorf("%T: %s", err, err) } } else { @@ -163,3 +163,16 @@ func TestFileBuildNoBatch(t *testing.T) { } } } + +// Check if a file contains BatchNOC notification of change +func TestFileNotificationOfChange(t *testing.T) { + file := NewFile().SetHeader(mockFileHeader()) + file.AddBatch(mockBatchPPD()) + bCOR := mockBatchCOR() + file.AddBatch(bCOR) + file.Create() + + if file.NotificationOfChange[0] != bCOR { + t.Error("BatchCOR added to File.AddBatch should exist in NotificationOfChange") + } +} From 04ef1443e1efe499b003ab6dfaf70d3908d5760a Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 14:21:58 -0600 Subject: [PATCH 0017/1694] Expose File.NotificationOfChange to File to early check for BatchCOR / NOC entries --- file.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/file.go b/file.go index b4c1a6eb1..251297c3b 100644 --- a/file.go +++ b/file.go @@ -66,6 +66,9 @@ type File struct { Batches []Batcher Control FileControl + // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches + NotificationOfChange []*BatchCOR + converters } @@ -105,7 +108,7 @@ func (f *File) Create() error { } // Requires at least one Batch in the new file. if len(f.Batches) <= 0 { - return &FileError{FieldName: "Batchs", Value: strconv.Itoa(len(f.Batches)), Msg: "must have []*Batches to be built"} + return &FileError{FieldName: "Batches", Value: strconv.Itoa(len(f.Batches)), Msg: "must have []*Batches to be built"} } // add 2 for FileHeader/control and reset if build was called twice do to error totalRecordsInFile := 2 @@ -149,6 +152,10 @@ func (f *File) Create() error { // AddBatch appends a Batch to the ach.File func (f *File) AddBatch(batch Batcher) []Batcher { + switch batch.(type) { + case *BatchCOR: + f.NotificationOfChange = append(f.NotificationOfChange, batch.(*BatchCOR)) + } f.Batches = append(f.Batches, batch) return f.Batches } From ff6b67bb5711d762108ac060221ea8475bf0501c Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 14:22:31 -0600 Subject: [PATCH 0018/1694] Updated documentation and examples for NotificationOfChange --- README.md | 164 +++++++++++++++++++++-------------- example/ach-ppd-read/main.go | 8 +- 2 files changed, 102 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index cabe4392a..e218a8a1e 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ ACH is at an early stage and under active development. Please star the project i * PPD (Prearranged payment and deposits) * WEB (Internet-initiated Entries ) * CCD (Corporate credit or debit) + * COR (Automated Notification of Change(NOC)) ## Project Roadmap @@ -25,17 +26,48 @@ ACH is at an early stage and under active development. Please star the project i * Review the project issues for more detailed information ## Usage and examples -Examples exist in projects [example](https://github.com/moov-io/ach/tree/master/example) folder. The following is based on [simple file creation](https://github.com/moov-io/ach/tree/master/example/simple-file-creation) +[Read a file]((#read-a-file)) - To create a file +[Create a file]((#create-a-file)) + +Examples exist in projects [example](https://github.com/moov-io/ach/tree/master/example) folder. + +### Read a file + +```go +// open a file for reading or pass any io.Reader NewReader() +f, err := os.Open("name-of-your-ach-file.ach") +if err != nil { + log.Panicf("Can not open local file: %s: \n", err) +} +r := ach.NewReader(f) +achFile, err := r.Read() +if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) +} +// ensure we have a validated file structure +if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) +} +// Check if any Notifications Of Change exist in the file +if len(achFile.NotificationOfChange) > 0 { + for _, batch := range achFile.NotificationOfChange { + aNOC := batch.GetEntries()[0].Addendum[0].(*AddendaNOC) + println(aNOC.CorrectedData) + } +} +``` + +### Create a file +The following is based on [simple file creation](https://github.com/moov-io/ach/tree/master/example/simple-file-creation) ```go - file := ach.NewFile(ach.FileParam{ - ImmediateDestination: "0210000890", - ImmediateOrigin: "123456789", - ImmediateDestinationName: "Your Bank", - ImmediateOriginName: "Your Company", - ReferenceCode: "#00000A1"}) +file := ach.NewFile(ach.FileParam{ + ImmediateDestination: "0210000890", + ImmediateOrigin: "123456789", + ImmediateDestinationName: "Your Bank", + ImmediateOriginName: "Your Company", + ReferenceCode: "#00000A1"}) ``` To create a batch @@ -43,117 +75,117 @@ To create a batch Errors only if payment type is not supported ```go - batch := ach.NewBatch(ach.BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company", - StandardEntryClass: "PPD", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Trans. Description", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) +batch := ach.NewBatch(ach.BatchParam{ + ServiceClassCode: "220", + CompanyName: "Your Company", + StandardEntryClass: "PPD", + CompanyIdentification: "123456789", + CompanyEntryDescription: "Trans. Description", + CompanyDescriptiveDate: "Oct 23", + ODFIIdentification: "123456789"}) ``` To create an entry ```go - entry := ach.NewEntryDetail(ach.EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "17500", - TransactionCode: "27", - IDNumber: "ABC##jvkdjfuiwn", - IndividualName: "Bob Smith", - DiscretionaryData: "B1"}) +entry := ach.NewEntryDetail(ach.EntryParam{ + ReceivingDFI: "102001017", + RDFIAccount: "5343121", + Amount: "17500", + TransactionCode: "27", + IDNumber: "ABC##jvkdjfuiwn", + IndividualName: "Bob Smith", + DiscretionaryData: "B1"}) ``` To add one or more optional addenda records for an entry ```go - addenda := ach.NewAddenda(ach.AddendaParam{ - PaymentRelatedInfo: "bonus pay for amazing work on #OSS"}) - entry.AddAddenda(addenda) +addenda := ach.NewAddenda(ach.AddendaParam{ + PaymentRelatedInfo: "bonus pay for amazing work on #OSS"}) +entry.AddAddenda(addenda) ``` Entries are added to batches like so: ```go - batch.AddEntry(entry) +batch.AddEntry(entry) ``` When all of the Entries are added to the batch we can create the batch. ```go - if err := batch.Create(); err != nil { - fmt.Printf("%T: %s", err, err) - } +if err := batch.Create(); err != nil { + fmt.Printf("%T: %s", err, err) +} ``` And batches are added to files much the same way: ```go - file.AddBatch(batch) +file.AddBatch(batch) ``` Now add a new batch for accepting payments on the web ```go - batch2, _ := ach.NewBatch(ach.BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company", - StandardEntryClass: "WEB", - CompanyIdentification: "123456789", - CompanyEntryDescription: "subscr", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) +batch2, _ := ach.NewBatch(ach.BatchParam{ + ServiceClassCode: "220", + CompanyName: "Your Company", + StandardEntryClass: "WEB", + CompanyIdentification: "123456789", + CompanyEntryDescription: "subscr", + CompanyDescriptiveDate: "Oct 23", + ODFIIdentification: "123456789"}) ``` Add an entry and define if it is a single or reoccurring payment. The following is a reoccurring payment for $7.99 ```go - entry2 := ach.NewEntryDetail(ach.EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "799", - TransactionCode: "22", - IDNumber: "#123456", - IndividualName: "Wade Arnold", - PaymentType: "R"}) - - addenda2 := ach.NewAddenda(ach.AddendaParam{ - PaymentRelatedInfo: "Monthly Membership Subscription"}) +entry2 := ach.NewEntryDetail(ach.EntryParam{ + ReceivingDFI: "102001017", + RDFIAccount: "5343121", + Amount: "799", + TransactionCode: "22", + IDNumber: "#123456", + IndividualName: "Wade Arnold", + PaymentType: "R"}) + +addenda2 := ach.NewAddenda(ach.AddendaParam{ + PaymentRelatedInfo: "Monthly Membership Subscription"}) ``` Add the entry to the batch ```go - entry2.AddAddenda(addenda2) +entry2.AddAddenda(addenda2) ``` Create and add the second batch ```go - batch2.AddEntry(entry2) - if err := batch2.Create(); err != nil { - fmt.Printf("%T: %s", err, err) - } - file.AddBatch(batch2) +batch2.AddEntry(entry2) +if err := batch2.Create(); err != nil { + fmt.Printf("%T: %s", err, err) +} +file.AddBatch(batch2) ``` Once we added all our batches we must build the file ```go - if err := file.Create(); err != nil { - fmt.Printf("%T: %s", err, err) - } +if err := file.Create(); err != nil { + fmt.Printf("%T: %s", err, err) +} ``` -Finally we wnt to write the file to an io.Writer +Finally we want to write the file to an io.Writer ```go - w := ach.NewWriter(os.Stdout) - if err := w.Write(file); err != nil { - fmt.Printf("%T: %s", err, err) - } - w.Flush() +w := ach.NewWriter(os.Stdout) +if err := w.Write(file); err != nil { + fmt.Printf("%T: %s", err, err) +} +w.Flush() } ``` diff --git a/example/ach-ppd-read/main.go b/example/ach-ppd-read/main.go index 72a70df9c..3afec70bf 100644 --- a/example/ach-ppd-read/main.go +++ b/example/ach-ppd-read/main.go @@ -15,18 +15,18 @@ func main() { log.Panicf("Can not open file: %s: \n", err) } r := ach.NewReader(f) - _, err = r.Read() + achFile, err := r.Read() if err != nil { fmt.Printf("Issue reading file: %+v \n", err) } // ensure we have a validated file structure - if r.File.Validate(); err != nil { + if achFile.Validate(); err != nil { fmt.Printf("Could not validate entire read file: %v", err) } // If you trust the file but it's formating is off building will probably resolve the malformed file. - if r.File.Create(); err != nil { + if achFile.Create(); err != nil { fmt.Printf("Could not build file with read properties: %v", err) } - fmt.Printf("total amount debit: %v \n", r.File.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("total amount debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) } From 89216a8c42532c492b580388a6418af91f2c5eb5 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 14:29:30 -0600 Subject: [PATCH 0019/1694] Changes to the README --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index e218a8a1e..0c853cd18 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,7 @@ ACH is at an early stage and under active development. Please star the project i * Review the project issues for more detailed information ## Usage and examples -[Read a file]((#read-a-file)) - -[Create a file]((#create-a-file)) - -Examples exist in projects [example](https://github.com/moov-io/ach/tree/master/example) folder. +The following is a high level of reading and writing an ach file. Examples exist in projects [example](https://github.com/moov-io/ach/tree/master/example) folder with more details. ### Read a file From 1e397671df4afeacbf762c9e93ab48f03fb19dcd Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 30 Oct 2017 16:16:28 -0600 Subject: [PATCH 0020/1694] Update examples to handle two parameters (Addendum, error) for creating a NewAddeda --- example/ach-ppd-write/main.go | 22 +++++++++++----------- example/simple-file-creation/main.go | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/example/ach-ppd-write/main.go b/example/ach-ppd-write/main.go index 2fd6c3f66..4596226c0 100644 --- a/example/ach-ppd-write/main.go +++ b/example/ach-ppd-write/main.go @@ -11,12 +11,12 @@ import ( func main() { // Example transfer to write an ACH PPD file to send/credit a external institutions account - // Important: All financial instituions are different and will require registration and exact field values. + // Important: All financial institutions are different and will require registration and exact field values. // Set originator bank ODFI and destination Operator for the financial institution - // this is the funding/recieving source of the transfer + // this is the funding/receiving source of the transfer fh := ach.NewFileHeader() - fh.ImmediateDestination = 9876543210 // A blank space followed by your ODFI's transit/routing numbe + fh.ImmediateDestination = 9876543210 // A blank space followed by your ODFI's transit/routing number fh.ImmediateOrigin = 1234567890 // Organization or Company FED ID usually 1 and FEIN/SSN. Assigned by your ODFI fh.FileCreationDate = time.Now() // Todays Date fh.ImmediateDestinationName = "Federal Reserve Bank" @@ -25,22 +25,22 @@ func main() { // BatchHeader identifies the originating entity and the type of transactions contained in the batch bh := ach.NewBatchHeader() bh.ServiceClassCode = 220 // ACH credit pushes money out, 225 debits/pulls money in. - bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with reciever + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver bh.CompanyIdentification = strconv.Itoa(fh.ImmediateOrigin) bh.StandardEntryClassCode = "PPD" // Consumer destination vs Company CCD - bh.CompanyEntryDescription = "REG.SALARY" // will be on recieving accounts statement + bh.CompanyEntryDescription = "REG.SALARY" // will be on receiving accounts statement bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) bh.ODFIIdentification = 12345678 // first 8 digits of your bank account - // Identifies the recievers account information - // can be multiple entrys per batch + // Identifies the receivers account information + // can be multiple entry's per batch entry := ach.NewEntryDetail() // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan entry.TransactionCode = 22 // Code 22: Credit (deposit) to checking account - entry.SetRDFI(9101298) // Recievers bank transit rounting number - entry.DFIAccountNumber = "12345678" // Recievers bank account number - entry.Amount = 100000000 // Amount of transaction with no decimil. One dollar and eleven cents = 111 - entry.IndividualName = "Reciever Account Name" // Identifies the reciever of the transaction + entry.SetRDFI(9101298) // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.IndividualName = "Receiver Account Name" // Identifies the receiver of the transaction // build the batch batch := ach.NewBatchPPD() diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index 7ec754f24..a8a234727 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -38,7 +38,7 @@ func main() { DiscretionaryData: "B1"}) // To add one or more optional addenda records for an entry - addenda := ach.NewAddenda(ach.AddendaParam{ + addenda, _ := ach.NewAddenda(ach.AddendaParam{ PaymentRelatedInfo: "bonus pay for amazing work on #OSS"}) entry.AddAddenda(addenda) @@ -79,7 +79,7 @@ func main() { IndividualName: "Wade Arnold", PaymentType: "R"}) - addenda2 := ach.NewAddenda(ach.AddendaParam{ + addenda2, _ := ach.NewAddenda(ach.AddendaParam{ PaymentRelatedInfo: "Monthly Membership Subscription"}) // add the entry to the batch From 4ed34080133c92c0ac0964e232145eb114fdc09e Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 31 Oct 2017 11:07:31 -0600 Subject: [PATCH 0021/1694] Rename ReturnAddenda -> AddendaReturn to be consistent with the naming conventions of the the library --- returnAddenda.go => addendaReturn.go | 0 returnAddenda_test.go => addendaReturn_test.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename returnAddenda.go => addendaReturn.go (100%) rename returnAddenda_test.go => addendaReturn_test.go (100%) diff --git a/returnAddenda.go b/addendaReturn.go similarity index 100% rename from returnAddenda.go rename to addendaReturn.go diff --git a/returnAddenda_test.go b/addendaReturn_test.go similarity index 100% rename from returnAddenda_test.go rename to addendaReturn_test.go From 1973d9e2012777cbfb0ca1371b435fecccc4dfd0 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 31 Oct 2017 12:23:39 -0600 Subject: [PATCH 0022/1694] ReturnAddenda -> AddendaReturn Files should have been a part of #144 --- addenda.go | 2 +- addendaReturn.go | 92 +++++++++++++++++++++---------------------- addendaReturn_test.go | 54 ++++++++++++------------- batch.go | 4 +- entryDetail.go | 10 ++--- 5 files changed, 81 insertions(+), 81 deletions(-) diff --git a/addenda.go b/addenda.go index f236238fc..56afa50c2 100644 --- a/addenda.go +++ b/addenda.go @@ -65,7 +65,7 @@ func NewAddenda(params ...AddendaParam) (Addendumer, error) { case "98": return NewAddendaNOC(params[0]), nil case "99": - return NewReturnAddenda(params[0]), nil + return NewAddendaReturn(params[0]), nil default: msg := fmt.Sprintf("Addenda Type Code %v is not supported", typeCode) return nil, &FileError{FieldName: "TypeCode", Msg: msg} diff --git a/addendaReturn.go b/addendaReturn.go index 75ba91ae1..4cf96a397 100644 --- a/addendaReturn.go +++ b/addendaReturn.go @@ -18,7 +18,7 @@ var ( returnCodeDict = map[string]*returnCode{} // Error messages specific to Return Addenda - msgReturnAddendaReturnCode = "found is not a valid return code" + msgAddendaReturnReturnCode = "found is not a valid return code" ) func init() { @@ -26,8 +26,8 @@ func init() { returnCodeDict = makeReturnCodeDict() } -// ReturnAddenda utilized for Notification of Change Entry (COR) and Return types. -type ReturnAddenda struct { +// AddendaReturn utilized for Notification of Change Entry (COR) and Return types. +type AddendaReturn struct { // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string // TypeCode Addenda types code '99' @@ -61,9 +61,9 @@ type returnCode struct { Code, Reason, Description string } -// NewReturnAddenda returns a new ReturnAddenda with default values for none exported fields -func NewReturnAddenda(params ...AddendaParam) *ReturnAddenda { - rAddenda := &ReturnAddenda{ +// NewAddendaReturn returns a new AddendaReturn with default values for none exported fields +func NewAddendaReturn(params ...AddendaParam) *AddendaReturn { + rAddenda := &AddendaReturn{ recordType: "7", typeCode: "99", } @@ -77,90 +77,90 @@ func NewReturnAddenda(params ...AddendaParam) *ReturnAddenda { return rAddenda } -// Parse takes the input record string and parses the ReturnAddenda values -func (returnAddenda *ReturnAddenda) Parse(record string) { +// Parse takes the input record string and parses the AddendaReturn values +func (addendaReturn *AddendaReturn) Parse(record string) { // 1-1 Always "7" - returnAddenda.recordType = "7" + addendaReturn.recordType = "7" // 2-3 Defines the specific explanation and format for the addenda information contained in the same record - returnAddenda.typeCode = record[1:3] + addendaReturn.typeCode = record[1:3] // 4-6 - returnAddenda.ReturnCode = record[3:6] + addendaReturn.ReturnCode = record[3:6] // 7-21 - returnAddenda.OriginalTrace = returnAddenda.parseNumField(record[6:21]) + addendaReturn.OriginalTrace = addendaReturn.parseNumField(record[6:21]) // 22-27, might be a date or blank - returnAddenda.DateOfDeath = returnAddenda.parseSimpleDate(record[21:27]) + addendaReturn.DateOfDeath = addendaReturn.parseSimpleDate(record[21:27]) // 28-35 - returnAddenda.OriginalDFI = returnAddenda.parseNumField(record[27:35]) + addendaReturn.OriginalDFI = addendaReturn.parseNumField(record[27:35]) // 36-79 - returnAddenda.AddendaInformation = strings.TrimSpace(record[35:79]) + addendaReturn.AddendaInformation = strings.TrimSpace(record[35:79]) // 80-94 - returnAddenda.TraceNumber = returnAddenda.parseNumField(record[79:94]) + addendaReturn.TraceNumber = addendaReturn.parseNumField(record[79:94]) } -// String writes the ReturnAddenda struct to a 94 character string -func (returnAddenda *ReturnAddenda) String() string { +// String writes the AddendaReturn struct to a 94 character string +func (addendaReturn *AddendaReturn) String() string { return fmt.Sprintf("%v%v%v%v%v%v%v%v", - returnAddenda.recordType, - returnAddenda.TypeCode(), - returnAddenda.ReturnCode, - returnAddenda.OriginalTraceField(), - returnAddenda.DateOfDeathField(), - returnAddenda.OriginalDFIField(), - returnAddenda.AddendaInformationField(), - returnAddenda.TraceNumberField(), + addendaReturn.recordType, + addendaReturn.TypeCode(), + addendaReturn.ReturnCode, + addendaReturn.OriginalTraceField(), + addendaReturn.DateOfDeathField(), + addendaReturn.OriginalDFIField(), + addendaReturn.AddendaInformationField(), + addendaReturn.TraceNumberField(), ) } -// Validate verifies NACHA rules for ReturnAddenda -func (returnAddenda *ReturnAddenda) Validate() error { +// Validate verifies NACHA rules for AddendaReturn +func (addendaReturn *AddendaReturn) Validate() error { - if returnAddenda.recordType != "7" { + if addendaReturn.recordType != "7" { msg := fmt.Sprintf(msgRecordType, 7) - return &FieldError{FieldName: "recordType", Value: returnAddenda.recordType, Msg: msg} + return &FieldError{FieldName: "recordType", Value: addendaReturn.recordType, Msg: msg} } // @TODO Type Code should be 99. - _, ok := returnCodeDict[returnAddenda.ReturnCode] + _, ok := returnCodeDict[addendaReturn.ReturnCode] if !ok { // Return Addenda requires a valid ReturnCode - return &FieldError{FieldName: "ReturnCode", Value: returnAddenda.ReturnCode, Msg: msgReturnAddendaReturnCode} + return &FieldError{FieldName: "ReturnCode", Value: addendaReturn.ReturnCode, Msg: msgAddendaReturnReturnCode} } return nil } // TypeCode defines the format of the underlying addenda record -func (returnAddenda *ReturnAddenda) TypeCode() string { - return returnAddenda.typeCode +func (addendaReturn *AddendaReturn) TypeCode() string { + return addendaReturn.typeCode } // OriginalTraceField returns a zero padded OriginalTrace string -func (returnAddenda *ReturnAddenda) OriginalTraceField() string { - return returnAddenda.numericField(returnAddenda.OriginalTrace, 15) +func (addendaReturn *AddendaReturn) OriginalTraceField() string { + return addendaReturn.numericField(addendaReturn.OriginalTrace, 15) } // DateOfDeathField returns a space padded DateOfDeath string -func (returnAddenda *ReturnAddenda) DateOfDeathField() string { +func (addendaReturn *AddendaReturn) DateOfDeathField() string { // Return space padded 6 characters if it is a zero value of DateOfDeath - if returnAddenda.DateOfDeath.IsZero() { - return returnAddenda.alphaField("", 6) + if addendaReturn.DateOfDeath.IsZero() { + return addendaReturn.alphaField("", 6) } // YYMMDD - return returnAddenda.formatSimpleDate(returnAddenda.DateOfDeath) + return addendaReturn.formatSimpleDate(addendaReturn.DateOfDeath) } // OriginalDFIField returns a zero padded OriginalDFI string -func (returnAddenda *ReturnAddenda) OriginalDFIField() string { - return returnAddenda.numericField(returnAddenda.OriginalDFI, 8) +func (addendaReturn *AddendaReturn) OriginalDFIField() string { + return addendaReturn.numericField(addendaReturn.OriginalDFI, 8) } //AddendaInformationField returns a space padded AddendaInformation string -func (returnAddenda *ReturnAddenda) AddendaInformationField() string { - return returnAddenda.alphaField(returnAddenda.AddendaInformation, 44) +func (addendaReturn *AddendaReturn) AddendaInformationField() string { + return addendaReturn.alphaField(addendaReturn.AddendaInformation, 44) } // TraceNumberField returns a zero padded traceNumber string -func (returnAddenda *ReturnAddenda) TraceNumberField() string { - return returnAddenda.numericField(returnAddenda.TraceNumber, 15) +func (addendaReturn *AddendaReturn) TraceNumberField() string { + return addendaReturn.numericField(addendaReturn.TraceNumber, 15) } func makeReturnCodeDict() map[string]*returnCode { diff --git a/addendaReturn_test.go b/addendaReturn_test.go index b760109af..9c5d420c0 100644 --- a/addendaReturn_test.go +++ b/addendaReturn_test.go @@ -10,8 +10,8 @@ import ( "time" ) -func mockReturnAddenda() *ReturnAddenda { - rAddenda := NewReturnAddenda() +func mockAddendaReturn() *AddendaReturn { + rAddenda := NewAddendaReturn() rAddenda.typeCode = "99" rAddenda.ReturnCode = "R07" rAddenda.OriginalTrace = 99912340000015 @@ -21,15 +21,15 @@ func mockReturnAddenda() *ReturnAddenda { return rAddenda } -func TestMockReturnAddenda(t *testing.T) { +func TestMockAddendaReturn(t *testing.T) { // TODO: build a mock addenda } -func TestReturnAddendaParse(t *testing.T) { - rAddenda := NewReturnAddenda() +func TestAddendaReturnParse(t *testing.T) { + rAddenda := NewAddendaReturn() line := "799R07099912340000015 09101298Authorization revoked 091012980000066" rAddenda.Parse(line) - // walk the returnAddenda struct + // walk the addendaReturn struct if rAddenda.recordType != "7" { t.Errorf("expected %v got %v", "7", rAddenda.recordType) } @@ -56,8 +56,8 @@ func TestReturnAddendaParse(t *testing.T) { } } -func TestReturnAddendaString(t *testing.T) { - rAddenda := NewReturnAddenda() +func TestAddendaReturnString(t *testing.T) { + rAddenda := NewAddendaReturn() line := "799R07099912340000015 09101298Authorization revoked 091012980000066" rAddenda.Parse(line) @@ -67,7 +67,7 @@ func TestReturnAddendaString(t *testing.T) { } // This is not an exported function but utilized for validation -func TestReturnAddendaMakeReturnCodeDict(t *testing.T) { +func TestAddendaReturnMakeReturnCodeDict(t *testing.T) { codes := makeReturnCodeDict() // check if known code is present _, prs := codes["R01"] @@ -81,8 +81,8 @@ func TestReturnAddendaMakeReturnCodeDict(t *testing.T) { } } -func TestReturnAddendaValidateTrue(t *testing.T) { - rAddenda := mockReturnAddenda() +func TestAddendaReturnValidateTrue(t *testing.T) { + rAddenda := mockAddendaReturn() rAddenda.ReturnCode = "R13" if err := rAddenda.Validate(); err != nil { if e, ok := err.(*FieldError); ok { @@ -95,8 +95,8 @@ func TestReturnAddendaValidateTrue(t *testing.T) { } } -func TestReturnAddendaValidateReturnCodeFalse(t *testing.T) { - rAddenda := mockReturnAddenda() +func TestAddendaReturnValidateReturnCodeFalse(t *testing.T) { + rAddenda := mockAddendaReturn() rAddenda.ReturnCode = "" if err := rAddenda.Validate(); err != nil { if e, ok := err.(*FieldError); ok { @@ -109,16 +109,16 @@ func TestReturnAddendaValidateReturnCodeFalse(t *testing.T) { } } -func TestReturnAddendaOriginalTraceField(t *testing.T) { - rAddenda := mockReturnAddenda() +func TestAddendaReturnOriginalTraceField(t *testing.T) { + rAddenda := mockAddendaReturn() rAddenda.OriginalTrace = 12345 if rAddenda.OriginalTraceField() != "000000000012345" { t.Errorf("expected %v received %v", "000000000012345", rAddenda.OriginalTraceField()) } } -func TestReturnAddendaDateOfDeathField(t *testing.T) { - rAddenda := mockReturnAddenda() +func TestAddendaReturnDateOfDeathField(t *testing.T) { + rAddenda := mockAddendaReturn() // Check for all zeros if rAddenda.DateOfDeathField() != " " { t.Errorf("expected %v received %v", " ", rAddenda.DateOfDeathField()) @@ -130,24 +130,24 @@ func TestReturnAddendaDateOfDeathField(t *testing.T) { } } -func TestReturnAddendaOriginalDFIField(t *testing.T) { - rAddenda := mockReturnAddenda() +func TestAddendaReturnOriginalDFIField(t *testing.T) { + rAddenda := mockAddendaReturn() exp := "09101298" if rAddenda.OriginalDFIField() != exp { t.Errorf("expected %v received %v", exp, rAddenda.OriginalDFIField()) } } -func TestReturnAddendaAddendaInformationField(t *testing.T) { - rAddenda := mockReturnAddenda() +func TestAddendaReturnAddendaInformationField(t *testing.T) { + rAddenda := mockAddendaReturn() exp := "Authorization Revoked " if rAddenda.AddendaInformationField() != exp { t.Errorf("expected %v received %v", exp, rAddenda.AddendaInformationField()) } } -func TestReturnAddendaTraceNumberField(t *testing.T) { - rAddenda := mockReturnAddenda() +func TestAddendaReturnTraceNumberField(t *testing.T) { + rAddenda := mockAddendaReturn() rAddenda.TraceNumber = 91012980000066 exp := "091012980000066" if rAddenda.TraceNumberField() != exp { @@ -155,7 +155,7 @@ func TestReturnAddendaTraceNumberField(t *testing.T) { } } -func TestReturnAddendaNewAddendaParam(t *testing.T) { +func TestAddendaReturnNewAddendaParam(t *testing.T) { aParam := AddendaParam{ TypeCode: "99", ReturnCode: "R07", @@ -167,11 +167,11 @@ func TestReturnAddendaNewAddendaParam(t *testing.T) { a, err := NewAddenda(aParam) if err != nil { - t.Errorf("returnAddenda from NewAddeda: %v", err) + t.Errorf("addendaReturn from NewAddeda: %v", err) } - rAddenda, ok := a.(*ReturnAddenda) + rAddenda, ok := a.(*AddendaReturn) if !ok { - t.Errorf("expecting *ReturnAddenda received %T ", a) + t.Errorf("expecting *AddendaReturn received %T ", a) } if rAddenda.TypeCode() != aParam.TypeCode { t.Errorf("expected %v got %v", aParam.TypeCode, rAddenda.TypeCode()) diff --git a/batch.go b/batch.go index e54c593e5..e2032059e 100644 --- a/batch.go +++ b/batch.go @@ -324,7 +324,7 @@ func (batch *batch) isAddendaSequence() error { // "PPD", "WEB", "CCD", "CIE", "DNE", "MTE", "POS", "SHR" func (batch *batch) isAddendaCount(count int) error { for _, entry := range batch.entries { - if !entry.HasReturnAddenda() { + if !entry.HasAddendaReturn() { if len(entry.Addendum) > count { msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.header.StandardEntryClassCode) return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaCount", Msg: msg} @@ -332,7 +332,7 @@ func (batch *batch) isAddendaCount(count int) error { } else { if len(entry.ReturnAddendum) > count { msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.ReturnAddendum), count, batch.header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "ReturnAddendaCount", Msg: msg} + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaReturnCount", Msg: msg} } } } diff --git a/entryDetail.go b/entryDetail.go index f6cd900ae..e6b6f0da5 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -77,7 +77,7 @@ type EntryDetail struct { // Addendum a list of Addenda for the Entry Detail Addendum []Addendumer // ReturnAddendum stores return types. These are processed separately - ReturnAddendum []ReturnAddenda + ReturnAddendum []AddendaReturn // validator is composed for data validation validator // converters is composed for ACH to golang Converters @@ -243,8 +243,8 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { return ed.Addendum } -// AddReturnAddenda appends an ReturnAddendum to the entry -func (ed *EntryDetail) AddReturnAddenda(returnAddendum ReturnAddenda) []ReturnAddenda { +// AddAddendaReturn appends an ReturnAddendum to the entry +func (ed *EntryDetail) AddAddendaReturn(returnAddendum AddendaReturn) []AddendaReturn { ed.AddendaRecordIndicator = 1 // checks to make sure that we only have either or, not both if ed.Addendum != nil { @@ -330,7 +330,7 @@ func (ed *EntryDetail) TraceNumberField() string { return ed.numericField(ed.TraceNumber, 15) } -// HasReturnAddenda returns true if entry has return addenda -func (ed *EntryDetail) HasReturnAddenda() bool { +// HasAddendaReturn returns true if entry has return addenda +func (ed *EntryDetail) HasAddendaReturn() bool { return ed.ReturnAddendum != nil } From a831d93691e213145a8779113220a79b207a5861 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 31 Oct 2017 12:29:49 -0600 Subject: [PATCH 0023/1694] Rename rAddenda -> addendaReturn --- addendaReturn.go | 14 ++--- addendaReturn_test.go | 138 +++++++++++++++++++++--------------------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/addendaReturn.go b/addendaReturn.go index 4cf96a397..bc09bbd87 100644 --- a/addendaReturn.go +++ b/addendaReturn.go @@ -63,18 +63,18 @@ type returnCode struct { // NewAddendaReturn returns a new AddendaReturn with default values for none exported fields func NewAddendaReturn(params ...AddendaParam) *AddendaReturn { - rAddenda := &AddendaReturn{ + addendaReturn := &AddendaReturn{ recordType: "7", typeCode: "99", } if len(params) > 0 { - rAddenda.ReturnCode = params[0].ReturnCode - rAddenda.OriginalTrace = rAddenda.parseNumField(params[0].OriginalTrace) - rAddenda.OriginalDFI = rAddenda.parseNumField(params[0].OriginalDFI) - rAddenda.AddendaInformation = params[0].AddendaInfo - rAddenda.TraceNumber = rAddenda.parseNumField(params[0].TraceNumber) + addendaReturn.ReturnCode = params[0].ReturnCode + addendaReturn.OriginalTrace = addendaReturn.parseNumField(params[0].OriginalTrace) + addendaReturn.OriginalDFI = addendaReturn.parseNumField(params[0].OriginalDFI) + addendaReturn.AddendaInformation = params[0].AddendaInfo + addendaReturn.TraceNumber = addendaReturn.parseNumField(params[0].TraceNumber) } - return rAddenda + return addendaReturn } // Parse takes the input record string and parses the AddendaReturn values diff --git a/addendaReturn_test.go b/addendaReturn_test.go index 9c5d420c0..a9d142344 100644 --- a/addendaReturn_test.go +++ b/addendaReturn_test.go @@ -11,14 +11,14 @@ import ( ) func mockAddendaReturn() *AddendaReturn { - rAddenda := NewAddendaReturn() - rAddenda.typeCode = "99" - rAddenda.ReturnCode = "R07" - rAddenda.OriginalTrace = 99912340000015 - rAddenda.AddendaInformation = "Authorization Revoked" - rAddenda.OriginalDFI = 9101298 - - return rAddenda + addendaReturn := NewAddendaReturn() + addendaReturn.typeCode = "99" + addendaReturn.ReturnCode = "R07" + addendaReturn.OriginalTrace = 99912340000015 + addendaReturn.AddendaInformation = "Authorization Revoked" + addendaReturn.OriginalDFI = 9101298 + + return addendaReturn } func TestMockAddendaReturn(t *testing.T) { @@ -26,43 +26,43 @@ func TestMockAddendaReturn(t *testing.T) { } func TestAddendaReturnParse(t *testing.T) { - rAddenda := NewAddendaReturn() + addendaReturn := NewAddendaReturn() line := "799R07099912340000015 09101298Authorization revoked 091012980000066" - rAddenda.Parse(line) + addendaReturn.Parse(line) // walk the addendaReturn struct - if rAddenda.recordType != "7" { - t.Errorf("expected %v got %v", "7", rAddenda.recordType) + if addendaReturn.recordType != "7" { + t.Errorf("expected %v got %v", "7", addendaReturn.recordType) } - if rAddenda.typeCode != "99" { - t.Errorf("expected %v got %v", "99", rAddenda.typeCode) + if addendaReturn.typeCode != "99" { + t.Errorf("expected %v got %v", "99", addendaReturn.typeCode) } - if rAddenda.ReturnCode != "R07" { - t.Errorf("expected %v got %v", "R07", rAddenda.ReturnCode) + if addendaReturn.ReturnCode != "R07" { + t.Errorf("expected %v got %v", "R07", addendaReturn.ReturnCode) } - if rAddenda.OriginalTrace != 99912340000015 { - t.Errorf("expected: %v got: %v", 99912340000015, rAddenda.OriginalTrace) + if addendaReturn.OriginalTrace != 99912340000015 { + t.Errorf("expected: %v got: %v", 99912340000015, addendaReturn.OriginalTrace) } - if rAddenda.DateOfDeath.IsZero() != true { - t.Errorf("expected: %v got: %v", time.Time{}, rAddenda.DateOfDeath) + if addendaReturn.DateOfDeath.IsZero() != true { + t.Errorf("expected: %v got: %v", time.Time{}, addendaReturn.DateOfDeath) } - if rAddenda.OriginalDFI != 9101298 { - t.Errorf("expected: %v got: %v", 9101298, rAddenda.OriginalDFI) + if addendaReturn.OriginalDFI != 9101298 { + t.Errorf("expected: %v got: %v", 9101298, addendaReturn.OriginalDFI) } - if rAddenda.AddendaInformation != "Authorization revoked" { - t.Errorf("expected: %v got: %v", "Authorization revoked", rAddenda.AddendaInformation) + if addendaReturn.AddendaInformation != "Authorization revoked" { + t.Errorf("expected: %v got: %v", "Authorization revoked", addendaReturn.AddendaInformation) } - if rAddenda.TraceNumber != 91012980000066 { - t.Errorf("expected: %v got: %v", 91012980000066, rAddenda.TraceNumber) + if addendaReturn.TraceNumber != 91012980000066 { + t.Errorf("expected: %v got: %v", 91012980000066, addendaReturn.TraceNumber) } } func TestAddendaReturnString(t *testing.T) { - rAddenda := NewAddendaReturn() + addendaReturn := NewAddendaReturn() line := "799R07099912340000015 09101298Authorization revoked 091012980000066" - rAddenda.Parse(line) + addendaReturn.Parse(line) - if rAddenda.String() != line { - t.Errorf("\n expected: %v\n got : %v", line, rAddenda.String()) + if addendaReturn.String() != line { + t.Errorf("\n expected: %v\n got : %v", line, addendaReturn.String()) } } @@ -82,9 +82,9 @@ func TestAddendaReturnMakeReturnCodeDict(t *testing.T) { } func TestAddendaReturnValidateTrue(t *testing.T) { - rAddenda := mockAddendaReturn() - rAddenda.ReturnCode = "R13" - if err := rAddenda.Validate(); err != nil { + addendaReturn := mockAddendaReturn() + addendaReturn.ReturnCode = "R13" + if err := addendaReturn.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.FieldName != "ReturnCode" { t.Errorf("%T: %s", err, err) @@ -96,9 +96,9 @@ func TestAddendaReturnValidateTrue(t *testing.T) { } func TestAddendaReturnValidateReturnCodeFalse(t *testing.T) { - rAddenda := mockAddendaReturn() - rAddenda.ReturnCode = "" - if err := rAddenda.Validate(); err != nil { + addendaReturn := mockAddendaReturn() + addendaReturn.ReturnCode = "" + if err := addendaReturn.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.FieldName != "ReturnCode" { t.Errorf("%T: %s", err, err) @@ -110,48 +110,48 @@ func TestAddendaReturnValidateReturnCodeFalse(t *testing.T) { } func TestAddendaReturnOriginalTraceField(t *testing.T) { - rAddenda := mockAddendaReturn() - rAddenda.OriginalTrace = 12345 - if rAddenda.OriginalTraceField() != "000000000012345" { - t.Errorf("expected %v received %v", "000000000012345", rAddenda.OriginalTraceField()) + addendaReturn := mockAddendaReturn() + addendaReturn.OriginalTrace = 12345 + if addendaReturn.OriginalTraceField() != "000000000012345" { + t.Errorf("expected %v received %v", "000000000012345", addendaReturn.OriginalTraceField()) } } func TestAddendaReturnDateOfDeathField(t *testing.T) { - rAddenda := mockAddendaReturn() + addendaReturn := mockAddendaReturn() // Check for all zeros - if rAddenda.DateOfDeathField() != " " { - t.Errorf("expected %v received %v", " ", rAddenda.DateOfDeathField()) + if addendaReturn.DateOfDeathField() != " " { + t.Errorf("expected %v received %v", " ", addendaReturn.DateOfDeathField()) } // Year: 1978 Month: October Day: 23 - rAddenda.DateOfDeath = time.Date(1978, time.October, 23, 0, 0, 0, 0, time.UTC) - if rAddenda.DateOfDeathField() != "781023" { - t.Errorf("expected %v received %v", "781023", rAddenda.DateOfDeathField()) + addendaReturn.DateOfDeath = time.Date(1978, time.October, 23, 0, 0, 0, 0, time.UTC) + if addendaReturn.DateOfDeathField() != "781023" { + t.Errorf("expected %v received %v", "781023", addendaReturn.DateOfDeathField()) } } func TestAddendaReturnOriginalDFIField(t *testing.T) { - rAddenda := mockAddendaReturn() + addendaReturn := mockAddendaReturn() exp := "09101298" - if rAddenda.OriginalDFIField() != exp { - t.Errorf("expected %v received %v", exp, rAddenda.OriginalDFIField()) + if addendaReturn.OriginalDFIField() != exp { + t.Errorf("expected %v received %v", exp, addendaReturn.OriginalDFIField()) } } func TestAddendaReturnAddendaInformationField(t *testing.T) { - rAddenda := mockAddendaReturn() + addendaReturn := mockAddendaReturn() exp := "Authorization Revoked " - if rAddenda.AddendaInformationField() != exp { - t.Errorf("expected %v received %v", exp, rAddenda.AddendaInformationField()) + if addendaReturn.AddendaInformationField() != exp { + t.Errorf("expected %v received %v", exp, addendaReturn.AddendaInformationField()) } } func TestAddendaReturnTraceNumberField(t *testing.T) { - rAddenda := mockAddendaReturn() - rAddenda.TraceNumber = 91012980000066 + addendaReturn := mockAddendaReturn() + addendaReturn.TraceNumber = 91012980000066 exp := "091012980000066" - if rAddenda.TraceNumberField() != exp { - t.Errorf("expected %v received %v", exp, rAddenda.TraceNumberField()) + if addendaReturn.TraceNumberField() != exp { + t.Errorf("expected %v received %v", exp, addendaReturn.TraceNumberField()) } } @@ -169,26 +169,26 @@ func TestAddendaReturnNewAddendaParam(t *testing.T) { if err != nil { t.Errorf("addendaReturn from NewAddeda: %v", err) } - rAddenda, ok := a.(*AddendaReturn) + addendaReturn, ok := a.(*AddendaReturn) if !ok { t.Errorf("expecting *AddendaReturn received %T ", a) } - if rAddenda.TypeCode() != aParam.TypeCode { - t.Errorf("expected %v got %v", aParam.TypeCode, rAddenda.TypeCode()) + if addendaReturn.TypeCode() != aParam.TypeCode { + t.Errorf("expected %v got %v", aParam.TypeCode, addendaReturn.TypeCode()) } - if rAddenda.ReturnCode != aParam.ReturnCode { - t.Errorf("expected %v got %v", aParam.ReturnCode, rAddenda.ReturnCode) + if addendaReturn.ReturnCode != aParam.ReturnCode { + t.Errorf("expected %v got %v", aParam.ReturnCode, addendaReturn.ReturnCode) } - if !strings.Contains(rAddenda.OriginalTraceField(), aParam.OriginalTrace) { - t.Errorf("expected %v got %v", aParam.OriginalTrace, rAddenda.OriginalTrace) + if !strings.Contains(addendaReturn.OriginalTraceField(), aParam.OriginalTrace) { + t.Errorf("expected %v got %v", aParam.OriginalTrace, addendaReturn.OriginalTrace) } - if !strings.Contains(rAddenda.OriginalDFIField(), aParam.OriginalDFI) { - t.Errorf("expected %v got %v", aParam.OriginalDFI, rAddenda.OriginalDFI) + if !strings.Contains(addendaReturn.OriginalDFIField(), aParam.OriginalDFI) { + t.Errorf("expected %v got %v", aParam.OriginalDFI, addendaReturn.OriginalDFI) } - if rAddenda.AddendaInformation != aParam.AddendaInfo { - t.Errorf("expected %v got %v", aParam.AddendaInfo, rAddenda.AddendaInformation) + if addendaReturn.AddendaInformation != aParam.AddendaInfo { + t.Errorf("expected %v got %v", aParam.AddendaInfo, addendaReturn.AddendaInformation) } - if !strings.Contains(rAddenda.TraceNumberField(), aParam.TraceNumber) { - t.Errorf("expected %v got %v", aParam.TraceNumber, rAddenda.TraceNumber) + if !strings.Contains(addendaReturn.TraceNumberField(), aParam.TraceNumber) { + t.Errorf("expected %v got %v", aParam.TraceNumber, addendaReturn.TraceNumber) } } From 0cddc7e2b84a1b20c288e0004fa2ec59396b8e73 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 31 Oct 2017 13:09:10 -0600 Subject: [PATCH 0024/1694] Create a Code of Conduct I want to keep the crazy actions of others out of this project. --- CODE_OF_CONDUCT.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..3f089c799 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at wade@wadearnold.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ From 749db25d91d57ca63ed258d3e6e51e115ec0a58a Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 1 Nov 2017 11:27:04 -0600 Subject: [PATCH 0025/1694] Support Batch IsReturn() to expose a batch as a Return Entry. Export File.ReturnEntires to have a list of batches being returned --- batch.go | 9 +++++++++ batcher.go | 1 + entryDetail.go | 22 ++++++++++++++-------- entryDetail_internal_test.go | 25 +++++++++++++++++++++++++ file.go | 5 +++++ 5 files changed, 54 insertions(+), 8 deletions(-) diff --git a/batch.go b/batch.go index e2032059e..94cb9dcde 100644 --- a/batch.go +++ b/batch.go @@ -7,6 +7,9 @@ type batch struct { header *BatchHeader entries []*EntryDetail control *BatchControl + + // isReturn is true if a return entry was added to the batch + isReturn bool // Converters is composed for ACH to GoLang Converters converters } @@ -160,9 +163,15 @@ func (batch *batch) GetEntries() []*EntryDetail { // AddEntry appends an EntryDetail to the Batch func (batch *batch) AddEntry(entry *EntryDetail) { + batch.isReturn = entry.isReturn batch.entries = append(batch.entries, entry) } +// IsReturn is true if the batch contains an Entry Return +func (batch *batch) IsReturn() bool { + return batch.isReturn +} + // isFieldInclusion iterates through all the records in the batch and verifies against default fields func (batch *batch) isFieldInclusion() error { if err := batch.header.Validate(); err != nil { diff --git a/batcher.go b/batcher.go index da0042632..60587ac10 100644 --- a/batcher.go +++ b/batcher.go @@ -21,6 +21,7 @@ type Batcher interface { AddEntry(*EntryDetail) Create() error Validate() error + IsReturn() bool } // BatchError is an Error that describes batch validation issues diff --git a/entryDetail.go b/entryDetail.go index e6b6f0da5..54b2f831b 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -28,7 +28,7 @@ type EntryDetail struct { // Prenote for debit to savings account ‘38’ TransactionCode int - // rdfiIdentification is the RDFI's routing number without the last digit. + // RDFIIdentification is the RDFI's routing number without the last digit. // Receiving Depository Financial Institution RDFIIdentification int @@ -78,6 +78,8 @@ type EntryDetail struct { Addendum []Addendumer // ReturnAddendum stores return types. These are processed separately ReturnAddendum []AddendaReturn + // isReturn indicates the existence of a AddendaReturn in Addendum + isReturn bool // validator is composed for data validation validator // converters is composed for ACH to golang Converters @@ -200,7 +202,6 @@ func (ed *EntryDetail) Validate() error { msg := fmt.Sprintf(msgValidCheckDigit, calculated) return &FieldError{FieldName: "RDFIIdentification", Value: strconv.Itoa(ed.CheckDigit), Msg: msg} } - return nil } @@ -219,10 +220,6 @@ func (ed *EntryDetail) fieldInclusion() error { if ed.DFIAccountNumber == "" { return &FieldError{FieldName: "DFIAccountNumber", Value: ed.DFIAccountNumber, Msg: msgFieldInclusion} } - // TODO: amount can be 0 if it's COR, should probably be more specific... - /*if ed.Amount == 0 { - return &FieldError{FieldName: "Amount", Value: ed.AmountField(), Msg: msgFieldInclusion} - }*/ if ed.IndividualName == "" { return &FieldError{FieldName: "IndividualName", Value: ed.IndividualName, Msg: msgFieldInclusion} } @@ -236,9 +233,18 @@ func (ed *EntryDetail) fieldInclusion() error { func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.AddendaRecordIndicator = 1 // checks to make sure that we only have either or, not both - if ed.ReturnAddendum != nil { - return nil + switch addenda.(type) { + case *AddendaReturn: + ed.isReturn = true + // Only 1 Addendum can exist for returns. Overwrite existing AddendaReturn + if ed.Addendum != nil { + ed.Addendum[0] = addenda + return ed.Addendum + } + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum } + ed.isReturn = false ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum } diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index 8789efafc..da946178d 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -287,3 +287,28 @@ func TestEDFieldInclusionTraceNumber(t *testing.T) { } } } + +func TestEDAddAddendaAddendaReturn(t *testing.T) { + entry := mockEntryDetail() + entry.AddAddenda(mockAddendaReturn()) + if !entry.isReturn { + t.Error("AddendaReturn added and isReturn is false") + } + if entry.AddendaRecordIndicator != 1 { + t.Error("AddendaReturn added and record indicator is not 1") + } + +} + +func TestEDAddAddendaAddendaReturnTwice(t *testing.T) { + entry := mockEntryDetail() + entry.AddAddenda(mockAddendaReturn()) + entry.AddAddenda(mockAddendaReturn()) + if !entry.isReturn { + t.Error("AddendaReturn added and isReturn is false") + } + + if len(entry.Addendum) != 1 { + t.Error("AddendaReturn added and isReturn is false") + } +} diff --git a/file.go b/file.go index 251297c3b..c1159f149 100644 --- a/file.go +++ b/file.go @@ -68,6 +68,8 @@ type File struct { // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches NotificationOfChange []*BatchCOR + // ReturnEntries is a slice of references to file.Batches that contain return entires + ReturnEntries []Batcher converters } @@ -156,6 +158,9 @@ func (f *File) AddBatch(batch Batcher) []Batcher { case *BatchCOR: f.NotificationOfChange = append(f.NotificationOfChange, batch.(*BatchCOR)) } + if batch.IsReturn() { + f.ReturnEntries = append(f.ReturnEntries, batch) + } f.Batches = append(f.Batches, batch) return f.Batches } From e630e34cda3b248dbd4632b4f2f2ad26e2a5cc6e Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 1 Nov 2017 12:04:16 -0600 Subject: [PATCH 0026/1694] Simplified to clear the Addendum to assign a AddendaReturn --- entryDetail.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/entryDetail.go b/entryDetail.go index 54b2f831b..69d295ae3 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -236,11 +236,8 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { switch addenda.(type) { case *AddendaReturn: ed.isReturn = true - // Only 1 Addendum can exist for returns. Overwrite existing AddendaReturn - if ed.Addendum != nil { - ed.Addendum[0] = addenda - return ed.Addendum - } + // Only 1 Addendum can exist for returns. Overwrite existing Addendum + ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum } From 927406c6a2963fb12611c648b443915cb17bc46b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 1 Nov 2017 18:36:23 -0600 Subject: [PATCH 0027/1694] Exported File.ReturnEntries[] is a slice of pointers to File.Batches[] that contain return entries --- entryDetail.go | 13 ++++++++++--- file_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/entryDetail.go b/entryDetail.go index 69d295ae3..0a86847d9 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -240,10 +240,17 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum + case *AddendaNOC: + // Only 1 Addendum can exist for Notification of Change. Overwrite existing Addendum + ed.Addendum = nil + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum + default: + // Batch type needs to validate number of addendum + ed.isReturn = false + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum } - ed.isReturn = false - ed.Addendum = append(ed.Addendum, addenda) - return ed.Addendum } // AddAddendaReturn appends an ReturnAddendum to the entry diff --git a/file_test.go b/file_test.go index fb04e40b1..16bee67ce 100644 --- a/file_test.go +++ b/file_test.go @@ -5,6 +5,7 @@ package ach import ( + "fmt" "testing" ) @@ -176,3 +177,33 @@ func TestFileNotificationOfChange(t *testing.T) { t.Error("BatchCOR added to File.AddBatch should exist in NotificationOfChange") } } + +func TestFileReturnEntries(t *testing.T) { + // create or copy the entry to be returned record + entry := mockEntryDetail() + // Add the addenda return with appropriate ReturnCode and addenda information + entry.AddAddenda(mockAddendaReturn()) + // create or copy the previous batch header of the item being returned + batchHeader := mockBatchHeader() + // create or copy the batch to be returned + batch, err := NewBatch(BatchParam{StandardEntryClass: batchHeader.StandardEntryClassCode}) + if err != nil { + t.Error(err.Error()) + } + // Add the entry to be returned to the batch + batch.AddEntry(entry) + // Create the batch + batch.Create() + // Add the batch to your file. + file := NewFile().SetHeader(mockFileHeader()) + file.AddBatch(batch) + // Create the return file + if err := file.Create(); err != nil { + t.Error(err.Error()) + } + + for i := range file.ReturnEntries { + fmt.Printf("%+v\n", file.ReturnEntries[i]) + } + +} From 57e37f393ba748c296b9c00fef0b62e775379def Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 1 Nov 2017 18:57:47 -0600 Subject: [PATCH 0028/1694] Readme: Document how to access just the ReturnEntires in a file --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 0c853cd18..a494dced8 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,13 @@ if len(achFile.NotificationOfChange) > 0 { println(aNOC.CorrectedData) } } +// Check if any Return Entries exist in the file +if len(achFile.ReturnEntries) > 0 { + for _, batch := range achFile.ReturnEntries { + aReturn := batch.GetEntries()[0].Addendum[0].(*AddendaReturn) + println(aReturn.ReturnCode) + } +} ``` ### Create a file From e03432cff9a5a46be1d13db37ae82f089885e025 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 2 Nov 2017 10:55:10 -0600 Subject: [PATCH 0029/1694] Updating Reaadme for google groups information --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a494dced8..3200a20d7 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,14 @@ Package 'moov-io/ach' implements a file reader and writer for parsing [ACH](http ## Project Status -ACH is at an early stage and under active development. Please star the project if you are interested in its progress. +ACH is at an early stage and under active development but is already in product for multiple companies. Please star the project if you are interested in its progress. * Library currently supports the reading and writing * PPD (Prearranged payment and deposits) * WEB (Internet-initiated Entries ) * CCD (Corporate credit or debit) * COR (Automated Notification of Change(NOC)) + * Return Entires ## Project Roadmap @@ -206,6 +207,9 @@ Which will generate a well formed ACH flat file. 82200000020010200101000000000000000000000799123456789 234567890000002 9000002000001000000040020400202000000017500000000000799 ``` +# Mailing lists + +Users trade notes on the Google group moov-users (send mail to moov-users@googlegroups.com). You must join the group in order to post. # Contributing From 577d4caa1ab926d7d7bde7ebe680e63eb8b0d823 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 2 Nov 2017 10:57:44 -0600 Subject: [PATCH 0030/1694] Link to Moov-users google group --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3200a20d7..a682a2147 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ Which will generate a well formed ACH flat file. ``` # Mailing lists -Users trade notes on the Google group moov-users (send mail to moov-users@googlegroups.com). You must join the group in order to post. +Users trade notes on the Google group moov-users (send mail to moov-users@googlegroups.com). You must join the [moov-users](https://groups.google.com/forum/#!forum/moov-users)forumn in order to post. # Contributing From ed1b0d281d37a2d1bc3d54f446df2437d172dff5 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 14 Nov 2017 10:43:24 -0700 Subject: [PATCH 0031/1694] fail test if return entries does not exists as set --- file_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/file_test.go b/file_test.go index 16bee67ce..721e11316 100644 --- a/file_test.go +++ b/file_test.go @@ -5,7 +5,6 @@ package ach import ( - "fmt" "testing" ) @@ -202,8 +201,7 @@ func TestFileReturnEntries(t *testing.T) { t.Error(err.Error()) } - for i := range file.ReturnEntries { - fmt.Printf("%+v\n", file.ReturnEntries[i]) + if len(file.ReturnEntries) != 1 { + t.Errorf("1 file.ReturnEntries added and %v exist", len(file.ReturnEntries)) } - } From 6ff982bfdcfaf2577fe6ebdda8e3b115093056a6 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 14 Nov 2017 11:03:18 -0700 Subject: [PATCH 0032/1694] Remove ReturnAddendum. A batch can only be a return or payment. Simpliefied with Addendumer interface on Addenda type --- batch.go | 16 +++++----------- entryDetail.go | 18 ------------------ 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/batch.go b/batch.go index 94cb9dcde..16ed36908 100644 --- a/batch.go +++ b/batch.go @@ -199,7 +199,7 @@ func (batch *batch) isFieldInclusion() error { func (batch *batch) isBatchEntryCount() error { entryCount := 0 for _, entry := range batch.entries { - entryCount = entryCount + 1 + len(entry.Addendum) + len(entry.ReturnAddendum) + entryCount = entryCount + 1 + len(entry.Addendum) } if entryCount != batch.control.EntryAddendaCount { msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.control.EntryAddendaCount) @@ -333,17 +333,11 @@ func (batch *batch) isAddendaSequence() error { // "PPD", "WEB", "CCD", "CIE", "DNE", "MTE", "POS", "SHR" func (batch *batch) isAddendaCount(count int) error { for _, entry := range batch.entries { - if !entry.HasAddendaReturn() { - if len(entry.Addendum) > count { - msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaCount", Msg: msg} - } - } else { - if len(entry.ReturnAddendum) > count { - msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.ReturnAddendum), count, batch.header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaReturnCount", Msg: msg} - } + if len(entry.Addendum) > count { + msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.header.StandardEntryClassCode) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaCount", Msg: msg} } + } return nil } diff --git a/entryDetail.go b/entryDetail.go index 0a86847d9..9d1154e81 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -76,8 +76,6 @@ type EntryDetail struct { // Addendum a list of Addenda for the Entry Detail Addendum []Addendumer - // ReturnAddendum stores return types. These are processed separately - ReturnAddendum []AddendaReturn // isReturn indicates the existence of a AddendaReturn in Addendum isReturn bool // validator is composed for data validation @@ -253,17 +251,6 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { } } -// AddAddendaReturn appends an ReturnAddendum to the entry -func (ed *EntryDetail) AddAddendaReturn(returnAddendum AddendaReturn) []AddendaReturn { - ed.AddendaRecordIndicator = 1 - // checks to make sure that we only have either or, not both - if ed.Addendum != nil { - return nil - } - ed.ReturnAddendum = append(ed.ReturnAddendum, returnAddendum) - return ed.ReturnAddendum -} - // SetRDFI takes the 9 digit RDFI account number and separates it for RDFIIdentification and CheckDigit func (ed *EntryDetail) SetRDFI(rdfi int) *EntryDetail { s := ed.numericField(rdfi, 9) @@ -339,8 +326,3 @@ func (ed *EntryDetail) SetPaymentType(t string) { func (ed *EntryDetail) TraceNumberField() string { return ed.numericField(ed.TraceNumber, 15) } - -// HasAddendaReturn returns true if entry has return addenda -func (ed *EntryDetail) HasAddendaReturn() bool { - return ed.ReturnAddendum != nil -} From aa9f6ef788ee3d5fb4f1e0ca7ae1f980fb5e1626 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 14 Nov 2017 11:56:50 -0700 Subject: [PATCH 0033/1694] Seperate batch.go generic batch test coverage from BatchPPD test coverage into batch_test --- batchPPD_test.go | 215 ------------------------------------------- batch_test.go | 234 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+), 215 deletions(-) create mode 100644 batch_test.go diff --git a/batchPPD_test.go b/batchPPD_test.go index ca7a531bc..f3e4e3118 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -102,221 +102,6 @@ func TestBatchODFIIDMismatch(t *testing.T) { } } -func TestBatchNumberMismatch(t *testing.T) { - mockBatch := mockBatchPPD() - mockBatch.GetControl().BatchNumber = 2 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "BatchNumber" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestCreditBatchisBatchAmount(t *testing.T) { - mockBatch := NewBatchPPD() - mockBatch.SetHeader(mockBatchHeader()) - e1 := mockEntryDetail() - e1.TransactionCode = 22 - e1.Amount = 100 - e2 := mockEntryDetail() - e2.TransactionCode = 22 - e2.Amount = 100 - mockBatch.AddEntry(e1) - mockBatch.AddEntry(e2) - if err := mockBatch.Create(); err != nil { - t.Errorf("%T: %s", err, err) - } - - mockBatch.GetControl().TotalCreditEntryDollarAmount = 1 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "TotalCreditEntryDollarAmount" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestSavingsBatchisBatchAmount(t *testing.T) { - mockBatch := NewBatchPPD() - mockBatch.SetHeader(mockBatchHeader()) - e1 := mockEntryDetail() - e1.TransactionCode = 32 - e1.Amount = 100 - e2 := mockEntryDetail() - e2.TransactionCode = 37 - e2.Amount = 100 - mockBatch.AddEntry(e1) - mockBatch.AddEntry(e2) - if err := mockBatch.Create(); err != nil { - t.Errorf("%T: %s", err, err) - } - - mockBatch.GetControl().TotalDebitEntryDollarAmount = 1 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "TotalDebitEntryDollarAmount" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchisEntryHash(t *testing.T) { - mockBatch := mockBatchPPD() - mockBatch.GetControl().EntryHash = 1 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "EntryHash" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchDNEMismatch(t *testing.T) { - mockBatch := NewBatchPPD() - mockBatch.SetHeader(mockBatchHeader()) - ed := mockEntryDetail() - ed.AddAddenda(mockAddenda()) - ed.AddAddenda(mockAddenda()) - mockBatch.AddEntry(ed) - mockBatch.Create() - - mockBatch.GetHeader().OriginatorStatusCode = 1 - mockBatch.GetEntries()[0].TransactionCode = 23 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "OriginatorStatusCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchTraceNumberNotODFI(t *testing.T) { - mockBatch := mockBatchPPD() - mockBatch.GetEntries()[0].setTraceNumber(12345678, 1) - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "ODFIIdentificationField" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchEntryCountEquality(t *testing.T) { - mockBatch := NewBatchPPD() - mockBatch.SetHeader(mockBatchHeader()) - e := mockEntryDetail() - a := mockAddenda() - e.AddAddenda(a) - mockBatch.AddEntry(e) - if err := mockBatch.Create(); err != nil { - t.Errorf("%T: %s", err, err) - } - - mockBatch.GetControl().EntryAddendaCount = 1 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "EntryAddendaCount" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchAddendaIndicator(t *testing.T) { - mockBatch := mockBatchPPD() - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) - mockBatch.GetEntries()[0].AddendaRecordIndicator = 0 - mockBatch.GetControl().EntryAddendaCount = 2 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "AddendaRecordIndicator" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchIsAddendaSeqAscending(t *testing.T) { - mockBatch := NewBatchPPD() - mockBatch.SetHeader(mockBatchHeader()) - ed := mockEntryDetail() - ed.AddAddenda(mockAddenda()) - ed.AddAddenda(mockAddenda()) - mockBatch.AddEntry(ed) - mockBatch.Create() - - mockBatch.GetEntries()[0].Addendum[0].(*Addenda).SequenceNumber = 2 - mockBatch.GetEntries()[0].Addendum[1].(*Addenda).SequenceNumber = 1 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "SequenceNumber" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchIsSequenceAscending(t *testing.T) { - mockBatch := mockBatchPPD() - e3 := mockEntryDetail() - e3.TraceNumber = 1 - mockBatch.AddEntry(e3) - mockBatch.GetControl().EntryAddendaCount = 2 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "TraceNumber" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestBatchAddendaTraceNumber(t *testing.T) { - mockBatch := mockBatchPPD() - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) - if err := mockBatch.Create(); err != nil { - t.Errorf("%T: %s", err, err) - } - - mockBatch.GetEntries()[0].Addendum[0].(*Addenda).EntryDetailSequenceNumber = 99 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "TraceNumber" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - func TestBatchBuild(t *testing.T) { mockBatch := NewBatchPPD() header := NewBatchHeader() diff --git a/batch_test.go b/batch_test.go new file mode 100644 index 000000000..eb2eb69fa --- /dev/null +++ b/batch_test.go @@ -0,0 +1,234 @@ +package ach + +import ( + "testing" +) + +// batch should never be used directly. +func mockBatch() *batch { + mockBatch := &batch{} + mockBatch.SetHeader(mockBatchHeader()) + mockBatch.AddEntry(mockEntryDetail()) + if err := mockBatch.build(); err != nil { + panic(err) + } + return mockBatch +} + +// Test cases that apply to all batch types + +func TestBatchNumberMismatch(t *testing.T) { + mockBatch := mockBatch() + mockBatch.GetControl().BatchNumber = 2 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "BatchNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestCreditBatchisBatchAmount(t *testing.T) { + mockBatch := mockBatch() + mockBatch.SetHeader(mockBatchHeader()) + e1 := mockEntryDetail() + e1.TransactionCode = 22 + e1.Amount = 100 + e2 := mockEntryDetail() + e2.TransactionCode = 22 + e2.Amount = 100 + mockBatch.AddEntry(e1) + mockBatch.AddEntry(e2) + if err := mockBatch.verify(); err != nil { + t.Errorf("%T: %s", err, err) + } + + mockBatch.GetControl().TotalCreditEntryDollarAmount = 1 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TotalCreditEntryDollarAmount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestSavingsBatchisBatchAmount(t *testing.T) { + mockBatch := mockBatch() + mockBatch.SetHeader(mockBatchHeader()) + e1 := mockEntryDetail() + e1.TransactionCode = 32 + e1.Amount = 100 + e2 := mockEntryDetail() + e2.TransactionCode = 37 + e2.Amount = 100 + mockBatch.AddEntry(e1) + mockBatch.AddEntry(e2) + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + mockBatch.GetControl().TotalDebitEntryDollarAmount = 1 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TotalDebitEntryDollarAmount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchisEntryHash(t *testing.T) { + mockBatch := mockBatch() + mockBatch.GetControl().EntryHash = 1 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "EntryHash" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchDNEMismatch(t *testing.T) { + mockBatch := mockBatch() + mockBatch.SetHeader(mockBatchHeader()) + ed := mockEntryDetail() + ed.AddAddenda(mockAddenda()) + ed.AddAddenda(mockAddenda()) + mockBatch.AddEntry(ed) + mockBatch.build() + + mockBatch.GetHeader().OriginatorStatusCode = 1 + mockBatch.GetEntries()[0].TransactionCode = 23 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "OriginatorStatusCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchTraceNumberNotODFI(t *testing.T) { + mockBatch := mockBatch() + mockBatch.GetEntries()[0].setTraceNumber(12345678, 1) + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ODFIIdentificationField" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchEntryCountEquality(t *testing.T) { + mockBatch := mockBatch() + mockBatch.SetHeader(mockBatchHeader()) + e := mockEntryDetail() + a := mockAddenda() + e.AddAddenda(a) + mockBatch.AddEntry(e) + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + mockBatch.GetControl().EntryAddendaCount = 1 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "EntryAddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchAddendaIndicator(t *testing.T) { + mockBatch := mockBatch() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddendaRecordIndicator = 0 + mockBatch.GetControl().EntryAddendaCount = 2 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaRecordIndicator" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchIsAddendaSeqAscending(t *testing.T) { + mockBatch := mockBatch() + mockBatch.SetHeader(mockBatchHeader()) + ed := mockEntryDetail() + ed.AddAddenda(mockAddenda()) + ed.AddAddenda(mockAddenda()) + mockBatch.AddEntry(ed) + mockBatch.build() + + mockBatch.GetEntries()[1].Addendum[0].(*Addenda).SequenceNumber = 2 + mockBatch.GetEntries()[1].Addendum[1].(*Addenda).SequenceNumber = 1 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "SequenceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +func TestBatchIsSequenceAscending(t *testing.T) { + mockBatch := mockBatch() + e3 := mockEntryDetail() + e3.TraceNumber = 1 + mockBatch.AddEntry(e3) + mockBatch.GetControl().EntryAddendaCount = 2 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchAddendaTraceNumber(t *testing.T) { + mockBatch := mockBatch() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + mockBatch.GetEntries()[0].Addendum[0].(*Addenda).EntryDetailSequenceNumber = 99 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} From c183ecb46e52f5777b9171642402a855303ee12a Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 14 Nov 2017 12:12:09 -0700 Subject: [PATCH 0034/1694] NewBatch default error and test coverage for none supported SEC codes. --- batch.go | 4 ++-- batch_test.go | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/batch.go b/batch.go index 16ed36908..2075fb95b 100644 --- a/batch.go +++ b/batch.go @@ -26,9 +26,9 @@ func NewBatch(bp BatchParam) (Batcher, error) { case "COR": return NewBatchCOR(bp), nil default: - msg := fmt.Sprintf(msgFileNoneSEC, sec) - return nil, &FileError{FieldName: "StandardEntryClassCode", Msg: msg} } + msg := fmt.Sprintf(msgFileNoneSEC, bp.StandardEntryClass) + return nil, &FileError{FieldName: "StandardEntryClassCode", Msg: msg} } // verify checks basic valid NACHA batch rules. Assumes properly parsed records. This does not mean it is a valid batch as validity is tied to each batch type diff --git a/batch_test.go b/batch_test.go index eb2eb69fa..5ae2976b4 100644 --- a/batch_test.go +++ b/batch_test.go @@ -42,7 +42,7 @@ func TestCreditBatchisBatchAmount(t *testing.T) { e2.Amount = 100 mockBatch.AddEntry(e1) mockBatch.AddEntry(e2) - if err := mockBatch.verify(); err != nil { + if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) } @@ -232,3 +232,16 @@ func TestBatchAddendaTraceNumber(t *testing.T) { } } } + +func TestNewBatchDefault(t *testing.T) { + _, err := NewBatch(BatchParam{ + StandardEntryClass: "NIL"}) + + if e, ok := err.(*FileError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } +} From 1484d548a08aef10a08194c7b1e5bbe9843c1a5e Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 14 Nov 2017 16:51:29 -0700 Subject: [PATCH 0035/1694] BatchHeader.BatchParam returns the current BatchHeader values as a BatchParam --- batchHeader.go | 14 ++++++++++ batchHeader_internal_test.go | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/batchHeader.go b/batchHeader.go index 39464ebdf..8270a2002 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -280,3 +280,17 @@ func (bh *BatchHeader) BatchNumberField() string { func (bh *BatchHeader) settlementDateField() string { return bh.alphaField(bh.settlementDate, 3) } + +// BatchParam returns a BatchParam with the values from BatchHeader +func (bh *BatchHeader) BatchParam() BatchParam { + bp := BatchParam{ + ServiceClassCode: strconv.Itoa(bh.ServiceClassCode), + CompanyName: bh.CompanyNameField(), + CompanyIdentification: bh.CompanyIdentification, + StandardEntryClass: bh.StandardEntryClassCode, + CompanyEntryDescription: bh.CompanyEntryDescription, + CompanyDescriptiveDate: bh.CompanyDescriptiveDateField(), + EffectiveEntryDate: bh.EffectiveEntryDateField(), + ODFIIdentification: bh.ODFIIdentificationField()} + return bp +} diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index cab77c8a9..bac07450e 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -294,3 +294,53 @@ func TestBHFieldInclusionOriginatorStatusCode(t *testing.T) { } } } + +func TestBHtoBatchParam(t *testing.T) { + bh := mockBatchHeader() + bp := bh.BatchParam() + + if bh.ServiceClassCode != bh.parseNumField(bp.ServiceClassCode) { + t.Errorf("ServiceClassCode Expected '%b' got: %s", bh.ServiceClassCode, bp.ServiceClassCode) + } + if bh.CompanyName != bp.CompanyName { + t.Errorf("CompanyName Expected '%s' got: %s", bh.CompanyName, bp.CompanyName) + } + if bh.StandardEntryClassCode != bp.StandardEntryClass { + t.Errorf("StandardEntryClass Expected '%s' got: %s", bh.StandardEntryClassCode, bp.StandardEntryClass) + } + if bh.CompanyIdentification != bp.CompanyIdentification { + t.Errorf("CompanyIdentification Expected '%s' got: %s", bh.CompanyIdentification, bp.CompanyIdentification) + } + if bh.CompanyEntryDescription != bp.CompanyEntryDescription { + t.Errorf("CompanyEntryDescription Expected '%s' got: %s", bh.CompanyEntryDescription, bp.CompanyEntryDescription) + } + if bh.CompanyDescriptiveDateField() != bp.CompanyDescriptiveDate { + t.Errorf("CompanyDescriptiveDate Expected '%s' got: %s", bh.CompanyDescriptiveDateField(), bp.CompanyDescriptiveDate) + } + if bh.ODFIIdentification != bh.parseNumField(bp.ODFIIdentification) { + t.Errorf("ODFIIdentification Expected '%b' got: %s", bh.ODFIIdentification, bp.ODFIIdentification) + } + +} + +/* +batch := NewBatchPPD(BatchParam{ + ServiceClassCode: "220", + CompanyName: companyName, + StandardEntryClass: "PPD", + CompanyIdentification: "123456789", + CompanyEntryDescription: "Trans. Description", + CompanyDescriptiveDate: "Oct 23", + ODFIIdentification: "123456789"}) + +*/ +/* + bh.ServiceClassCode = bh.parseNumField(params[0].ServiceClassCode) + bh.CompanyName = params[0].CompanyName + bh.CompanyIdentification = params[0].CompanyIdentification + bh.StandardEntryClassCode = params[0].StandardEntryClass + bh.CompanyEntryDescription = params[0].CompanyEntryDescription + bh.CompanyDescriptiveDate = params[0].CompanyDescriptiveDate + bh.EffectiveEntryDate = bh.parseSimpleDate(params[0].EffectiveEntryDate) + bh.ODFIIdentification = bh.parseNumField(params[0].ODFIIdentification) +*/ From 7bec2a7dea60d431a41b9512bedb53c58c97fd3e Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 14 Nov 2017 16:52:36 -0700 Subject: [PATCH 0036/1694] remove commented code --- batchHeader_internal_test.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index bac07450e..fd4fa1a0c 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -320,27 +320,4 @@ func TestBHtoBatchParam(t *testing.T) { if bh.ODFIIdentification != bh.parseNumField(bp.ODFIIdentification) { t.Errorf("ODFIIdentification Expected '%b' got: %s", bh.ODFIIdentification, bp.ODFIIdentification) } - } - -/* -batch := NewBatchPPD(BatchParam{ - ServiceClassCode: "220", - CompanyName: companyName, - StandardEntryClass: "PPD", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Trans. Description", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) - -*/ -/* - bh.ServiceClassCode = bh.parseNumField(params[0].ServiceClassCode) - bh.CompanyName = params[0].CompanyName - bh.CompanyIdentification = params[0].CompanyIdentification - bh.StandardEntryClassCode = params[0].StandardEntryClass - bh.CompanyEntryDescription = params[0].CompanyEntryDescription - bh.CompanyDescriptiveDate = params[0].CompanyDescriptiveDate - bh.EffectiveEntryDate = bh.parseSimpleDate(params[0].EffectiveEntryDate) - bh.ODFIIdentification = bh.parseNumField(params[0].ODFIIdentification) -*/ From 5296c5ffa518c2e6f89e0db6627f8609a25c8f5f Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 16 Nov 2017 16:04:39 -0700 Subject: [PATCH 0037/1694] Entry isReturn to Category w/ Forward, Return, or NOC values for better reporting and error handling --- batch.go | 10 +++++----- batcher.go | 3 ++- entryDetail.go | 19 ++++++++++++------- entryDetail_internal_test.go | 7 ++++--- file.go | 2 +- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/batch.go b/batch.go index 2075fb95b..e64485809 100644 --- a/batch.go +++ b/batch.go @@ -8,8 +8,8 @@ type batch struct { entries []*EntryDetail control *BatchControl - // isReturn is true if a return entry was added to the batch - isReturn bool + // category defines if the entry is a Forward, Return, or NOC + category string // Converters is composed for ACH to GoLang Converters converters } @@ -163,13 +163,13 @@ func (batch *batch) GetEntries() []*EntryDetail { // AddEntry appends an EntryDetail to the Batch func (batch *batch) AddEntry(entry *EntryDetail) { - batch.isReturn = entry.isReturn + batch.category = entry.Category batch.entries = append(batch.entries, entry) } // IsReturn is true if the batch contains an Entry Return -func (batch *batch) IsReturn() bool { - return batch.isReturn +func (batch *batch) Category() string { + return batch.category } // isFieldInclusion iterates through all the records in the batch and verifies against default fields diff --git a/batcher.go b/batcher.go index 60587ac10..aef37915a 100644 --- a/batcher.go +++ b/batcher.go @@ -21,7 +21,8 @@ type Batcher interface { AddEntry(*EntryDetail) Create() error Validate() error - IsReturn() bool + // Category defines if a Forward or Return + Category() string } // BatchError is an Error that describes batch validation issues diff --git a/entryDetail.go b/entryDetail.go index 9d1154e81..e96a1befb 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -76,14 +76,20 @@ type EntryDetail struct { // Addendum a list of Addenda for the Entry Detail Addendum []Addendumer - // isReturn indicates the existence of a AddendaReturn in Addendum - isReturn bool + // Category defines if the entry is a Forward, Return, or NOC + Category string // validator is composed for data validation validator // converters is composed for ACH to golang Converters converters } +const ( + CategoryForward = "Forward" + CategoryReturn = "Return" + CategoryNOC = "NOC" +) + // EntryParam is the minimal fields required to make a ach entry type EntryParam struct { ReceivingDFI string `json:"receiving_dfi"` @@ -101,6 +107,7 @@ type EntryParam struct { func NewEntryDetail(params ...EntryParam) *EntryDetail { entry := &EntryDetail{ recordType: "6", + Category: CategoryForward, } if len(params) > 0 { entry.SetRDFI(entry.parseNumField(params[0].ReceivingDFI)) @@ -233,19 +240,17 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { // checks to make sure that we only have either or, not both switch addenda.(type) { case *AddendaReturn: - ed.isReturn = true - // Only 1 Addendum can exist for returns. Overwrite existing Addendum + ed.Category = CategoryReturn ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum case *AddendaNOC: - // Only 1 Addendum can exist for Notification of Change. Overwrite existing Addendum + ed.Category = CategoryNOC ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum default: - // Batch type needs to validate number of addendum - ed.isReturn = false + ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum } diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index da946178d..4d4ce1ce9 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -17,6 +17,7 @@ func mockEntryDetail() *EntryDetail { entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" entry.TraceNumber = 123456789 + entry.Category = CategoryForward return entry } @@ -291,7 +292,7 @@ func TestEDFieldInclusionTraceNumber(t *testing.T) { func TestEDAddAddendaAddendaReturn(t *testing.T) { entry := mockEntryDetail() entry.AddAddenda(mockAddendaReturn()) - if !entry.isReturn { + if entry.Category != CategoryReturn { t.Error("AddendaReturn added and isReturn is false") } if entry.AddendaRecordIndicator != 1 { @@ -304,8 +305,8 @@ func TestEDAddAddendaAddendaReturnTwice(t *testing.T) { entry := mockEntryDetail() entry.AddAddenda(mockAddendaReturn()) entry.AddAddenda(mockAddendaReturn()) - if !entry.isReturn { - t.Error("AddendaReturn added and isReturn is false") + if entry.Category != CategoryReturn { + t.Error("AddendaReturn added and Category is not CategoryReturn") } if len(entry.Addendum) != 1 { diff --git a/file.go b/file.go index c1159f149..186403ae7 100644 --- a/file.go +++ b/file.go @@ -158,7 +158,7 @@ func (f *File) AddBatch(batch Batcher) []Batcher { case *BatchCOR: f.NotificationOfChange = append(f.NotificationOfChange, batch.(*BatchCOR)) } - if batch.IsReturn() { + if batch.Category() == CategoryReturn { f.ReturnEntries = append(f.ReturnEntries, batch) } f.Batches = append(f.Batches, batch) From a73156a3602dbfba7d89f110e693492243fc7b7d Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sat, 18 Nov 2017 11:08:02 -0700 Subject: [PATCH 0038/1694] batch verify that forward and return entries are not in the sae batch closes #150 --- batch.go | 19 +++++++++++++++++++ batch_test.go | 33 +++++++++++++++++++++++++++++++++ batcher.go | 1 + 3 files changed, 53 insertions(+) diff --git a/batch.go b/batch.go index e64485809..32115ba4b 100644 --- a/batch.go +++ b/batch.go @@ -91,6 +91,9 @@ func (batch *batch) verify() error { if err := batch.isAddendaSequence(); err != nil { return err } + if err := batch.isCategory(); err != nil { + return err + } return nil } @@ -354,3 +357,19 @@ func (batch *batch) isTypeCode(typeCode string) error { } return nil } + +// isCategory verifies that a Forward and Return Category are not in the same batch +func (batch *batch) isCategory() error { + category := batch.GetEntries()[0].Category + if len(batch.entries) > 1 { + for i := 1; i < len(batch.entries); i++ { + if batch.entries[i].Category == CategoryNOC { + continue + } + if batch.entries[i].Category != category { + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Category", Msg: msgBatchForwardReturn} + } + } + } + return nil +} diff --git a/batch_test.go b/batch_test.go index 5ae2976b4..5ef7233a4 100644 --- a/batch_test.go +++ b/batch_test.go @@ -245,3 +245,36 @@ func TestNewBatchDefault(t *testing.T) { t.Errorf("%T: %s", err, err) } } + +func TestBatchCategory(t *testing.T) { + mockBatch := mockBatch() + // Add a Addenda Return to the mock batch + mockBatch.GetEntries()[0].AddAddenda(mockAddendaReturn()) + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + if mockBatch.Category() != CategoryReturn { + t.Errorf("AddendaReturn added to batch and category is %s", mockBatch.Category()) + } +} + +func TestBatchCategoryForwardReturn(t *testing.T) { + mockBatch := mockBatch() + // Add a Addenda Return to the mock batch + entry := mockEntryDetail() + entry.AddAddenda(mockAddendaReturn()) + mockBatch.AddEntry(entry) + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Category" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} diff --git a/batcher.go b/batcher.go index aef37915a..49abe9566 100644 --- a/batcher.go +++ b/batcher.go @@ -77,4 +77,5 @@ var ( msgBatchTransactionCodeCredit = "%v a credit is not allowed" msgBatchSECType = "header SEC type code %v for batch type %v" msgBatchTypeCode = "%v found in addenda and expecting %v for batch type %v" + msgBatchForwardReturn = "Forward and Return entries found in the same batch" ) From 62812198be4fb8a6fce630eade4863576086d2d5 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 20 Nov 2017 11:39:19 -0700 Subject: [PATCH 0039/1694] Ignore delve debugger config --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 44842dc21..c3142be78 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ _testmain.go *.exe *.test *.prof + +.vscode/launch.json From 419be60380e14147bf17d39ca46654f6932d9dd1 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 20 Nov 2017 12:45:12 -0700 Subject: [PATCH 0040/1694] resolved failed test case looking at the wrong element --- batch_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/batch_test.go b/batch_test.go index 5ef7233a4..ea9169969 100644 --- a/batch_test.go +++ b/batch_test.go @@ -176,7 +176,6 @@ func TestBatchAddendaIndicator(t *testing.T) { func TestBatchIsAddendaSeqAscending(t *testing.T) { mockBatch := mockBatch() - mockBatch.SetHeader(mockBatchHeader()) ed := mockEntryDetail() ed.AddAddenda(mockAddenda()) ed.AddAddenda(mockAddenda()) @@ -248,8 +247,12 @@ func TestNewBatchDefault(t *testing.T) { func TestBatchCategory(t *testing.T) { mockBatch := mockBatch() + // mockBatch.GetEntries()[0] = nil // Add a Addenda Return to the mock batch - mockBatch.GetEntries()[0].AddAddenda(mockAddendaReturn()) + entry := mockEntryDetail() + entry.AddAddenda(mockAddendaReturn()) + mockBatch.AddEntry(entry) + if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) } From 0647b4bf425134f4e1e6fb125c2a7a6dc18a47c8 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 20 Nov 2017 12:55:54 -0700 Subject: [PATCH 0041/1694] Add BatchParam into its own file rather than batcher.go --- batchParam.go | 25 +++++++++++++++++++++++++ batch_test.go | 1 - batcher.go | 24 ------------------------ 3 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 batchParam.go diff --git a/batchParam.go b/batchParam.go new file mode 100644 index 000000000..86ce7563b --- /dev/null +++ b/batchParam.go @@ -0,0 +1,25 @@ +package ach + +// BatchParam contains information about the company(Originator) and the type of detail records to follow. +// It is a subset of BatchHeader used for simplifying the client api build process. +type BatchParam struct { + // ServiceClassCode a three digit code identifies: + // - 200 mixed debits and credits + // - 220 credits only + // - 225 debits only + ServiceClassCode string `json:"service_class_code"` + // CompanyName is the legal company name making the transaction. + CompanyName string `json:"company_name"` + // CompanyIdentification is assigned by your bank to identify your company. Frequently the federal tax ID + CompanyIdentification string `json:"company_identification"` + // StandardEntryClass identifies the payment type (product) found within the batch using a 3-character code + StandardEntryClass string `json:"standard_entry_class"` + // CompanyEntryDescription describes the transaction. For example "PAYROLL" + CompanyEntryDescription string `json:"company_entry_description"` + // CompanyDescriptiveDate a date chosen to identify the transactions in YYMMDD format. + CompanyDescriptiveDate string `json:"company_descriptive_date"` + // Date transactions are to be posted to the receivers’ account in YYMMDD format. + EffectiveEntryDate string `json:"effective_entry_date"` + // ODFIIdentification originating ODFI's routing number without the last digit + ODFIIdentification string `json:"ODFI_identification"` +} diff --git a/batch_test.go b/batch_test.go index ea9169969..874b03623 100644 --- a/batch_test.go +++ b/batch_test.go @@ -247,7 +247,6 @@ func TestNewBatchDefault(t *testing.T) { func TestBatchCategory(t *testing.T) { mockBatch := mockBatch() - // mockBatch.GetEntries()[0] = nil // Add a Addenda Return to the mock batch entry := mockEntryDetail() entry.AddAddenda(mockAddendaReturn()) diff --git a/batcher.go b/batcher.go index 49abe9566..1ab88eff2 100644 --- a/batcher.go +++ b/batcher.go @@ -36,30 +36,6 @@ func (e *BatchError) Error() string { return fmt.Sprintf("BatchNumber %d %s %s", e.BatchNumber, e.FieldName, e.Msg) } -// BatchParam contains information about the company(Originator) and the type of detail records to follow. -// It is a subset of BatchHeader used for simplifying the client api build process. -type BatchParam struct { - // ServiceClassCode a three digit code identifies: - // - 200 mixed debits and credits - // - 220 credits only - // - 225 debits only - ServiceClassCode string `json:"service_class_code"` - // CompanyName is the legal company name making the transaction. - CompanyName string `json:"company_name"` - // CompanyIdentification is assigned by your bank to identify your company. Frequently the federal tax ID - CompanyIdentification string `json:"company_identification"` - // StandardEntryClass identifies the payment type (product) found within the batch using a 3-character code - StandardEntryClass string `json:"standard_entry_class"` - // CompanyEntryDescription describes the transaction. For example "PAYROLL" - CompanyEntryDescription string `json:"company_entry_description"` - // CompanyDescriptiveDate a date chosen to identify the transactions in YYMMDD format. - CompanyDescriptiveDate string `json:"company_descriptive_date"` - // Date transactions are to be posted to the receivers’ account in YYMMDD format. - EffectiveEntryDate string `json:"effective_entry_date"` - // ODFIIdentification originating ODFI's routing number without the last digit - ODFIIdentification string `json:"ODFI_identification"` -} - // Errors specific to parsing a Batch container var ( // generic messages From cdbc94ac886d3dfd73272c8f9b68eb020d2b7011 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 20 Nov 2017 13:12:02 -0700 Subject: [PATCH 0042/1694] Modify NewBatch to utilize batchheader.BatchParam --- reader.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/reader.go b/reader.go index 9c8b9c0b1..af675dc78 100644 --- a/reader.go +++ b/reader.go @@ -195,8 +195,7 @@ func (r *Reader) parseBatchHeader() error { } // Passing SEC type into NewBatch creates a Batcher of SEC code type. - batch, err := NewBatch(BatchParam{ - StandardEntryClass: bh.StandardEntryClassCode}) + batch, err := NewBatch(bh.BatchParam()) if err != nil { return r.error(err) } From 46283b2788b78e3d97ab2b5c1288beb1ac5ed10c Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 20 Nov 2017 15:54:29 -0700 Subject: [PATCH 0043/1694] Entry trace numbers need to not be overwritten when a batch is built if they exist Closes #149 --- batch.go | 14 ++++++++++++-- batchCCD_test.go | 2 +- batchCOR_test.go | 2 +- batchWEB_test.go | 2 +- batch_test.go | 33 +++++++++++++++++++++++---------- entryDetail.go | 2 +- entryDetail_internal_test.go | 6 +++--- recordParams_test.go | 2 +- 8 files changed, 43 insertions(+), 20 deletions(-) diff --git a/batch.go b/batch.go index 32115ba4b..f486203d6 100644 --- a/batch.go +++ b/batch.go @@ -1,6 +1,9 @@ package ach -import "fmt" +import ( + "fmt" + "strconv" +) // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries type batch struct { @@ -112,7 +115,14 @@ func (batch *batch) build() error { seq := 1 for i, entry := range batch.entries { entryCount = entryCount + 1 + len(entry.Addendum) - batch.entries[i].setTraceNumber(batch.header.ODFIIdentification, seq) + currentTraceNumberODFI, err := strconv.Atoi(entry.TraceNumberField()[:8]) + if err != nil { + return err + } + // Add a sequenced TraceNumber if one is not already set. Have to keep original trance number Return and NOC entries + if currentTraceNumberODFI != batch.header.ODFIIdentification { + batch.entries[i].setTraceNumber(batch.header.ODFIIdentification, seq) + } seq++ addendaSeq := 1 for x := range entry.Addendum { diff --git a/batchCCD_test.go b/batchCCD_test.go index ea63bdf20..d28bbc4b2 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -23,7 +23,7 @@ func mockCCDEntryDetail() *EntryDetail { entry.Amount = 5000000 entry.IdentificationNumber = "location #23" entry.SetReceivingCompany("Best Co. #23") - entry.TraceNumber = 123456789 + entry.setTraceNumber(6200001, 123) entry.DiscretionaryData = "S" return entry } diff --git a/batchCOR_test.go b/batchCOR_test.go index 504325b15..517e253de 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -25,7 +25,7 @@ func mockCOREntryDetail() *EntryDetail { entry.Amount = 0 entry.IdentificationNumber = "location #23" entry.SetReceivingCompany("Best Co. #23") - entry.TraceNumber = 123456789 + entry.setTraceNumber(6200001, 0) entry.DiscretionaryData = "S" return entry } diff --git a/batchWEB_test.go b/batchWEB_test.go index 00a49587c..3692a7baa 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -20,7 +20,7 @@ func mockWEBEntryDetail() *EntryDetail { entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" - entry.TraceNumber = 123456789 + entry.setTraceNumber(6200001, 1) entry.SetPaymentType("S") return entry } diff --git a/batch_test.go b/batch_test.go index 874b03623..40608765c 100644 --- a/batch_test.go +++ b/batch_test.go @@ -34,13 +34,13 @@ func TestBatchNumberMismatch(t *testing.T) { func TestCreditBatchisBatchAmount(t *testing.T) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) - e1 := mockEntryDetail() + e1 := mockBatch.GetEntries()[0] e1.TransactionCode = 22 e1.Amount = 100 e2 := mockEntryDetail() e2.TransactionCode = 22 e2.Amount = 100 - mockBatch.AddEntry(e1) + e2.TraceNumber = e1.TraceNumber + 10 mockBatch.AddEntry(e2) if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) @@ -61,13 +61,14 @@ func TestCreditBatchisBatchAmount(t *testing.T) { func TestSavingsBatchisBatchAmount(t *testing.T) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) - e1 := mockEntryDetail() + e1 := mockBatch.GetEntries()[0] e1.TransactionCode = 32 e1.Amount = 100 e2 := mockEntryDetail() e2.TransactionCode = 37 e2.Amount = 100 - mockBatch.AddEntry(e1) + e2.TraceNumber = e1.TraceNumber + 10 + mockBatch.AddEntry(e2) if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) @@ -102,10 +103,9 @@ func TestBatchisEntryHash(t *testing.T) { func TestBatchDNEMismatch(t *testing.T) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) - ed := mockEntryDetail() + ed := mockBatch.GetEntries()[0] ed.AddAddenda(mockAddenda()) ed.AddAddenda(mockAddenda()) - mockBatch.AddEntry(ed) mockBatch.build() mockBatch.GetHeader().OriginatorStatusCode = 1 @@ -176,14 +176,13 @@ func TestBatchAddendaIndicator(t *testing.T) { func TestBatchIsAddendaSeqAscending(t *testing.T) { mockBatch := mockBatch() - ed := mockEntryDetail() + ed := mockBatch.GetEntries()[0] ed.AddAddenda(mockAddenda()) ed.AddAddenda(mockAddenda()) - mockBatch.AddEntry(ed) mockBatch.build() - mockBatch.GetEntries()[1].Addendum[0].(*Addenda).SequenceNumber = 2 - mockBatch.GetEntries()[1].Addendum[1].(*Addenda).SequenceNumber = 1 + mockBatch.GetEntries()[0].Addendum[0].(*Addenda).SequenceNumber = 2 + mockBatch.GetEntries()[0].Addendum[1].(*Addenda).SequenceNumber = 1 if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "SequenceNumber" { @@ -266,6 +265,7 @@ func TestBatchCategoryForwardReturn(t *testing.T) { // Add a Addenda Return to the mock batch entry := mockEntryDetail() entry.AddAddenda(mockAddendaReturn()) + entry.TraceNumber = entry.TraceNumber + 10 mockBatch.AddEntry(entry) if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) @@ -280,3 +280,16 @@ func TestBatchCategoryForwardReturn(t *testing.T) { } } } + +// Don't over write a batch trace number when building if it already exists +func TestBatchTraceNumberExists(t *testing.T) { + mockBatch := mockBatch() + entry := mockEntryDetail() + traceBefore := entry.TraceNumberField() + mockBatch.AddEntry(entry) + mockBatch.build() + traceAfter := mockBatch.GetEntries()[1].TraceNumberField() + if traceBefore != traceAfter { + t.Errorf("Trace number was set to %v before batch.build and is now %v\n", traceBefore, traceAfter) + } +} diff --git a/entryDetail.go b/entryDetail.go index e96a1befb..cd288b913 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -156,7 +156,7 @@ func (ed *EntryDetail) Parse(record string) { ed.DiscretionaryData = record[76:78] // 79-79 1 if addenda exists 0 if it does not ed.AddendaRecordIndicator = ed.parseNumField(record[78:79]) - // 80-84 An internal identification (alphanumeric) that you use to uniquely identify + // 80-94 An internal identification (alphanumeric) that you use to uniquely identify // this Entry Detail Record This number should be unique to the transaction and will help identify the transaction in case of an inquiry ed.TraceNumber = ed.parseNumField(record[79:94]) } diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index 4d4ce1ce9..e542f6cb2 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -16,7 +16,7 @@ func mockEntryDetail() *EntryDetail { entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" - entry.TraceNumber = 123456789 + entry.setTraceNumber(6200001, 1) entry.Category = CategoryForward return entry } @@ -38,8 +38,8 @@ func TestMockEntryDetail(t *testing.T) { if entry.IndividualName != "Wade Arnold" { t.Error("IndividualName dependent default value has changed") } - if entry.TraceNumber != 123456789 { - t.Error("TraceNumber dependent default value has changed") + if entry.TraceNumber != 62000010000001 { + t.Errorf("TraceNumber dependent default value has changed %v", entry.TraceNumber) } } diff --git a/recordParams_test.go b/recordParams_test.go index c5c23d155..bd6d1bc28 100644 --- a/recordParams_test.go +++ b/recordParams_test.go @@ -110,7 +110,7 @@ func TestBuildFileParam(t *testing.T) { CompanyIdentification: "123456789", CompanyEntryDescription: "Trans. Description", CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) + ODFIIdentification: "12345678"}) // To create an entry entry := NewEntryDetail(EntryParam{ From 6ca958d3dd9141474df8f3a6227b2c149ffceb7c Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 4 Dec 2017 17:33:52 -0700 Subject: [PATCH 0044/1694] Add all valid Transaction Type As defined by NACHA Corporate Rules in Appendix 3 - ACH Record Format Specifications --- validators.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/validators.go b/validators.go index a5a167c69..fd146e21e 100644 --- a/validators.go +++ b/validators.go @@ -98,8 +98,9 @@ func (v *validator) isTransactionCode(code int) error { switch code { // TransactionCode if the receivers account is: case - // Automated Return or Notification of Change for - // original transaction code '22', '23, '24' + // Demand Credit Records (for checking, NOW, and share draft accounts) + + // Automated Return or Notification of Change for original transaction code '22', '23, '24' 21, // Credit (deposit) to checking account ‘22’ 22, @@ -107,25 +108,98 @@ func (v *validator) isTransactionCode(code int) error { 23, // Zero dollar with remittance data (CCD/CTX only) 24, + + // Demand Debit Records (for checking, NOW, and share draft accounts) + // Automated Return or Notification of Change for original transaction code 27, 28, or 29 26, // Debit (withdrawal) to checking account ‘27’ 27, // Prenote for debit to checking account ‘28’ 28, + // Zero dollar with remittance data (for CCD, CTX, and IAT Entries only) + 29, + + // Savings Account Credit Records + + // Return or Notification of Change for original transaction code 32, 33, or 34 + 31, // Credit to savings account ‘32’ 32, // Prenote for credit to savings account ‘33’ 33, - // Autmoated Return or Notification of Change for - // original transaction code '37', '38', '39 + // Zero dollar with remittance data (for CCD, CTX, and IAT Entries only); Acknowledgment Entries (ACK and ATX Entries only) + 34, + + // Savings Account Debit Records + + // Automated Return or Notification of Change for original transaction code '37', '38', '39 36, // Debit to savings account ‘37’ 37, // Prenote for debit to savings account ‘38’ 38, // Zero dollar with remittance data (CCD/CTX only) - 39: + 39, + + // Financial Institution General Ledger Credit Records + + //Return or Notification of Change for original transaction code 42, 43, or 44 + 41, + // General Ledger Credit + 42, + // Prenotification of General Ledger Credit (non-dollar) + 43, + // Zero dollar with remittance data (for CCD and CTX Entries only) + 44, + + // Financial Institution General Ledger Debit Records + + // Return or Notification of Change for original transaction code 47, 48, or 49 + 46, + //General Ledger Debit + 47, + // Prenotification of General Ledger Debit (non-dollar) + 48, + // Zero dollar with remittance data (for CCD and CTX only) + 49, + + // Loan Account Credit Records + // Return or Notification of Change for original transaction code 52, 53, or 54 + 51, + // Loan Account Credit + 52, + // Prenotification of Loan Account Credit (non-dollar) + 53, + // Zero dollar with remittance data (for CCD and CTX Entries only) + 54, + + // Loan Account Debit Records (for Reversals Only) + + // Loan Account Debit (Reversals Only) + 55, + // Return or Notification of Change for original transaction code 55 + 56, + + // Accounting Records (for use in ADV Files only) + // These transaction codes represent accounting Entries. + + // Credit for ACH debits originated + 81, + //Debit for ACH credits originated + 82, + // Credit for ACH credits received + 83, + // Debit for ACH debits received + 84, + // Credit for ACH credits in Rejected batches + 85, + // Debit for ACH debits in Rejected batches + 86, + // Summary credit for respondent ACH activity + 87, + // Summary debit for respondent ACH activity + 88: return nil } return errors.New(msgTransactionCode) From bebd34b8a3b05bcefe5d9acf27314aef6e86197d Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 31 Jan 2018 10:26:56 -0700 Subject: [PATCH 0045/1694] Add support for SEC type TEL w/ BatchTEL (#156) * Create support for BatchTEL SEC type Closes #155 --- batch.go | 17 +++++ batchCCD.go | 2 +- batchTEL.go | 72 ++++++++++++++++++++ batchTEL_test.go | 123 +++++++++++++++++++++++++++++++++++ batchWeb.go | 14 ---- entryDetail.go | 13 ++++ entryDetail_internal_test.go | 12 ++++ validators.go | 44 +++++++------ 8 files changed, 263 insertions(+), 34 deletions(-) create mode 100644 batchTEL.go create mode 100644 batchTEL_test.go diff --git a/batch.go b/batch.go index f486203d6..fa59d57b7 100644 --- a/batch.go +++ b/batch.go @@ -3,6 +3,7 @@ package ach import ( "fmt" "strconv" + "strings" ) // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries @@ -28,6 +29,8 @@ func NewBatch(bp BatchParam) (Batcher, error) { return NewBatchCCD(bp), nil case "COR": return NewBatchCOR(bp), nil + case "TEL": + return NewBatchTEL(bp), nil default: } msg := fmt.Sprintf(msgFileNoneSEC, bp.StandardEntryClass) @@ -383,3 +386,17 @@ func (batch *batch) isCategory() error { } return nil } + +// isPaymentTypeCode checks that the Entry detail records have either: +// "R" For a recurring WEB Entry +// "S" For a Single-Entry WEB Entry +func (batch *batch) isPaymentTypeCode() error { + for _, entry := range batch.entries { + if !strings.Contains(strings.ToUpper(entry.PaymentTypeField()), "S") && !strings.Contains(strings.ToUpper(entry.PaymentTypeField()), "R") { + // TODO dead code because PaymentTypeField always returns S regardless of Discretionary Data value + msg := fmt.Sprintf(msgBatchWebPaymentType, entry.PaymentTypeField()) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "PaymentType", Msg: msg} + } + } + return nil +} diff --git a/batchCCD.go b/batchCCD.go index 26a4f2e9c..0e86a7551 100644 --- a/batchCCD.go +++ b/batchCCD.go @@ -4,7 +4,7 @@ import ( "fmt" ) -// BatchCCD creates a batch file that handles SEC payment type CCD amd CCD+. +// BatchCCD is a batch file that handles SEC payment type CCD amd CCD+. // Corporate credit or debit. Identifies an Entry initiated by an Organization to transfer funds to or from an account of that Organization or another Organization. // For commercial accounts only. type BatchCCD struct { diff --git a/batchTEL.go b/batchTEL.go new file mode 100644 index 000000000..6442f38b7 --- /dev/null +++ b/batchTEL.go @@ -0,0 +1,72 @@ +package ach + +import "fmt" + +// BatchTEL is a batch that handles SEC payment type Telephone-Initiated Entries (TEL) +// Telephone-Initiated Entries (TEL) are consumer debit transactions. The NACHA Operating Rules permit TEL entries when the Originator obtains the Receiver’s authorization for the debit entry orally via the telephone. +// An entry based upon a Receiver’s oral authorization must utilize the TEL (Telephone-Initiated Entry) Standard Entry Class (SEC) Code. +type BatchTEL struct { + batch +} + +// NewBatchTEL returns a *BatchTEL +func NewBatchTEL(params ...BatchParam) *BatchTEL { + batch := new(BatchTEL) + batch.SetControl(NewBatchControl()) + + if len(params) > 0 { + bh := NewBatchHeader(params[0]) + bh.StandardEntryClassCode = "TEL" + batch.SetHeader(bh) + return batch + } + bh := NewBatchHeader() + bh.StandardEntryClassCode = "TEL" + batch.SetHeader(bh) + return batch +} + +// Validate ensures the batch meets NACHA rules specific to the SEC type TEL +func (batch *BatchTEL) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + // TEL can not have an addenda + if err := batch.isAddendaCount(0); err != nil { + return err + } + + // Add type specific validation. + if batch.header.StandardEntryClassCode != "TEL" { + msg := fmt.Sprintf(msgBatchSECType, batch.header.StandardEntryClassCode, "TEL") + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + // can not have credits in TEL batches + for _, entry := range batch.entries { + if entry.CreditOrDebit() != "D" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.IndividualName) + return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + } + + if err := batch.isPaymentTypeCode(); err != nil { + return err + } + + return nil +} + +// Create builds the batch sequence numbers and batch control. Additional creation +func (batch *BatchTEL) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + + if err := batch.Validate(); err != nil { + return err + } + return nil +} diff --git a/batchTEL_test.go b/batchTEL_test.go new file mode 100644 index 000000000..5f4edc247 --- /dev/null +++ b/batchTEL_test.go @@ -0,0 +1,123 @@ +package ach + +import ( + "testing" +) + +func mockBatchTELHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "TEL" + bh.CompanyName = "Your Company, inc" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "Vndr Pay" + bh.ODFIIdentification = 6200001 + return bh +} + +func mockTELEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI(9101298) + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 5000000 + entry.IdentificationNumber = "Phone 333-2222" + entry.IndividualName = "Wade Arnold" + entry.setTraceNumber(6200001, 123) + entry.SetPaymentType("S") + return entry +} + +func mockBatchTEL() *BatchTEL { + mockBatch := NewBatchTEL() + mockBatch.SetHeader(mockBatchTELHeader()) + mockBatch.AddEntry(mockTELEntryDetail()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +func TestBatchTELParam(t *testing.T) { + batch, _ := NewBatch(mockBatchTELHeader().BatchParam()) + err, ok := batch.(*BatchTEL) + if !ok { + t.Errorf("Expecting BatchTEL got %T", err) + } +} + +func TestBatchTELCreate(t *testing.T) { + mockBatch := mockBatchTEL() + // Batch Header information is required to Create a batch. + mockBatch.GetHeader().ServiceClassCode = 0 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchTELAddendaCount(t *testing.T) { + mockBatch := mockBatchTEL() + // TEL can not have an addendum + mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchTELSEC(t *testing.T) { + mockBatch := mockBatchTEL() + mockBatch.header.StandardEntryClassCode = "RCK" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestBatchTELDebit(t *testing.T) { + mockBatch := mockBatchTEL() + mockBatch.GetEntries()[0].TransactionCode = 22 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// verify that the entry detail payment type / discretionary data is either single or reoccurring for the +func TestBatchTELPaymentType(t *testing.T) { + mockBatch := mockBatchTEL() + mockBatch.GetEntries()[0].DiscretionaryData = "AA" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + println(e.Error()) + if e.FieldName != "PaymentType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} diff --git a/batchWeb.go b/batchWeb.go index 89a70aab3..571fec959 100644 --- a/batchWeb.go +++ b/batchWeb.go @@ -2,7 +2,6 @@ package ach import ( "fmt" - "strings" ) // BatchWEB creates a batch file that handles SEC payment type WEB. @@ -73,16 +72,3 @@ func (batch *BatchWEB) Create() error { } return nil } - -// isPaymentTypeCode checks that the Entry detail records have either: -// "R" For a recurring WEB Entry -// "S" For a Single-Entry WEB Entry -func (batch *BatchWEB) isPaymentTypeCode() error { - for _, entry := range batch.entries { - if !strings.Contains(strings.ToUpper(entry.PaymentTypeField()), "S") && !strings.Contains(strings.ToUpper(entry.PaymentTypeField()), "R") { - msg := fmt.Sprintf(msgBatchWebPaymentType, entry.PaymentTypeField()) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "PaymentType", Msg: msg} - } - } - return nil -} diff --git a/entryDetail.go b/entryDetail.go index cd288b913..edff73b49 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -331,3 +331,16 @@ func (ed *EntryDetail) SetPaymentType(t string) { func (ed *EntryDetail) TraceNumberField() string { return ed.numericField(ed.TraceNumber, 15) } + +// CreditOrDebit returns a "C" for credit or "D" for debit based on the entry TransactionCode +func (ed *EntryDetail) CreditOrDebit() string { + tc := strconv.Itoa(ed.TransactionCode) + // take the second number in the Transaction code + switch tc[1:2] { + case "1", "2", "3": + return "C" + case "6", "7", "8": + return "D" + } + return "" +} diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index e542f6cb2..e4afd1c13 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -313,3 +313,15 @@ func TestEDAddAddendaAddendaReturnTwice(t *testing.T) { t.Error("AddendaReturn added and isReturn is false") } } + +func TestEDCreditOrDebit(t *testing.T) { + // TODO add more credit and debit transaction code's to this test + entry := mockEntryDetail() + if entry.CreditOrDebit() != "C" { + t.Errorf("TransactionCode %v expected a Credit(C) got %v", entry.TransactionCode, entry.CreditOrDebit()) + } + entry.TransactionCode = 27 + if entry.CreditOrDebit() != "D" { + t.Errorf("TransactionCode %v expected a Debit(D) got %v", entry.TransactionCode, entry.CreditOrDebit()) + } +} diff --git a/validators.go b/validators.go index fd146e21e..d7f71287a 100644 --- a/validators.go +++ b/validators.go @@ -44,7 +44,7 @@ var ( msgValidCheckDigit = "does not match calculated check digit %d" ) -// iServiceClass returns true if a valid service class code +// iServiceClass returns true if a valid service class code of a batch is found func (v *validator) isServiceClass(code int) error { switch code { case @@ -61,7 +61,7 @@ func (v *validator) isServiceClass(code int) error { return errors.New(msgServiceClass) } -// isSECCode returns true if a SEC Code is found +// isSECCode returns true if a SEC Code of a Batch is found func (v *validator) isSECCode(code string) error { switch code { case @@ -72,7 +72,9 @@ func (v *validator) isSECCode(code string) error { return errors.New(msgSECCode) } -// isTypeCode returns true if a valid type code is found +// isTypeCode returns true if a valid type code of an Addendum is found +// +// The Addenda Type Code defines the specific interpretation and format for the addenda information contained in the Entry. func (v *validator) isTypeCode(code string) error { switch code { case @@ -93,7 +95,24 @@ func (v *validator) isTypeCode(code string) error { return errors.New(msgAddendaTypeCode) } -// isTransactionCode ensures TransactionCode code is valid +// isTransactionCode ensures TransactionCode of an Entry is valid +// +// The Tran Code is a two-digit code in positions 2 - 3 of the Entry Detail Record (6 Record) within an ACH File. +// The first digit of the Tran Code indicates the account type to which the entry will post, where the number: +// "2"designates a Checking Account. +// "3"designates a Savings Account. +// "4"designates a General Ledger Account. +// "5"designates Loan Account. +//The second digit of the Tran Code identifies the entry as: +// an original forward entry, where the number: +// "2"designates a credit. or +// "7"designates a debit. +// a return or NOC, where the number: +// "1"designates the return/NOC of a credit, or +// "6"designates a return/NOC of a debit. +// a pre-note or non-monetary informational transaction, where the number: +// "3"designates a credit, or +// "8"designates a debit. func (v *validator) isTransactionCode(code int) error { switch code { // TransactionCode if the receivers account is: @@ -205,7 +224,7 @@ func (v *validator) isTransactionCode(code int) error { return errors.New(msgTransactionCode) } -// isOriginatorStatusCode ensures status code is valid +// isOriginatorStatusCode ensures status code of a batch is valid func (v *validator) isOriginatorStatusCode(code int) error { switch code { case @@ -237,20 +256,7 @@ func (v *validator) isAlphanumeric(s string) error { return nil } -// isOriginatorStatusCode ensures status code is valid -/* -func (v *validator) isCheckDigit(routingNumber string, checkDigit int) error { - calculated := v.CalculateCheckDigit(routingNumber) - if calculated != checkDigit { - msg := fmt.Sprintf(msgValidCheckDigit, calculated) - return &FieldError{FieldName: "RoutingNumber", Value: routingNumber, Msg: msg} - //return msgValidCheckDigit - } - return nil -} -*/ - -// CalculateCheckDigit returns a check digit for a rounting number +// CalculateCheckDigit returns a check digit for a routing number // Multiply each digit in the Routing number by a weighting factor. The weighting factors for each digit are: // Position: 1 2 3 4 5 6 7 8 // Weights : 3 7 1 3 7 1 3 7 From b2a3160a5d14fe38d595afa96f721f7fb13259bf Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 31 Jan 2018 10:55:19 -0700 Subject: [PATCH 0046/1694] Adding TEL support to readme documentation --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a682a2147..e0023df78 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,13 @@ Package 'moov-io/ach' implements a file reader and writer for parsing [ACH](http ## Project Status -ACH is at an early stage and under active development but is already in product for multiple companies. Please star the project if you are interested in its progress. +ACH is under active development but already in production for multiple companies. Please star the project if you are interested in its progress. * Library currently supports the reading and writing * PPD (Prearranged payment and deposits) * WEB (Internet-initiated Entries ) * CCD (Corporate credit or debit) + * TEL (Telephone-Initiated Entry) * COR (Automated Notification of Change(NOC)) * Return Entires From 326785cbb79c901ff2f0a11ed46a578e019cc82e Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 6 Apr 2018 15:13:52 -0600 Subject: [PATCH 0047/1694] Record Length of instantiated Batch Control should be 94 Closes #158 --- batchControl.go | 7 ++++--- batchControl_internal_test.go | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/batchControl.go b/batchControl.go index 3fed720b1..74a016f73 100644 --- a/batchControl.go +++ b/batchControl.go @@ -98,9 +98,10 @@ func (bc *BatchControl) Parse(record string) { // NewBatchControl returns a new BatchControl with default values for none exported fields func NewBatchControl() *BatchControl { return &BatchControl{ - recordType: "8", - EntryHash: 1, - BatchNumber: 1, + recordType: "8", + ServiceClassCode: 200, + EntryHash: 1, + BatchNumber: 1, } } diff --git a/batchControl_internal_test.go b/batchControl_internal_test.go index 200e51693..389b527d9 100644 --- a/batchControl_internal_test.go +++ b/batchControl_internal_test.go @@ -205,3 +205,11 @@ func TestBCFieldInclusionODFIIdentification(t *testing.T) { } } } + +func TestBatchControlLength(t *testing.T) { + bc := NewBatchControl() + recordLength := len(bc.String()) + if recordLength != 94 { + t.Errorf("Instantiated length of Batch Control string is not 94 but %v", recordLength) + } +} From e55876e21a4725d2d2814379d1bd5e24238ed951 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 9 Apr 2018 09:33:26 -0600 Subject: [PATCH 0048/1694] Removing BatchParam (#161) --- addenda_internal_test.go | 6 ++--- batch.go | 18 ++++++------- batchCCD.go | 11 +------- batchCCD_test.go | 31 ++++++++------------- batchCOR.go | 11 +------- batchCOR_test.go | 36 +++++++++---------------- batchControl_internal_test.go | 6 ++--- batchHeader.go | 33 +++-------------------- batchHeader_internal_test.go | 27 ------------------- batchPPD.go | 11 +------- batchPPD_test.go | 37 ++++++++++++++++++++----- batchParam.go | 25 ----------------- batchTEL.go | 11 +------- batchTEL_test.go | 7 +++-- batchWEB_test.go | 4 +-- batchWeb.go | 11 +------- batch_test.go | 20 ++++++++++++-- entryDetail_internal_test.go | 4 +-- example/ach-ppd-write/main.go | 3 +-- example/simple-file-creation/main.go | 40 +++++++++++++++++----------- file_test.go | 8 +++--- reader.go | 3 +-- reader_test.go | 8 +++--- recordParams_test.go | 40 ++++++++-------------------- writer_test.go | 2 +- 25 files changed, 150 insertions(+), 263 deletions(-) delete mode 100644 batchParam.go diff --git a/addenda_internal_test.go b/addenda_internal_test.go index ac5f9437d..2daa80517 100644 --- a/addenda_internal_test.go +++ b/addenda_internal_test.go @@ -33,8 +33,8 @@ func TestParseAddenda(t *testing.T) { var line = "705WEB DIEGO MAY 00010000001" r := NewReader(strings.NewReader(line)) - r.addCurrentBatch(NewBatchPPD()) - r.currentBatch.GetHeader().StandardEntryClassCode = "PPD" + r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) + //r.currentBatch.GetHeader().StandardEntryClassCode = "PPD" r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 22, AddendaRecordIndicator: 1}) r.line = line err := r.parseAddenda() @@ -64,7 +64,7 @@ func TestParseAddenda(t *testing.T) { func TestAddendaString(t *testing.T) { var line = "705WEB DIEGO MAY 00010000001" r := NewReader(strings.NewReader(line)) - r.addCurrentBatch(NewBatchPPD()) + r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) r.currentBatch.GetHeader().StandardEntryClassCode = "PPD" r.currentBatch.AddEntry(&EntryDetail{AddendaRecordIndicator: 1}) r.line = line diff --git a/batch.go b/batch.go index fa59d57b7..dae70d3a7 100644 --- a/batch.go +++ b/batch.go @@ -18,22 +18,22 @@ type batch struct { converters } -// NewBatch takes a BatchParm and returns a matching SEC code batch type that is a batcher. Returns and error if the SEC code is not supported. -func NewBatch(bp BatchParam) (Batcher, error) { - switch sec := bp.StandardEntryClass; sec { +// NewBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported. +func NewBatch(bh *BatchHeader) (Batcher, error) { + switch bh.StandardEntryClassCode { case "PPD": - return NewBatchPPD(bp), nil + return NewBatchPPD(bh), nil case "WEB": - return NewBatchWEB(bp), nil + return NewBatchWEB(bh), nil case "CCD": - return NewBatchCCD(bp), nil + return NewBatchCCD(bh), nil case "COR": - return NewBatchCOR(bp), nil + return NewBatchCOR(bh), nil case "TEL": - return NewBatchTEL(bp), nil + return NewBatchTEL(bh), nil default: } - msg := fmt.Sprintf(msgFileNoneSEC, bp.StandardEntryClass) + msg := fmt.Sprintf(msgFileNoneSEC, bh.StandardEntryClassCode) return nil, &FileError{FieldName: "StandardEntryClassCode", Msg: msg} } diff --git a/batchCCD.go b/batchCCD.go index 0e86a7551..6c4486469 100644 --- a/batchCCD.go +++ b/batchCCD.go @@ -12,18 +12,9 @@ type BatchCCD struct { } // NewBatchCCD returns a *BatchCCD -func NewBatchCCD(params ...BatchParam) *BatchCCD { +func NewBatchCCD(bh *BatchHeader) *BatchCCD { batch := new(BatchCCD) batch.SetControl(NewBatchControl()) - - if len(params) > 0 { - bh := NewBatchHeader(params[0]) - bh.StandardEntryClassCode = "CCD" - batch.SetHeader(bh) - return batch - } - bh := NewBatchHeader() - bh.StandardEntryClassCode = "CCD" batch.SetHeader(bh) return batch } diff --git a/batchCCD_test.go b/batchCCD_test.go index d28bbc4b2..e71bb5f26 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -29,8 +29,7 @@ func mockCCDEntryDetail() *EntryDetail { } func mockBatchCCD() *BatchCCD { - mockBatch := NewBatchCCD() - mockBatch.SetHeader(mockBatchCCDHeader()) + mockBatch := NewBatchCCD(mockBatchCCDHeader()) mockBatch.AddEntry(mockCCDEntryDetail()) mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) if err := mockBatch.Create(); err != nil { @@ -39,6 +38,16 @@ func mockBatchCCD() *BatchCCD { return mockBatch } +// Test Batch Header +func TestBatchCCDHeader(t *testing.T) { + batch, _ := NewBatch(mockBatchCCDHeader()) + + _, ok := batch.(*BatchCCD) + if !ok { + t.Error("Expecting BatchCCD") + } +} + // A Batch CCD can only have one addendum per entry detail func TestBatchCCDAddendumCount(t *testing.T) { mockBatch := mockBatchCCD() @@ -131,21 +140,3 @@ func TestBatchCCDCreate(t *testing.T) { } } } - -func TestBatchCCDParam(t *testing.T) { - - batch, _ := NewBatch(BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company, inc", - StandardEntryClass: "CCD", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Vndr Pay", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) - - _, ok := batch.(*BatchCCD) - if !ok { - t.Error("Expecting BachCCD") - } - -} diff --git a/batchCOR.go b/batchCOR.go index 35a6979a6..b5ce90016 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -16,18 +16,9 @@ var msgBatchCORAddenda = "found and 1 AddendaNOC is required for SEC Type COR" var msgBatchCORAddendaType = "%T found where AddendaNOC is required for SEC type NOC" // NewBatchCOR returns a *BatchCOR -func NewBatchCOR(params ...BatchParam) *BatchCOR { +func NewBatchCOR(bh *BatchHeader) *BatchCOR { batch := new(BatchCOR) batch.SetControl(NewBatchControl()) - - if len(params) > 0 { - bh := NewBatchHeader(params[0]) - bh.StandardEntryClassCode = "COR" - batch.SetHeader(bh) - return batch - } - bh := NewBatchHeader() - bh.StandardEntryClassCode = "COR" batch.SetHeader(bh) return batch } diff --git a/batchCOR_test.go b/batchCOR_test.go index 517e253de..bfd35b49d 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -31,8 +31,7 @@ func mockCOREntryDetail() *EntryDetail { } func mockBatchCOR() *BatchCOR { - mockBatch := NewBatchCOR() - mockBatch.SetHeader(mockBatchCORHeader()) + mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) mockBatch.GetEntries()[0].AddAddenda(mockAddendaNOC()) if err := mockBatch.Create(); err != nil { @@ -41,6 +40,16 @@ func mockBatchCOR() *BatchCOR { return mockBatch } +// Test Batch Header +func TestBatchCORHeader(t *testing.T) { + batch, _ := NewBatch(mockBatchCORHeader()) + + _, ok := batch.(*BatchCOR) + if !ok { + t.Error("Expecting BachCOR") + } +} + func TestBatchCORSEC(t *testing.T) { mockBatch := mockBatchCOR() mockBatch.header.StandardEntryClassCode = "RCK" @@ -55,23 +64,6 @@ func TestBatchCORSEC(t *testing.T) { } } -func TestBatchCORParam(t *testing.T) { - - batch, _ := NewBatch(BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company, inc", - StandardEntryClass: "COR", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Vndr Pay", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) - - _, ok := batch.(*BatchCOR) - if !ok { - t.Error("Expecting BachCOR") - } -} - func TestBatchCORAddendumCountTwo(t *testing.T) { mockBatch := mockBatchCOR() // Adding a second addenda to the mock entry @@ -90,8 +82,7 @@ func TestBatchCORAddendumCountTwo(t *testing.T) { } func TestBatchCORAddendaCountZero(t *testing.T) { - mockBatch := NewBatchCOR() - mockBatch.SetHeader(mockBatchCORHeader()) + mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) if err := mockBatch.Create(); err != nil { if e, ok := err.(*BatchError); ok { @@ -106,8 +97,7 @@ func TestBatchCORAddendaCountZero(t *testing.T) { // check that Addendum is of type AddendaNOC func TestBatchCORAddendaType(t *testing.T) { - mockBatch := NewBatchCOR() - mockBatch.SetHeader(mockBatchCORHeader()) + mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) if err := mockBatch.Create(); err != nil { diff --git a/batchControl_internal_test.go b/batchControl_internal_test.go index 389b527d9..e6fa7cbf7 100644 --- a/batchControl_internal_test.go +++ b/batchControl_internal_test.go @@ -38,12 +38,11 @@ func TestParseBatchControl(t *testing.T) { var line = "82250000010005320001000000010500000000000000origid 076401250000001" r := NewReader(strings.NewReader(line)) r.line = line - r.addCurrentBatch(NewBatchPPD()) bh := BatchHeader{BatchNumber: 1, ServiceClassCode: 225, CompanyIdentification: "origid", ODFIIdentification: 7640125} - r.currentBatch.SetHeader(&bh) + r.addCurrentBatch(NewBatchPPD(&bh)) r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: 5320001, TraceNumber: 76401255655291}) if err := r.parseBatchControl(); err != nil { @@ -91,12 +90,11 @@ func TestBCString(t *testing.T) { var line = "82250000010005320001000000010500000000000000origid 076401250000001" r := NewReader(strings.NewReader(line)) r.line = line - r.addCurrentBatch(NewBatchPPD()) bh := BatchHeader{BatchNumber: 1, ServiceClassCode: 225, CompanyIdentification: "origid", ODFIIdentification: 7640125} - r.currentBatch.SetHeader(&bh) + r.addCurrentBatch(NewBatchPPD(&bh)) r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: 5320001, TraceNumber: 76401255655291}) if err := r.parseBatchControl(); err != nil { diff --git a/batchHeader.go b/batchHeader.go index 8270a2002..a96b0622d 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -98,24 +98,13 @@ type BatchHeader struct { converters } -// NewBatchHeader returns a new BatchHeader with default values for none exported fields -func NewBatchHeader(params ...BatchParam) *BatchHeader { +// NewBatchHeader returns a new BatchHeader with default values for non exported fields +func NewBatchHeader() *BatchHeader { bh := &BatchHeader{ recordType: "5", OriginatorStatusCode: 0, //Prepared by an Originator BatchNumber: 1, } - if len(params) > 0 { - bh.ServiceClassCode = bh.parseNumField(params[0].ServiceClassCode) - bh.CompanyName = params[0].CompanyName - bh.CompanyIdentification = params[0].CompanyIdentification - bh.StandardEntryClassCode = params[0].StandardEntryClass - bh.CompanyEntryDescription = params[0].CompanyEntryDescription - bh.CompanyDescriptiveDate = params[0].CompanyDescriptiveDate - bh.EffectiveEntryDate = bh.parseSimpleDate(params[0].EffectiveEntryDate) - bh.ODFIIdentification = bh.parseNumField(params[0].ODFIIdentification) - return bh - } return bh } @@ -134,7 +123,7 @@ func (bh *BatchHeader) Parse(record string) { bh.CompanyIdentification = strings.TrimSpace(record[40:50]) // 51-53 If the entries are PPD (credits/debits towards consumer account), use "PPD". // If the entries are CCD (credits/debits towards corporate account), use "CCD". - // The difference between the 2 class codes are outside of the scope of this post, but generally most ACH transfers to consumer bank accounts should use "PPD" + // The difference between the 2 SEC codes are outside of the scope of this post. bh.StandardEntryClassCode = record[50:53] // 54-63 Your description of the transaction. This text will appear on the receivers’ bank statement. // For example: "Payroll " @@ -279,18 +268,4 @@ func (bh *BatchHeader) BatchNumberField() string { func (bh *BatchHeader) settlementDateField() string { return bh.alphaField(bh.settlementDate, 3) -} - -// BatchParam returns a BatchParam with the values from BatchHeader -func (bh *BatchHeader) BatchParam() BatchParam { - bp := BatchParam{ - ServiceClassCode: strconv.Itoa(bh.ServiceClassCode), - CompanyName: bh.CompanyNameField(), - CompanyIdentification: bh.CompanyIdentification, - StandardEntryClass: bh.StandardEntryClassCode, - CompanyEntryDescription: bh.CompanyEntryDescription, - CompanyDescriptiveDate: bh.CompanyDescriptiveDateField(), - EffectiveEntryDate: bh.EffectiveEntryDateField(), - ODFIIdentification: bh.ODFIIdentificationField()} - return bp -} +} \ No newline at end of file diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index fd4fa1a0c..cab77c8a9 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -294,30 +294,3 @@ func TestBHFieldInclusionOriginatorStatusCode(t *testing.T) { } } } - -func TestBHtoBatchParam(t *testing.T) { - bh := mockBatchHeader() - bp := bh.BatchParam() - - if bh.ServiceClassCode != bh.parseNumField(bp.ServiceClassCode) { - t.Errorf("ServiceClassCode Expected '%b' got: %s", bh.ServiceClassCode, bp.ServiceClassCode) - } - if bh.CompanyName != bp.CompanyName { - t.Errorf("CompanyName Expected '%s' got: %s", bh.CompanyName, bp.CompanyName) - } - if bh.StandardEntryClassCode != bp.StandardEntryClass { - t.Errorf("StandardEntryClass Expected '%s' got: %s", bh.StandardEntryClassCode, bp.StandardEntryClass) - } - if bh.CompanyIdentification != bp.CompanyIdentification { - t.Errorf("CompanyIdentification Expected '%s' got: %s", bh.CompanyIdentification, bp.CompanyIdentification) - } - if bh.CompanyEntryDescription != bp.CompanyEntryDescription { - t.Errorf("CompanyEntryDescription Expected '%s' got: %s", bh.CompanyEntryDescription, bp.CompanyEntryDescription) - } - if bh.CompanyDescriptiveDateField() != bp.CompanyDescriptiveDate { - t.Errorf("CompanyDescriptiveDate Expected '%s' got: %s", bh.CompanyDescriptiveDateField(), bp.CompanyDescriptiveDate) - } - if bh.ODFIIdentification != bh.parseNumField(bp.ODFIIdentification) { - t.Errorf("ODFIIdentification Expected '%b' got: %s", bh.ODFIIdentification, bp.ODFIIdentification) - } -} diff --git a/batchPPD.go b/batchPPD.go index 852fa6261..539d4ad0c 100644 --- a/batchPPD.go +++ b/batchPPD.go @@ -10,18 +10,9 @@ type BatchPPD struct { } // NewBatchPPD returns a *BatchPPD -func NewBatchPPD(params ...BatchParam) *BatchPPD { +func NewBatchPPD(bh *BatchHeader) *BatchPPD { batch := new(BatchPPD) batch.SetControl(NewBatchControl()) - - if len(params) > 0 { - bh := NewBatchHeader(params[0]) - bh.StandardEntryClassCode = ppd - batch.SetHeader(bh) - return batch - } - bh := NewBatchHeader() - bh.StandardEntryClassCode = ppd batch.SetHeader(bh) return batch } diff --git a/batchPPD_test.go b/batchPPD_test.go index f3e4e3118..510d923d0 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -9,10 +9,35 @@ import ( "time" ) + +func mockBatchPPDHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "PPD" + bh.CompanyName = "ACME Corporation" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "PAYROLL" + bh.EffectiveEntryDate = time.Now() + bh.ODFIIdentification = 6200001 + return bh +} + +func mockPPDEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI(9101298) + entry.DFIAccountNumber = "123456789" + entry.Amount = 100000000 + entry.IndividualName = "Wade Arnold" + entry.setTraceNumber(mockBatchPPDHeader().ODFIIdentification, 1) + //entry.setTraceNumber(6200001, 1) + entry.Category = CategoryForward + return entry +} + func mockBatchPPD() *BatchPPD { - mockBatch := NewBatchPPD() - mockBatch.SetHeader(mockBatchHeader()) - mockBatch.AddEntry(mockEntryDetail()) + mockBatch := NewBatchPPD(mockBatchPPDHeader()) + mockBatch.AddEntry(mockPPDEntryDetail()) if err := mockBatch.Create(); err != nil { panic(err) } @@ -103,7 +128,6 @@ func TestBatchODFIIDMismatch(t *testing.T) { } func TestBatchBuild(t *testing.T) { - mockBatch := NewBatchPPD() header := NewBatchHeader() header.ServiceClassCode = 200 header.CompanyName = "MY BEST COMP." @@ -113,11 +137,12 @@ func TestBatchBuild(t *testing.T) { header.CompanyEntryDescription = "PAYROLL" header.EffectiveEntryDate = time.Now() header.ODFIIdentification = 109991234 - mockBatch.SetHeader(header) + + mockBatch := NewBatchPPD(mockBatchPPDHeader()) entry := NewEntryDetail() entry.TransactionCode = 22 // ACH Credit - entry.SetRDFI(81086674) // scottrade bank routing number + entry.SetRDFI(81086674) // scottrade bank routing number entry.DFIAccountNumber = "62292250" // scottrade account number entry.Amount = 1000000 // 1k dollars entry.IdentificationNumber = "658-888-2468" // Unique ID for payment diff --git a/batchParam.go b/batchParam.go deleted file mode 100644 index 86ce7563b..000000000 --- a/batchParam.go +++ /dev/null @@ -1,25 +0,0 @@ -package ach - -// BatchParam contains information about the company(Originator) and the type of detail records to follow. -// It is a subset of BatchHeader used for simplifying the client api build process. -type BatchParam struct { - // ServiceClassCode a three digit code identifies: - // - 200 mixed debits and credits - // - 220 credits only - // - 225 debits only - ServiceClassCode string `json:"service_class_code"` - // CompanyName is the legal company name making the transaction. - CompanyName string `json:"company_name"` - // CompanyIdentification is assigned by your bank to identify your company. Frequently the federal tax ID - CompanyIdentification string `json:"company_identification"` - // StandardEntryClass identifies the payment type (product) found within the batch using a 3-character code - StandardEntryClass string `json:"standard_entry_class"` - // CompanyEntryDescription describes the transaction. For example "PAYROLL" - CompanyEntryDescription string `json:"company_entry_description"` - // CompanyDescriptiveDate a date chosen to identify the transactions in YYMMDD format. - CompanyDescriptiveDate string `json:"company_descriptive_date"` - // Date transactions are to be posted to the receivers’ account in YYMMDD format. - EffectiveEntryDate string `json:"effective_entry_date"` - // ODFIIdentification originating ODFI's routing number without the last digit - ODFIIdentification string `json:"ODFI_identification"` -} diff --git a/batchTEL.go b/batchTEL.go index 6442f38b7..50f92791c 100644 --- a/batchTEL.go +++ b/batchTEL.go @@ -10,18 +10,9 @@ type BatchTEL struct { } // NewBatchTEL returns a *BatchTEL -func NewBatchTEL(params ...BatchParam) *BatchTEL { +func NewBatchTEL(bh *BatchHeader) *BatchTEL { batch := new(BatchTEL) batch.SetControl(NewBatchControl()) - - if len(params) > 0 { - bh := NewBatchHeader(params[0]) - bh.StandardEntryClassCode = "TEL" - batch.SetHeader(bh) - return batch - } - bh := NewBatchHeader() - bh.StandardEntryClassCode = "TEL" batch.SetHeader(bh) return batch } diff --git a/batchTEL_test.go b/batchTEL_test.go index 5f4edc247..b3a245841 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -29,8 +29,7 @@ func mockTELEntryDetail() *EntryDetail { } func mockBatchTEL() *BatchTEL { - mockBatch := NewBatchTEL() - mockBatch.SetHeader(mockBatchTELHeader()) + mockBatch := NewBatchTEL(mockBatchTELHeader()) mockBatch.AddEntry(mockTELEntryDetail()) if err := mockBatch.Create(); err != nil { panic(err) @@ -38,8 +37,8 @@ func mockBatchTEL() *BatchTEL { return mockBatch } -func TestBatchTELParam(t *testing.T) { - batch, _ := NewBatch(mockBatchTELHeader().BatchParam()) +func TestBatchTELHeader(t *testing.T) { + batch, _ := NewBatch(mockBatchTELHeader()) err, ok := batch.(*BatchTEL) if !ok { t.Errorf("Expecting BatchTEL got %T", err) diff --git a/batchWEB_test.go b/batchWEB_test.go index 3692a7baa..d80ceb0a1 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -26,8 +26,7 @@ func mockWEBEntryDetail() *EntryDetail { } func mockBatchWEB() *BatchWEB { - mockBatch := NewBatchWEB() - mockBatch.SetHeader(mockBatchWEBHeader()) + mockBatch := NewBatchWEB(mockBatchWEBHeader()) mockBatch.AddEntry(mockWEBEntryDetail()) mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) if err := mockBatch.Create(); err != nil { @@ -37,6 +36,7 @@ func mockBatchWEB() *BatchWEB { } // No more than 1 batch per entry detail record can exist +// No more than 1 addenda record per entry detail record can exist func TestBatchWebAddenda(t *testing.T) { mockBatch := mockBatchWEB() // mock batch already has one addenda. Creating two addenda should error diff --git a/batchWeb.go b/batchWeb.go index 571fec959..03cd232c1 100644 --- a/batchWeb.go +++ b/batchWeb.go @@ -16,18 +16,9 @@ var ( ) // NewBatchWEB returns a *BatchWEB -func NewBatchWEB(params ...BatchParam) *BatchWEB { +func NewBatchWEB(bh *BatchHeader) *BatchWEB { batch := new(BatchWEB) batch.SetControl(NewBatchControl()) - - if len(params) > 0 { - bh := NewBatchHeader(params[0]) - bh.StandardEntryClassCode = "WEB" - batch.SetHeader(bh) - return batch - } - bh := NewBatchHeader() - bh.StandardEntryClassCode = "WEB" batch.SetHeader(bh) return batch } diff --git a/batch_test.go b/batch_test.go index 40608765c..4c1372452 100644 --- a/batch_test.go +++ b/batch_test.go @@ -2,6 +2,7 @@ package ach import ( "testing" + "time" ) // batch should never be used directly. @@ -15,6 +16,19 @@ func mockBatch() *batch { return mockBatch } +// Invalid SEC CODE Batch Header +func mockBatchInvalidSECHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "NIL" + bh.CompanyName = "ACME Corporation" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "PAYROLL" + bh.EffectiveEntryDate = time.Now() + bh.ODFIIdentification = 6200001 + return bh +} + // Test cases that apply to all batch types func TestBatchNumberMismatch(t *testing.T) { @@ -231,9 +245,9 @@ func TestBatchAddendaTraceNumber(t *testing.T) { } } + func TestNewBatchDefault(t *testing.T) { - _, err := NewBatch(BatchParam{ - StandardEntryClass: "NIL"}) + _, err := NewBatch(mockBatchInvalidSECHeader()) if e, ok := err.(*FileError); ok { if e.FieldName != "StandardEntryClassCode" { @@ -244,6 +258,8 @@ func TestNewBatchDefault(t *testing.T) { } } + + func TestBatchCategory(t *testing.T) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index e4afd1c13..600a21ad8 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -47,7 +47,7 @@ func TestMockEntryDetail(t *testing.T) { func TestParseEntryDetail(t *testing.T) { var line = "62705320001912345 0000010500c-1 Arnold Wade DD0076401255655291" r := NewReader(strings.NewReader(line)) - r.addCurrentBatch(NewBatchPPD()) + r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) r.currentBatch.SetHeader(mockBatchHeader()) r.line = line if err := r.parseEntryDetail(); err != nil { @@ -95,7 +95,7 @@ func TestParseEntryDetail(t *testing.T) { func TestEDString(t *testing.T) { var line = "62705320001912345 0000010500c-1 Arnold Wade DD0076401255655291" r := NewReader(strings.NewReader(line)) - r.addCurrentBatch(NewBatchPPD()) + r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) r.currentBatch.SetHeader(mockBatchHeader()) r.line = line if err := r.parseEntryDetail(); err != nil { diff --git a/example/ach-ppd-write/main.go b/example/ach-ppd-write/main.go index 4596226c0..42d964ca5 100644 --- a/example/ach-ppd-write/main.go +++ b/example/ach-ppd-write/main.go @@ -43,8 +43,7 @@ func main() { entry.IndividualName = "Receiver Account Name" // Identifies the receiver of the transaction // build the batch - batch := ach.NewBatchPPD() - batch.SetHeader(bh) + batch := ach.NewBatchPPD(bh) batch.AddEntry(entry) if err := batch.Create(); err != nil { log.Fatalf("Unexpected error building batch: %s\n", err) diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index a8a234727..4b57ea9fd 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -5,6 +5,8 @@ import ( "os" "github.com/moov-io/ach" + "strconv" + "time" ) func main() { @@ -18,14 +20,16 @@ func main() { // To create a batch. // Errors only if payment type is not supported. - batch, _ := ach.NewBatch(ach.BatchParam{ - ServiceClassCode: "200", - CompanyName: "Your Company", - StandardEntryClass: "PPD", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Trans. Description", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 200 + bh.CompanyName = "Your Company" + bh.CompanyIdentification = strconv.Itoa(file.Header.ImmediateOrigin) + bh.StandardEntryClassCode = "PPD" + bh.CompanyEntryDescription = "Trans. Description" + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = 123456789 + + batch, _ := ach.NewBatch(bh) // To create an entry entry := ach.NewEntryDetail(ach.EntryParam{ @@ -58,14 +62,16 @@ func main() { // Now add a new batch for accepting payments on the web - batch2, _ := ach.NewBatch(ach.BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company", - StandardEntryClass: "WEB", - CompanyIdentification: "123456789", - CompanyEntryDescription: "subscr", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) + bh2 := ach.NewBatchHeader() + bh2.ServiceClassCode = 220 + bh2.CompanyName = "Your Company" + bh2.CompanyIdentification = strconv.Itoa(file.Header.ImmediateOrigin) + bh2.StandardEntryClassCode = "WEB" + bh2.CompanyEntryDescription = "Subscr" + bh2.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh2.ODFIIdentification = 123456789 + + batch2, _ := ach.NewBatch(bh2) // Add an entry and define if it is a single or reoccuring payment // The following is a reoccuring payment for $7.99 @@ -106,3 +112,5 @@ func main() { } w.Flush() } + + diff --git a/file_test.go b/file_test.go index 721e11316..83cd82002 100644 --- a/file_test.go +++ b/file_test.go @@ -109,8 +109,7 @@ func TestFileEntryHash(t *testing.T) { func TestFileBlockCount10(t *testing.T) { file := NewFile().SetHeader(mockFileHeader()) - batch := NewBatchPPD() - batch.SetHeader(mockBatchHeader()) + batch := NewBatchPPD(mockBatchPPDHeader()) batch.AddEntry(mockEntryDetail()) batch.AddEntry(mockEntryDetail()) batch.AddEntry(mockEntryDetail()) @@ -185,7 +184,10 @@ func TestFileReturnEntries(t *testing.T) { // create or copy the previous batch header of the item being returned batchHeader := mockBatchHeader() // create or copy the batch to be returned - batch, err := NewBatch(BatchParam{StandardEntryClass: batchHeader.StandardEntryClassCode}) + + //batch, err := NewBatch(BatchParam{StandardEntryClass: batchHeader.StandardEntryClassCode}) + batch, err := NewBatch(batchHeader) + if err != nil { t.Error(err.Error()) } diff --git a/reader.go b/reader.go index af675dc78..ca22e9fe6 100644 --- a/reader.go +++ b/reader.go @@ -195,12 +195,11 @@ func (r *Reader) parseBatchHeader() error { } // Passing SEC type into NewBatch creates a Batcher of SEC code type. - batch, err := NewBatch(bh.BatchParam()) + batch, err := NewBatch(bh) if err != nil { return r.error(err) } - batch.SetHeader(bh) r.addCurrentBatch(batch) return nil } diff --git a/reader_test.go b/reader_test.go index 10d16db3e..40fa398cb 100644 --- a/reader_test.go +++ b/reader_test.go @@ -104,7 +104,7 @@ func TestTwoFileControls(t *testing.T) { var line = "9000001000001000000010005320001000000010500000000000000 " var twoControls = line + "\n" + line r := NewReader(strings.NewReader(twoControls)) - r.addCurrentBatch(NewBatchPPD()) + r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) bc := BatchControl{EntryAddendaCount: 1, TotalDebitEntryDollarAmount: 10500, EntryHash: 5320001} @@ -193,10 +193,10 @@ func TestFileBatchHeaderErr(t *testing.T) { // TestFileBatchHeaderErr Error when two batch headers exists in a current batch func TestFileBatchHeaderDuplicate(t *testing.T) { // create a new Batch header string - bh := mockBatchHeader() + bh := mockBatchPPDHeader() r := NewReader(strings.NewReader(bh.String())) // instantitate a batch header in the reader - r.addCurrentBatch(NewBatchPPD()) + r.addCurrentBatch(NewBatchPPD(bh)) // read should fail because it is parsing a second batch header and there can only be one. _, err := r.Read() if p, ok := err.(*ParseError); ok { @@ -232,7 +232,7 @@ func TestFileEntryDetail(t *testing.T) { ed.TransactionCode = 0 line := ed.String() r := NewReader(strings.NewReader(line)) - r.addCurrentBatch(NewBatchPPD()) + r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) r.currentBatch.SetHeader(mockBatchHeader()) _, err := r.Read() if p, ok := err.(*ParseError); ok { diff --git a/recordParams_test.go b/recordParams_test.go index bd6d1bc28..aa21c8c2d 100644 --- a/recordParams_test.go +++ b/recordParams_test.go @@ -1,8 +1,8 @@ package ach import ( - "bytes" "testing" + "bytes" ) func TestFileParam(t *testing.T) { @@ -22,24 +22,20 @@ func TestFileParam(t *testing.T) { } } + func TestBatchParam(t *testing.T) { - companyName := "Your Company" - batch := NewBatchPPD(BatchParam{ - ServiceClassCode: "220", - CompanyName: companyName, - StandardEntryClass: "PPD", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Trans. Description", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) - - if err := batch.header.Validate(); err != nil { + companyName := "ACME Corporation" + batch, _ := NewBatch(mockBatchPPDHeader()) + + bh := batch.GetHeader() + if err := bh.Validate(); err != nil { t.Errorf("%T: %s", err, err) } - if batch.header.CompanyName != companyName { + if bh.CompanyName != companyName { t.Errorf("BatchParam value was not copied to batch.header.CompanyName") } } + func TestEntryParam(t *testing.T) { entry := NewEntryDetail(EntryParam{ ReceivingDFI: "102001017", @@ -103,14 +99,7 @@ func TestBuildFileParam(t *testing.T) { ReferenceCode: "#00000A1"}) // To create a batch. Errors only if payment type is not supported. - batch, _ := NewBatch(BatchParam{ - ServiceClassCode: "225", - CompanyName: "Your Company", - StandardEntryClass: "PPD", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Trans. Description", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "12345678"}) + batch, _ := NewBatch(mockBatchHeader()) // To create an entry entry := NewEntryDetail(EntryParam{ @@ -144,14 +133,7 @@ func TestBuildFileParam(t *testing.T) { // Now add a new batch for accepting payments on the web - batch, _ = NewBatch(BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company", - StandardEntryClass: "WEB", - CompanyIdentification: "123456789", - CompanyEntryDescription: "monthly subscription", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) + batch, _ = NewBatch(mockBatchWEBHeader()) // Add an entry and define if it is a single or reoccuring payment // The following is a reoccuring payment for $7.99 diff --git a/writer_test.go b/writer_test.go index 2ea8120b1..bb5d934d1 100644 --- a/writer_test.go +++ b/writer_test.go @@ -10,7 +10,7 @@ func TestPPDWrite(t *testing.T) { file := NewFile().SetHeader(mockFileHeader()) entry := mockEntryDetail() entry.AddAddenda(mockAddenda()) - batch := NewBatchPPD() + batch := NewBatchPPD(mockBatchPPDHeader()) batch.SetHeader(mockBatchHeader()) batch.AddEntry(entry) batch.Create() From afb7943478bc50319f8958e69187872ffbbc1f93 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 9 Apr 2018 13:01:22 -0400 Subject: [PATCH 0049/1694] README Updqte README file for BatchParam changes --- README.md | 77 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e0023df78..9685d91da 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ACH is under active development but already in production for multiple companies * CCD (Corporate credit or debit) * TEL (Telephone-Initiated Entry) * COR (Automated Notification of Change(NOC)) - * Return Entires + * Return Entries ## Project Roadmap @@ -75,21 +75,45 @@ file := ach.NewFile(ach.FileParam{ ReferenceCode: "#00000A1"}) ``` -To create a batch +Explicitly create a PPD batch file. Errors only if payment type is not supported ```go -batch := ach.NewBatch(ach.BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company", - StandardEntryClass: "PPD", - CompanyIdentification: "123456789", - CompanyEntryDescription: "Trans. Description", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) +func mockBatchPPDHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "PPD" + bh.CompanyName = "ACME Corporation" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "PAYROLL" + bh.EffectiveEntryDate = time.Now() + bh.ODFIIdentification = 6200001 + return bh +} + +mockBatch := NewBatch(mockBatchPPDHeader()) ``` +OR use the NewBatch factory + + ```go +func mockBatchPPDHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "PPD" + bh.CompanyName = "ACME Corporation" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "PAYROLL" + bh.EffectiveEntryDate = time.Now() + bh.ODFIIdentification = 6200001 + return bh +} + +mockBatch, _ := ach.NewBatch(mockBatchPPDHeader()) +``` + + To create an entry ```go @@ -134,14 +158,18 @@ file.AddBatch(batch) Now add a new batch for accepting payments on the web ```go -batch2, _ := ach.NewBatch(ach.BatchParam{ - ServiceClassCode: "220", - CompanyName: "Your Company", - StandardEntryClass: "WEB", - CompanyIdentification: "123456789", - CompanyEntryDescription: "subscr", - CompanyDescriptiveDate: "Oct 23", - ODFIIdentification: "123456789"}) +func mockBatchWEBHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "WEB" + bh.CompanyName = "Your Company, inc" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "Online Order" + bh.ODFIIdentification = 6200001 + return bh +} + +batch2, _ := ach.NewBatch(mockBatchWEBHeader()) ``` Add an entry and define if it is a single or reoccurring payment. The following is a reoccurring payment for $7.99 @@ -249,18 +277,9 @@ type BatchMTE struct { Add the ability for the new type to be created. ```go -func NewBatchMTE(params ...BatchParam) *BatchMTE { +func NewBatchMTE(bh *BatchHeader) *BatchMTE { batch := new(BatchMTE) batch.setControl(NewBatchControl) - - if len(params) > 0 { - bh := NewBatchHeader(params[0]) - bh.StandardEntryClassCode = "MTE" - batch.SetHeader(bh) - return batch - } - bh := NewBatchHeader() - bh.StandardEntryClassCode = "MTE" batch.SetHeader(bh) return batch } @@ -310,7 +329,7 @@ Finally add the batch type to the NewBatch factory in batch.go. ```go //... case "MTE": - return NewBatchMTE(bp), nil + return NewBatchMTE(bh), nil //... ``` From 204991439d0cef127debd3f70539369f6c8ece6a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 10 Apr 2018 10:08:00 -0400 Subject: [PATCH 0050/1694] FileParam Remove FileParam --- README.md | 15 +++++++++------ example/simple-file-creation/main.go | 14 ++++++++------ file.go | 22 +--------------------- fileHeader.go | 11 +---------- recordParams_test.go | 19 +++++-------------- 5 files changed, 24 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 9685d91da..de84dee32 100644 --- a/README.md +++ b/README.md @@ -67,12 +67,15 @@ if len(achFile.ReturnEntries) > 0 { The following is based on [simple file creation](https://github.com/moov-io/ach/tree/master/example/simple-file-creation) ```go -file := ach.NewFile(ach.FileParam{ - ImmediateDestination: "0210000890", - ImmediateOrigin: "123456789", - ImmediateDestinationName: "Your Bank", - ImmediateOriginName: "Your Company", - ReferenceCode: "#00000A1"}) + fh := ach.NewFileHeader() + fh.ImmediateDestination = 9876543210 // A blank space followed by your ODFI's transit/routing number + fh.ImmediateOrigin = 1234567890 // Organization or Company FED ID usually 1 and FEIN/SSN. Assigned by your ODFI + fh.FileCreationDate = time.Now() // Todays Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name") + + file := ach.NewFile() + file.SetHeader(fh) ``` Explicitly create a PPD batch file. diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index 4b57ea9fd..e79f30591 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -11,12 +11,14 @@ import ( func main() { // To create a file - file := ach.NewFile(ach.FileParam{ - ImmediateDestination: "0210000890", - ImmediateOrigin: "123456789", - ImmediateDestinationName: "Your Bank", - ImmediateOriginName: "Your Company", - ReferenceCode: "#00000A1"}) + fh := ach.NewFileHeader() + fh.ImmediateDestination = 9876543210 + fh.ImmediateOrigin = 1234567890 + fh.FileCreationDate = time.Now() + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + file := ach.NewFile() + file.SetHeader(fh) // To create a batch. // Errors only if payment type is not supported. diff --git a/file.go b/file.go index 186403ae7..7d944aa8a 100644 --- a/file.go +++ b/file.go @@ -74,28 +74,8 @@ type File struct { converters } -// FileParam is the minimal fields required to make a ach file header -type FileParam struct { - // ImmediateDestination is the originating banks ABA routing number. Frequently your banks ABA routing number. - ImmediateDestination string `json:"immediate_destination"` - // ImmediateOrigin - ImmediateOrigin string `json:"immediate_origin"` - // ImmediateDestinationName is the originating banks name. - ImmediateDestinationName string `json:"immediate_destination_name"` - ImmediateOriginName string `json:"immediate_origin_name"` - ReferenceCode string `json:"reference_code,omitempty"` -} - // NewFile constructs a file template. -func NewFile(params ...FileParam) *File { - if len(params) > 0 { - fh := NewFileHeader(params[0]) - return &File{ - Header: fh, - Control: NewFileControl(), - } - - } +func NewFile() *File { return &File{ Header: NewFileHeader(), Control: NewFileControl(), diff --git a/fileHeader.go b/fileHeader.go index d5575f622..49d0f4d46 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -92,7 +92,7 @@ type FileHeader struct { } // NewFileHeader returns a new FileHeader with default values for none exported fields -func NewFileHeader(params ...FileParam) FileHeader { +func NewFileHeader() FileHeader { fh := FileHeader{ recordType: "1", priorityCode: "01", @@ -101,15 +101,6 @@ func NewFileHeader(params ...FileParam) FileHeader { blockingFactor: "10", formatCode: "1", } - if len(params) > 0 { - fh.ImmediateDestination = fh.parseNumField(params[0].ImmediateDestination) - fh.ImmediateOrigin = fh.parseNumField(params[0].ImmediateOrigin) - fh.ImmediateDestinationName = params[0].ImmediateDestinationName - fh.ImmediateOriginName = params[0].ImmediateOriginName - fh.ReferenceCode = params[0].ReferenceCode - fh.FileCreationDate = time.Now() - return fh - } return fh } diff --git a/recordParams_test.go b/recordParams_test.go index aa21c8c2d..97d0f5a67 100644 --- a/recordParams_test.go +++ b/recordParams_test.go @@ -6,18 +6,13 @@ import ( ) func TestFileParam(t *testing.T) { - f := NewFile( - FileParam{ - ImmediateDestination: "0210000890", - ImmediateOrigin: "123456789", - ImmediateDestinationName: "Your Bank", - ImmediateOriginName: "Your Company", - ReferenceCode: "#00000A1"}) + f := NewFile() + f.SetHeader(mockFileHeader()) if err := f.Header.Validate(); err != nil { t.Errorf("%T: %s", err, err) } - if f.Header.ImmediateOriginName != "Your Company" { + if f.Header.ImmediateOriginName != "My Bank Name" { t.Errorf("FileParam value was not copied to file.Header") } } @@ -91,12 +86,8 @@ func TestAddendaParam(t *testing.T) { func TestBuildFileParam(t *testing.T) { // To create a file - file := NewFile(FileParam{ - ImmediateDestination: "0210000890", - ImmediateOrigin: "123456789", - ImmediateDestinationName: "Your Bank", - ImmediateOriginName: "Your Company", - ReferenceCode: "#00000A1"}) + file := NewFile() + file.SetHeader(mockFileHeader()) // To create a batch. Errors only if payment type is not supported. batch, _ := NewBatch(mockBatchHeader()) From e16f46650f7c435bb6a47c59330f8288f65eef8f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 10 Apr 2018 21:02:24 -0400 Subject: [PATCH 0051/1694] #164 EntryParam EntryParam removal for #164 --- batch.go | 2 +- batchCCD_test.go | 2 +- batchCOR_test.go | 2 +- batchHeader.go | 10 ++++- batchPPD_test.go | 52 ++++++++++++---------- batchTEL_test.go | 2 +- batchWEB_test.go | 2 +- batch_test.go | 5 +-- entryDetail.go | 45 +++---------------- entryDetail_internal_test.go | 16 ++++++- example/ach-ppd-write/main.go | 11 ++--- example/simple-file-creation/main.go | 19 ++++---- recordParams_test.go | 65 +++++++++------------------- 13 files changed, 99 insertions(+), 134 deletions(-) diff --git a/batch.go b/batch.go index dae70d3a7..80c8d3aad 100644 --- a/batch.go +++ b/batch.go @@ -124,7 +124,7 @@ func (batch *batch) build() error { } // Add a sequenced TraceNumber if one is not already set. Have to keep original trance number Return and NOC entries if currentTraceNumberODFI != batch.header.ODFIIdentification { - batch.entries[i].setTraceNumber(batch.header.ODFIIdentification, seq) + batch.entries[i].SetTraceNumber(batch.header.ODFIIdentification, seq) } seq++ addendaSeq := 1 diff --git a/batchCCD_test.go b/batchCCD_test.go index e71bb5f26..07ee5138f 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -23,7 +23,7 @@ func mockCCDEntryDetail() *EntryDetail { entry.Amount = 5000000 entry.IdentificationNumber = "location #23" entry.SetReceivingCompany("Best Co. #23") - entry.setTraceNumber(6200001, 123) + entry.SetTraceNumber(mockBatchCCDHeader().ODFIIdentification, 1) entry.DiscretionaryData = "S" return entry } diff --git a/batchCOR_test.go b/batchCOR_test.go index bfd35b49d..6754f5d8d 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -25,7 +25,7 @@ func mockCOREntryDetail() *EntryDetail { entry.Amount = 0 entry.IdentificationNumber = "location #23" entry.SetReceivingCompany("Best Co. #23") - entry.setTraceNumber(6200001, 0) + entry.SetTraceNumber(mockBatchCORHeader().ODFIIdentification, 1) entry.DiscretionaryData = "S" return entry } diff --git a/batchHeader.go b/batchHeader.go index a96b0622d..65d0580e2 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -41,7 +41,13 @@ type BatchHeader struct { // alpha or numeric character) of the entity in the company name field CompanyIdentification string - // StandardEntryClassCode PPD’ for consumer transactions, ‘CCD’ or ‘CTX’ for corporate + // StandardEntryClassCode + // Identifies the payment type (product) found within an ACH batch-using a 3-character code. + // The SEC Code pertains to all items within batch. + // Determines format of the detail records. + // Determines addenda records (required or optional PLUS one or up to 9,999 records). + // Determines rules to follow (return time frames). + // Some SEC codes require specific data in predetermined fields within the ACH record StandardEntryClassCode string // CompanyEntryDescription A description of the entries contained in the batch @@ -268,4 +274,4 @@ func (bh *BatchHeader) BatchNumberField() string { func (bh *BatchHeader) settlementDateField() string { return bh.alphaField(bh.settlementDate, 3) -} \ No newline at end of file +} diff --git a/batchPPD_test.go b/batchPPD_test.go index 510d923d0..c3ec5c1b6 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -9,7 +9,6 @@ import ( "time" ) - func mockBatchPPDHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 @@ -29,8 +28,33 @@ func mockPPDEntryDetail() *EntryDetail { entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" - entry.setTraceNumber(mockBatchPPDHeader().ODFIIdentification, 1) - //entry.setTraceNumber(6200001, 1) + entry.SetTraceNumber(mockBatchPPDHeader().ODFIIdentification, 1) + entry.Category = CategoryForward + return entry +} + +func mockBatchPPDHeader2() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 200 + bh.CompanyName = "MY BEST COMP." + bh.CompanyDiscretionaryData = "INCLUDES OVERTIME" + bh.CompanyIdentification = "1419871234" + bh.StandardEntryClassCode = "PPD" + bh.CompanyEntryDescription = "PAYROLL" + bh.EffectiveEntryDate = time.Now() + bh.ODFIIdentification = 109991234 + return bh +} + +func mockPPDEntry2() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 // ACH Credit + entry.SetRDFI(81086674) // scottrade bank routing number + entry.DFIAccountNumber = "62292250" // scottrade account number + entry.Amount = 1000000 // 1k dollars + entry.IdentificationNumber = "658-888-2468" // Unique ID for payment + entry.IndividualName = "Wade Arnold" + entry.SetTraceNumber(mockBatchPPDHeader2().ODFIIdentification, 1) entry.Category = CategoryForward return entry } @@ -128,26 +152,8 @@ func TestBatchODFIIDMismatch(t *testing.T) { } func TestBatchBuild(t *testing.T) { - header := NewBatchHeader() - header.ServiceClassCode = 200 - header.CompanyName = "MY BEST COMP." - header.CompanyDiscretionaryData = "INCLUDES OVERTIME" - header.CompanyIdentification = "1419871234" - header.StandardEntryClassCode = "PPD" - header.CompanyEntryDescription = "PAYROLL" - header.EffectiveEntryDate = time.Now() - header.ODFIIdentification = 109991234 - - mockBatch := NewBatchPPD(mockBatchPPDHeader()) - - entry := NewEntryDetail() - entry.TransactionCode = 22 // ACH Credit - entry.SetRDFI(81086674) // scottrade bank routing number - entry.DFIAccountNumber = "62292250" // scottrade account number - entry.Amount = 1000000 // 1k dollars - entry.IdentificationNumber = "658-888-2468" // Unique ID for payment - entry.IndividualName = "Wade Arnold" - entry.setTraceNumber(header.ODFIIdentification, 1) + mockBatch := NewBatchPPD(mockBatchPPDHeader2()) + entry := mockPPDEntry2() a1, _ := NewAddenda() entry.AddAddenda(a1) mockBatch.AddEntry(entry) diff --git a/batchTEL_test.go b/batchTEL_test.go index b3a245841..fd6a9b5be 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -23,7 +23,7 @@ func mockTELEntryDetail() *EntryDetail { entry.Amount = 5000000 entry.IdentificationNumber = "Phone 333-2222" entry.IndividualName = "Wade Arnold" - entry.setTraceNumber(6200001, 123) + entry.SetTraceNumber(mockBatchTELHeader().ODFIIdentification, 123) entry.SetPaymentType("S") return entry } diff --git a/batchWEB_test.go b/batchWEB_test.go index d80ceb0a1..a5f53fe3d 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -20,7 +20,7 @@ func mockWEBEntryDetail() *EntryDetail { entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" - entry.setTraceNumber(6200001, 1) + entry.SetTraceNumber(mockBatchWEBHeader().ODFIIdentification, 1) entry.SetPaymentType("S") return entry } diff --git a/batch_test.go b/batch_test.go index 4c1372452..0b7dbfdca 100644 --- a/batch_test.go +++ b/batch_test.go @@ -137,7 +137,7 @@ func TestBatchDNEMismatch(t *testing.T) { func TestBatchTraceNumberNotODFI(t *testing.T) { mockBatch := mockBatch() - mockBatch.GetEntries()[0].setTraceNumber(12345678, 1) + mockBatch.GetEntries()[0].SetTraceNumber(12345678, 1) if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "ODFIIdentificationField" { @@ -245,7 +245,6 @@ func TestBatchAddendaTraceNumber(t *testing.T) { } } - func TestNewBatchDefault(t *testing.T) { _, err := NewBatch(mockBatchInvalidSECHeader()) @@ -258,8 +257,6 @@ func TestNewBatchDefault(t *testing.T) { } } - - func TestBatchCategory(t *testing.T) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch diff --git a/entryDetail.go b/entryDetail.go index edff73b49..97e2188d2 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -90,45 +90,12 @@ const ( CategoryNOC = "NOC" ) -// EntryParam is the minimal fields required to make a ach entry -type EntryParam struct { - ReceivingDFI string `json:"receiving_dfi"` - RDFIAccount string `json:"rdfi_account"` - Amount string `json:"amount"` - IDNumber string `json:"id_number,omitempty"` - IndividualName string `json:"individual_name,omitempty"` - ReceivingCompany string `json:"receiving_company,omitempty"` - DiscretionaryData string `json:"discretionary_data,omitempty"` - PaymentType string `json:"payment_type,omitempty"` - TransactionCode string `json:"transaction_code"` -} - -// NewEntryDetail returns a new EntryDetail with default values for none exported fields -func NewEntryDetail(params ...EntryParam) *EntryDetail { +// NewEntryDetail returns a new EntryDetail with default values for non exported fields +func NewEntryDetail() *EntryDetail { entry := &EntryDetail{ recordType: "6", Category: CategoryForward, } - if len(params) > 0 { - entry.SetRDFI(entry.parseNumField(params[0].ReceivingDFI)) - entry.DFIAccountNumber = params[0].RDFIAccount - entry.Amount = entry.parseNumField(params[0].Amount) - entry.IdentificationNumber = params[0].IDNumber - if params[0].IndividualName != "" { - entry.IndividualName = params[0].IndividualName - } else { - entry.IndividualName = params[0].ReceivingCompany - } - if params[0].PaymentType != "" { - entry.SetPaymentType(params[0].PaymentType) - } else { - entry.DiscretionaryData = params[0].DiscretionaryData - } - entry.TransactionCode = entry.parseNumField(params[0].TransactionCode) - - entry.setTraceNumber(entry.RDFIIdentification, 1) - return entry - } return entry } @@ -264,9 +231,9 @@ func (ed *EntryDetail) SetRDFI(rdfi int) *EntryDetail { return ed } -// setTraceNumber takes first 8 digits of RDFI and concatenates a sequence number onto the TraceNumber -func (ed *EntryDetail) setTraceNumber(RDFIIdentification int, seq int) { - trace := ed.numericField(RDFIIdentification, 8) + ed.numericField(seq, 7) +// SetTraceNumber takes first 8 digits of ODFI and concatenates a sequence number onto the TraceNumber +func (ed *EntryDetail) SetTraceNumber(ODFIIdentification int, seq int) { + trace := ed.numericField(ODFIIdentification, 8) + ed.numericField(seq, 7) ed.TraceNumber = ed.parseNumField(trace) } @@ -317,7 +284,7 @@ func (ed *EntryDetail) PaymentTypeField() string { return ed.DiscretionaryData } -// SetPaymentType as R (Reoccuring) all other values will result in S (single) +// SetPaymentType as R (Reccuring) all other values will result in S (single) func (ed *EntryDetail) SetPaymentType(t string) { t = strings.ToUpper(strings.TrimSpace(t)) if t == "R" { diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index 600a21ad8..72f94a463 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -16,7 +16,21 @@ func mockEntryDetail() *EntryDetail { entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" - entry.setTraceNumber(6200001, 1) + entry.SetTraceNumber(mockBatchHeader().ODFIIdentification, 1) + entry.IdentificationNumber = "ABC##jvkdjfuiwn" + entry.Category = CategoryForward + return entry +} + +func mockEntryDemandDebit() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI(102001017) + entry.DFIAccountNumber = "5343121" + entry.Amount = 17500 + entry.IndividualName = "Robert Smith" + entry.SetTraceNumber(mockBatchHeader().ODFIIdentification, 1) + entry.IdentificationNumber = "ABC##jvkdjfuiwn" entry.Category = CategoryForward return entry } diff --git a/example/ach-ppd-write/main.go b/example/ach-ppd-write/main.go index 42d964ca5..5c0a838cb 100644 --- a/example/ach-ppd-write/main.go +++ b/example/ach-ppd-write/main.go @@ -35,11 +35,12 @@ func main() { // Identifies the receivers account information // can be multiple entry's per batch entry := ach.NewEntryDetail() - // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan - entry.TransactionCode = 22 // Code 22: Credit (deposit) to checking account - entry.SetRDFI(9101298) // Receivers bank transit routing number - entry.DFIAccountNumber = "12345678" // Receivers bank account number - entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 22 // Code 22: Credit (deposit) to checking account + entry.SetRDFI(9101298) // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetTraceNumber(bh.ODFIIdentification, 1) entry.IndividualName = "Receiver Account Name" // Identifies the receiver of the transaction // build the batch diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index e79f30591..fc91a0a1a 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -34,14 +34,15 @@ func main() { batch, _ := ach.NewBatch(bh) // To create an entry - entry := ach.NewEntryDetail(ach.EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "17500", - TransactionCode: "27", - IDNumber: "#456789", - IndividualName: "Bob Smith", - DiscretionaryData: "B1"}) + entry := ach.NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI(9101298) + entry.DFIAccountNumber = "123456789" + entry.Amount = 100000000 + entry.IndividualName = "Wade Arnold" + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.IdentificationNumber = "ABC##jvkdjfuiwn" + entry.Category = ach.CategoryForward // To add one or more optional addenda records for an entry addenda, _ := ach.NewAddenda(ach.AddendaParam{ @@ -114,5 +115,3 @@ func main() { } w.Flush() } - - diff --git a/recordParams_test.go b/recordParams_test.go index 97d0f5a67..adea6d077 100644 --- a/recordParams_test.go +++ b/recordParams_test.go @@ -1,8 +1,8 @@ package ach import ( - "testing" "bytes" + "testing" ) func TestFileParam(t *testing.T) { @@ -17,7 +17,6 @@ func TestFileParam(t *testing.T) { } } - func TestBatchParam(t *testing.T) { companyName := "ACME Corporation" batch, _ := NewBatch(mockBatchPPDHeader()) @@ -31,44 +30,34 @@ func TestBatchParam(t *testing.T) { } } -func TestEntryParam(t *testing.T) { - entry := NewEntryDetail(EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "17500", - TransactionCode: "27", - IDNumber: "ABC##jvkdjfuiwn", - IndividualName: "Bob Smith", - DiscretionaryData: "B1"}) +func TestEntryDetail(t *testing.T) { + entry := mockEntryDetail() + //override mockEntryDetail + entry.TransactionCode = 27 if err := entry.Validate(); err != nil { t.Errorf("%T: %s", err, err) } } -func TestEntryParamPaymentType(t *testing.T) { - entry := NewEntryDetail(EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "17500", - TransactionCode: "27", - IDNumber: "ABC##jvkdjfuiwn", - IndividualName: "Bob Smith", - PaymentType: "R"}) +func TestEntryDetailPaymentType(t *testing.T) { + + entry := mockEntryDetail() + //override mockEntryDetail + entry.TransactionCode = 27 + entry.DiscretionaryData = "R" if err := entry.Validate(); err != nil { t.Errorf("%T: %s", err, err) } } -func TestEntryParamReceivingCompany(t *testing.T) { - entry := NewEntryDetail(EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "17500", - TransactionCode: "27", - IDNumber: "location #23", - ReceivingCompany: "Best Co. #23"}) +func TestEntryDetailReceivingCompany(t *testing.T) { + entry := mockEntryDetail() + //override mockEntryDetail + entry.TransactionCode = 27 + entry.IdentificationNumber = "location #23" + entry.IndividualName = "Best Co. #23" if err := entry.Validate(); err != nil { t.Errorf("%T: %s", err, err) @@ -93,14 +82,7 @@ func TestBuildFileParam(t *testing.T) { batch, _ := NewBatch(mockBatchHeader()) // To create an entry - entry := NewEntryDetail(EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "17500", - TransactionCode: "27", - IDNumber: "ABC##jvkdjfuiwn", - IndividualName: "Robert Smith", - DiscretionaryData: "B1"}) + entry := mockEntryDemandDebit() // To add one or more optional addenda records for an entry @@ -126,17 +108,10 @@ func TestBuildFileParam(t *testing.T) { batch, _ = NewBatch(mockBatchWEBHeader()) - // Add an entry and define if it is a single or reoccuring payment + // Add an entry and define if it is a single or reccuring payment // The following is a reoccuring payment for $7.99 - entry = NewEntryDetail(EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "799", - TransactionCode: "22", - IDNumber: "#123456", - IndividualName: "Wade Arnold", - PaymentType: "R"}) + entry = mockWEBEntryDetail() addenda, _ = NewAddenda(AddendaParam{ PaymentRelatedInfo: "Monthly Membership Subscription"}) From 4219f5efde877733d542c5a208d3d2090a0417c2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 10 Apr 2018 21:10:15 -0400 Subject: [PATCH 0052/1694] #164 EntryParam EntryParam removal in readme.md and Entry2 of main.go --- README.md | 35 +++++++++++++++------------- example/simple-file-creation/main.go | 18 +++++++------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index de84dee32..58d1d2cb7 100644 --- a/README.md +++ b/README.md @@ -120,14 +120,15 @@ mockBatch, _ := ach.NewBatch(mockBatchPPDHeader()) To create an entry ```go -entry := ach.NewEntryDetail(ach.EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "17500", - TransactionCode: "27", - IDNumber: "ABC##jvkdjfuiwn", - IndividualName: "Bob Smith", - DiscretionaryData: "B1"}) +entry := ach.NewEntryDetail() +entry.TransactionCode = 22 +entry.SetRDFI(9101298) +entry.DFIAccountNumber = "123456789" +entry.Amount = 100000000 +entry.IndividualName = "Wade Arnold" +entry.SetTraceNumber(bh.ODFIIdentification, 1) +entry.IdentificationNumber = "ABC##jvkdjfuiwn" +entry.Category = ach.CategoryForward ``` To add one or more optional addenda records for an entry @@ -178,14 +179,16 @@ batch2, _ := ach.NewBatch(mockBatchWEBHeader()) Add an entry and define if it is a single or reoccurring payment. The following is a reoccurring payment for $7.99 ```go -entry2 := ach.NewEntryDetail(ach.EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "799", - TransactionCode: "22", - IDNumber: "#123456", - IndividualName: "Wade Arnold", - PaymentType: "R"}) +entry2 := ach.NewEntryDetail() +entry2.TransactionCode = 22 +entry2.SetRDFI(102001017) +entry2.DFIAccountNumber = "5343121" +entry2.Amount = 799 +entry2.IndividualName = "Wade Arnold" +entry2.SetTraceNumber(bh.ODFIIdentification, 1) +entry2.IdentificationNumber = "#123456" +entry.DiscretionaryData = "R" +entry2.Category = ach.CategoryForward addenda2 := ach.NewAddenda(ach.AddendaParam{ PaymentRelatedInfo: "Monthly Membership Subscription"}) diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index fc91a0a1a..86fe8d7bb 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -79,14 +79,16 @@ func main() { // Add an entry and define if it is a single or reoccuring payment // The following is a reoccuring payment for $7.99 - entry2 := ach.NewEntryDetail(ach.EntryParam{ - ReceivingDFI: "102001017", - RDFIAccount: "5343121", - Amount: "799", - TransactionCode: "22", - IDNumber: "#123456", - IndividualName: "Wade Arnold", - PaymentType: "R"}) + entry2 := ach.NewEntryDetail() + entry2.TransactionCode = 22 + entry2.SetRDFI(102001017) + entry2.DFIAccountNumber = "5343121" + entry2.Amount = 799 + entry2.IndividualName = "Wade Arnold" + entry2.SetTraceNumber(bh.ODFIIdentification, 1) + entry2.IdentificationNumber = "#123456" + entry.DiscretionaryData = "R" + entry2.Category = ach.CategoryForward addenda2, _ := ach.NewAddenda(ach.AddendaParam{ PaymentRelatedInfo: "Monthly Membership Subscription"}) From 6477485820eb733c42fd33300fd77f356a47aec3 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 10 Apr 2018 21:41:59 -0400 Subject: [PATCH 0053/1694] #164 EntryParam entry2 adjustments --- example/simple-file-creation/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index 86fe8d7bb..2417c8511 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -76,8 +76,8 @@ func main() { batch2, _ := ach.NewBatch(bh2) - // Add an entry and define if it is a single or reoccuring payment - // The following is a reoccuring payment for $7.99 + // Add an entry and define if it is a single or reccuring payment + // The following is a reccuring payment for $7.99 entry2 := ach.NewEntryDetail() entry2.TransactionCode = 22 @@ -85,9 +85,9 @@ func main() { entry2.DFIAccountNumber = "5343121" entry2.Amount = 799 entry2.IndividualName = "Wade Arnold" - entry2.SetTraceNumber(bh.ODFIIdentification, 1) + entry2.SetTraceNumber(bh2.ODFIIdentification, 2) entry2.IdentificationNumber = "#123456" - entry.DiscretionaryData = "R" + entry2.DiscretionaryData = "R" entry2.Category = ach.CategoryForward addenda2, _ := ach.NewAddenda(ach.AddendaParam{ From 58b9640ab096b80e0d044e45da9153e014b2fec9 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Apr 2018 10:45:10 -0400 Subject: [PATCH 0054/1694] #164 Remove AddendaParam # 164 Remove AddendaParam --- addenda.go | 68 +++++------------- addenda05.go | 133 ++++++++++++++++++++++++++++++++++ addenda05_test.go | 151 +++++++++++++++++++++++++++++++++++++++ addendaNOC.go | 9 +-- addendaNOC_test.go | 4 +- addendaReturn.go | 9 +-- addendaReturn_test.go | 4 +- addenda_internal_test.go | 27 ++++--- batchPPD_test.go | 4 +- entryDetail.go | 5 ++ reader.go | 14 ++-- recordParams_test.go | 41 ++++++----- 12 files changed, 363 insertions(+), 106 deletions(-) create mode 100644 addenda05.go create mode 100644 addenda05_test.go diff --git a/addenda.go b/addenda.go index 56afa50c2..72565e1f4 100644 --- a/addenda.go +++ b/addenda.go @@ -6,7 +6,11 @@ import ( ) // Addenda provides business transaction information in a machine -// readable format. It is usually formatted according to ANSI, ASC, X12 Standard +// readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// This can be used as a model for addenda records, however going forward the library +// will contain Type Code specific addenda types. Addenda05 has been added to the +// library, and should be used for Type Code "05" addenda records. + type Addenda struct { // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string @@ -29,58 +33,13 @@ type Addenda struct { converters } -// AddendaParam is the minimal fields required to make a ach addenda -type AddendaParam struct { - TypeCode string `json:"type_code,omitempty"` - PaymentRelatedInfo string `json:"payment_related_info,omitempty"` - TraceNumber string `json:"trace_number,omitempty"` - // Following Fields are used for Return addenda - ReturnCode string `json:"return_code,omitempty"` - OriginalTrace string `json:"original_trace,omitempty"` - AddendaInfo string `json:"addenda_info,omitempty"` - OriginalDFI string `json:"original_dfi,omitempty"` - // Following fields are used for NOC(notification of change) addenda w/ return fields - ChangeCode string `json:"change_code,omitempty"` - CorrectedData string `json:"corrected_data,omitempty"` +func NewAddenda() *Addenda { + addenda := new(Addenda) + addenda.recordType = "7" + addenda.typeCode = "05" + return addenda } -// NewAddenda returns a new Addenda with default values for none exported fields -// TypeCode in AddendaParam for none ACK, ATX, CCD, CIE, CTX, DNE, ENR, PPD, TRX and WEB Entries -func NewAddenda(params ...AddendaParam) (Addendumer, error) { - if (len(params)) > 0 { - // most common use case is 05 ACK, ATX, CCD, CIE, CTX, DNE, ENR, PPD, TRX and WEB Entries - if params[0].TypeCode == "" { - params[0].TypeCode = "05" - } - switch typeCode := params[0].TypeCode; typeCode { - case "05": - addenda := Addenda{ - recordType: "7", - typeCode: "05", - SequenceNumber: 1, - EntryDetailSequenceNumber: 1, - } - addenda.PaymentRelatedInformation = params[0].PaymentRelatedInfo - return &addenda, nil - case "98": - return NewAddendaNOC(params[0]), nil - case "99": - return NewAddendaReturn(params[0]), nil - default: - msg := fmt.Sprintf("Addenda Type Code %v is not supported", typeCode) - return nil, &FileError{FieldName: "TypeCode", Msg: msg} - } - } - // TODO think about renaming Addenda to something for its TypeCode NewAddenda05 - addenda := Addenda{ - recordType: "7", - typeCode: "05", - SequenceNumber: 1, - EntryDetailSequenceNumber: 1, - } - return &addenda, nil - -} // Parse takes the input record string and parses the Addenda values func (addenda *Addenda) Parse(record string) { @@ -107,6 +66,13 @@ func (addenda *Addenda) String() string { addenda.EntryDetailSequenceNumberField()) } +// SetPaymentRealtedInformation +func (addenda *Addenda) SetPaymentRelatedInformation(s string) *Addenda { + addenda.PaymentRelatedInformation = s + + return addenda +} + // Validate performs NACHA format rule checks on the record and returns an error if not Validated // The first error encountered is returned and stops that parsing. func (addenda *Addenda) Validate() error { diff --git a/addenda05.go b/addenda05.go new file mode 100644 index 000000000..5bfb82303 --- /dev/null +++ b/addenda05.go @@ -0,0 +1,133 @@ +package ach + +import ( + "fmt" + "strings" +) + +// Addenda05 provides business transaction information for Addenda Type Code 05 in a machine +// readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// This should be used in place addenda.go. Future development to allow for use case +// specific 05 addenda records. + +type Addenda05 struct { + // RecordType defines the type of record in the block. entryAddenda05 Pos 7 + recordType string + // TypeCode Addenda05 types code '05' + typeCode string + // PaymentRelatedInformation + PaymentRelatedInformation string + // SequenceNumber is consecutively assigned to each Addenda05 Record following + // an Entry Detail Record. The first addenda05 sequence number must always + // be a "1". + SequenceNumber int + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda05 returns a new Addenda05 with default values for none exported fields + +func NewAddenda05() (*Addenda05) { + addenda05 := new(Addenda05) + addenda05.recordType = "7" + addenda05.typeCode = "05" + return addenda05 +} + +// Parse takes the input record string and parses the Addenda05 values +func (addenda05 *Addenda05) Parse(record string) { + // 1-1 Always "7" + addenda05.recordType = "7" + // 2-3 Always 05 + addenda05.typeCode = record[1:3] + // 4-83 Based on the information entered (04-83) 80 alphanumeric + addenda05.PaymentRelatedInformation = strings.TrimSpace(record[3:83]) + // 84-87 SequenceNumber is consecutively assigned to each Addenda05 Record following + // an Entry Detail Record + addenda05.SequenceNumber = addenda05.parseNumField(record[83:87]) + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda05.EntryDetailSequenceNumber = addenda05.parseNumField(record[87:94]) +} + +// String writes the Addenda05 struct to a 94 character string. +func (addenda05 *Addenda05) String() string { + return fmt.Sprintf("%v%v%v%v%v", + addenda05.recordType, + addenda05.typeCode, + addenda05.PaymentRelatedInformationField(), + addenda05.SequenceNumberField(), + addenda05.EntryDetailSequenceNumberField()) +} + +// SetPaymentRealtedInformation +func (addenda05 *Addenda05) SetPaymentRelatedInformation(s string) *Addenda05 { + addenda05.PaymentRelatedInformation = s + + return addenda05 +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda05 *Addenda05) Validate() error { + if err := addenda05.fieldInclusion(); err != nil { + return err + } + if addenda05.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda05.recordType, Msg: msg} + } + if err := addenda05.isTypeCode(addenda05.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda05.typeCode, Msg: err.Error()} + } + if err := addenda05.isAlphanumeric(addenda05.PaymentRelatedInformation); err != nil { + return &FieldError{FieldName: "PaymentRelatedInformation", Value: addenda05.PaymentRelatedInformation, Msg: err.Error()} + } + + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda05 *Addenda05) fieldInclusion() error { + if addenda05.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda05.recordType, Msg: msgFieldInclusion} + } + if addenda05.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda05.typeCode, Msg: msgFieldInclusion} + } + if addenda05.SequenceNumber == 0 { + return &FieldError{FieldName: "SequenceNumber", Value: addenda05.SequenceNumberField(), Msg: msgFieldInclusion} + } + if addenda05.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", Value: addenda05.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// PaymentRelatedInformationField returns a zero padded PaymentRelatedInformation string +func (addenda05 *Addenda05) PaymentRelatedInformationField() string { + return addenda05.alphaField(addenda05.PaymentRelatedInformation, 80) +} + +// SequenceNumberField returns a zero padded SequenceNumber string +func (addenda05 *Addenda05) SequenceNumberField() string { + return addenda05.numericField(addenda05.SequenceNumber, 4) +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda05 *Addenda05) EntryDetailSequenceNumberField() string { + return addenda05.numericField(addenda05.EntryDetailSequenceNumber, 7) +} + +// TypeCode Defines the specific explanation and format for the addenda05 information +func (addenda05 *Addenda05) TypeCode() string { + return addenda05.typeCode +} + diff --git a/addenda05_test.go b/addenda05_test.go new file mode 100644 index 000000000..1f99e7f39 --- /dev/null +++ b/addenda05_test.go @@ -0,0 +1,151 @@ +// Copyright 2017 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" + "strings" +) + +func mockAddenda05() *Addenda05 { + addenda05 := NewAddenda05() + addenda05.SequenceNumber = 1 + addenda05.EntryDetailSequenceNumber = 1234567 + + return addenda05 +} + +func TestMockAddenda05(t *testing.T) { + addenda05 := mockAddenda05() + if err := addenda05.Validate(); err != nil { + t.Error("mockAddenda05 does not validate and will break other tests") + } + if addenda05.EntryDetailSequenceNumber != 1234567 { + t.Error("EntryDetailSequenceNumber dependent default value has changed") + } +} + +func TestParseAddenda05(t *testing.T) { + addendaPPD := NewAddenda05() + //var line = "705WEB DIEGO MAY 00010000001" + var line = "705PPD DIEGO MAY 00010000001" + addendaPPD.Parse(line) + + r := NewReader(strings.NewReader(line)) + + //Add a new BatchWEB + r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) + + //Add a WEB EntryDetail + entryDetail := mockPPDEntryDetail() + + //Add an addenda to the WEB EntryDetail + entryDetail.AddAddenda(addendaPPD) + + // add the WEB entry detail to the batch + r.currentBatch.AddEntry(entryDetail) + + record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda05) + + if record.recordType != "7" { + t.Errorf("RecordType Expected '7' got: %v", record.recordType) + } + if record.TypeCode() != "05" { + t.Errorf("TypeCode Expected 10 got: %v", record.TypeCode()) + } + if record.PaymentRelatedInformationField() != "PPD DIEGO MAY " { + t.Errorf("PaymentRelatedInformation Expected 'PPD DIEGO MAY ' got: %v", record.PaymentRelatedInformationField()) + } + if record.SequenceNumberField() != "0001" { + t.Errorf("SequenceNumber Expected '0001' got: %v", record.SequenceNumberField()) + } + if record.EntryDetailSequenceNumberField() != "0000001" { + t.Errorf("EntryDetailSequenceNumber Expected '0000001' got: %v", record.EntryDetailSequenceNumberField()) + } +} + +// TestAddenda05 String validates that a known parsed file can be return to a string of the same value +func TestAddenda05String(t *testing.T) { + addenda05 := NewAddenda05() + var line = "705WEB DIEGO MAY 00010000001" + addenda05.Parse(line) + + if addenda05.String() != line { + t.Errorf("Strings do not match") + } +} + +func TestValidateAddenda05RecordType(t *testing.T) { + addenda05 := mockAddenda05() + addenda05.recordType = "63" + if err := addenda05.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestValidateAddenda05TypeCode(t *testing.T) { + addenda05 := mockAddenda05() + addenda05.typeCode = "23" + if err := addenda05.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda05FieldInclusion(t *testing.T) { + addenda05 := mockAddenda05() + addenda05.EntryDetailSequenceNumber = 0 + if err := addenda05.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "EntryDetailSequenceNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda05FieldInclusionRecordType(t *testing.T) { + addenda05 := mockAddenda05() + addenda05.recordType = "" + if err := addenda05.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda05PaymentRelatedInformationAlphaNumeric(t *testing.T) { + addenda05 := mockAddenda05() + addenda05.PaymentRelatedInformation = "®©" + if err := addenda05.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "PaymentRelatedInformation" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda05TyeCodeNil(t *testing.T) { + addenda05 := mockAddenda05() + addenda05.typeCode = "" + if err := addenda05.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + diff --git a/addendaNOC.go b/addendaNOC.go index b6a8c9883..420c4e809 100644 --- a/addendaNOC.go +++ b/addendaNOC.go @@ -53,18 +53,11 @@ type changeCode struct { } // NewAddendaNOC returns an reference to an instantiated AddendaNOC with default values -func NewAddendaNOC(params ...AddendaParam) *AddendaNOC { +func NewAddendaNOC() *AddendaNOC { addendaNOC := &AddendaNOC{ recordType: "7", typeCode: "98", } - if len(params) > 0 { - addendaNOC.ChangeCode = params[0].ChangeCode - addendaNOC.OriginalTrace = addendaNOC.parseNumField(params[0].OriginalTrace) - addendaNOC.OriginalDFI = addendaNOC.parseNumField(params[0].OriginalDFI) - addendaNOC.CorrectedData = params[0].CorrectedData - addendaNOC.TraceNumber = addendaNOC.parseNumField(params[0].TraceNumber) - } return addendaNOC } diff --git a/addendaNOC_test.go b/addendaNOC_test.go index 48ef5db70..92a9ded47 100644 --- a/addendaNOC_test.go +++ b/addendaNOC_test.go @@ -1,7 +1,6 @@ package ach import ( - "strings" "testing" ) @@ -155,7 +154,7 @@ func TestAddendaNOCTraceNumberField(t *testing.T) { } } -func TestAddendaNOCNewAddendaParam(t *testing.T) { +/*func TestAddendaNOCNewAddendaParam(t *testing.T) { aParam := AddendaParam{ TypeCode: "98", ChangeCode: "C01", @@ -192,3 +191,4 @@ func TestAddendaNOCNewAddendaParam(t *testing.T) { t.Errorf("expected %v got %v", aParam.TraceNumber, aNOC.TraceNumber) } } +*/ \ No newline at end of file diff --git a/addendaReturn.go b/addendaReturn.go index bc09bbd87..79d504cfd 100644 --- a/addendaReturn.go +++ b/addendaReturn.go @@ -62,18 +62,11 @@ type returnCode struct { } // NewAddendaReturn returns a new AddendaReturn with default values for none exported fields -func NewAddendaReturn(params ...AddendaParam) *AddendaReturn { +func NewAddendaReturn() *AddendaReturn { addendaReturn := &AddendaReturn{ recordType: "7", typeCode: "99", } - if len(params) > 0 { - addendaReturn.ReturnCode = params[0].ReturnCode - addendaReturn.OriginalTrace = addendaReturn.parseNumField(params[0].OriginalTrace) - addendaReturn.OriginalDFI = addendaReturn.parseNumField(params[0].OriginalDFI) - addendaReturn.AddendaInformation = params[0].AddendaInfo - addendaReturn.TraceNumber = addendaReturn.parseNumField(params[0].TraceNumber) - } return addendaReturn } diff --git a/addendaReturn_test.go b/addendaReturn_test.go index a9d142344..9f58b6174 100644 --- a/addendaReturn_test.go +++ b/addendaReturn_test.go @@ -5,7 +5,6 @@ package ach import ( - "strings" "testing" "time" ) @@ -155,7 +154,7 @@ func TestAddendaReturnTraceNumberField(t *testing.T) { } } -func TestAddendaReturnNewAddendaParam(t *testing.T) { +/*func TestAddendaReturnNewAddendaParam(t *testing.T) { aParam := AddendaParam{ TypeCode: "99", ReturnCode: "R07", @@ -192,3 +191,4 @@ func TestAddendaReturnNewAddendaParam(t *testing.T) { t.Errorf("expected %v got %v", aParam.TraceNumber, addendaReturn.TraceNumber) } } +*/ \ No newline at end of file diff --git a/addenda_internal_test.go b/addenda_internal_test.go index 2daa80517..9c601a505 100644 --- a/addenda_internal_test.go +++ b/addenda_internal_test.go @@ -19,8 +19,10 @@ func mockAddenda() *Addenda { return &addenda } + func TestMockAddenda(t *testing.T) { addenda := mockAddenda() + if err := addenda.Validate(); err != nil { t.Error("mockAddenda does not validate and will break other tests") } @@ -30,17 +32,24 @@ func TestMockAddenda(t *testing.T) { } func TestParseAddenda(t *testing.T) { + addendaWEB := NewAddenda() var line = "705WEB DIEGO MAY 00010000001" + addendaWEB.Parse(line) r := NewReader(strings.NewReader(line)) - r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) - //r.currentBatch.GetHeader().StandardEntryClassCode = "PPD" - r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 22, AddendaRecordIndicator: 1}) - r.line = line - err := r.parseAddenda() - if err != nil { - t.Errorf("%T: %s", err, err) - } + + //Add a new BatchWEB + r.addCurrentBatch(NewBatchWEB(mockBatchWEBHeader())) + + //Add a WEB EntryDetail + entryDetail := mockWEBEntryDetail() + + //Add an addenda to the WEB EntryDetail + entryDetail.AddAddenda(addendaWEB) + + // add the WEB entry detail to the batch + r.currentBatch.AddEntry(entryDetail) + record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda) if record.recordType != "7" { @@ -60,7 +69,7 @@ func TestParseAddenda(t *testing.T) { } } -// TestAddendaString validats that a known parsed file can be return to a string of the same value +// TestAddendaString validates that a known parsed file can be return to a string of the same value func TestAddendaString(t *testing.T) { var line = "705WEB DIEGO MAY 00010000001" r := NewReader(strings.NewReader(line)) diff --git a/batchPPD_test.go b/batchPPD_test.go index c3ec5c1b6..d8566d05a 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -154,8 +154,8 @@ func TestBatchODFIIDMismatch(t *testing.T) { func TestBatchBuild(t *testing.T) { mockBatch := NewBatchPPD(mockBatchPPDHeader2()) entry := mockPPDEntry2() - a1, _ := NewAddenda() - entry.AddAddenda(a1) + addenda05 := NewAddenda05() + entry.AddAddenda(addenda05) mockBatch.AddEntry(entry) if err := mockBatch.Create(); err != nil { t.Errorf("%T: %s", err, err) diff --git a/entryDetail.go b/entryDetail.go index 97e2188d2..e33f4f6f2 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -216,6 +216,11 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum + case *Addenda05: + ed.Category = CategoryForward + ed.Addendum = nil + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum default: ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) diff --git a/reader.go b/reader.go index ca22e9fe6..ea707e7f8 100644 --- a/reader.go +++ b/reader.go @@ -235,16 +235,14 @@ func (r *Reader) parseAddenda() error { if entry.AddendaRecordIndicator == 1 { // Passing TypeCode type into NewAddenda creates a Addendumer of type copde type. - addenda, err := NewAddenda(AddendaParam{ - TypeCode: r.line[1:3]}) - if err != nil { - return r.error(err) - } - addenda.Parse(r.line) - if err := addenda.Validate(); err != nil { + + addenda05 := NewAddenda05() + + addenda05.Parse(r.line) + if err := addenda05.Validate(); err != nil { return r.error(err) } - r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda) + r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda05) } else { msg := fmt.Sprintf(msgBatchAddendaIndicator) return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) diff --git a/recordParams_test.go b/recordParams_test.go index adea6d077..5e24b121a 100644 --- a/recordParams_test.go +++ b/recordParams_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestFileParam(t *testing.T) { +func TestFileRecord(t *testing.T) { f := NewFile() f.SetHeader(mockFileHeader()) if err := f.Header.Validate(); err != nil { @@ -17,7 +17,7 @@ func TestFileParam(t *testing.T) { } } -func TestBatchParam(t *testing.T) { +func TestBatchRecord(t *testing.T) { companyName := "ACME Corporation" batch, _ := NewBatch(mockBatchPPDHeader()) @@ -64,16 +64,18 @@ func TestEntryDetailReceivingCompany(t *testing.T) { } } -func TestAddendaParam(t *testing.T) { - addenda, _ := NewAddenda(AddendaParam{ - PaymentRelatedInfo: "Currently string needs ASC X12 Interchange Control Structures", - }) - if err := addenda.Validate(); err != nil { +func TestAddendaRecord(t *testing.T) { + addenda05 := NewAddenda05() + addenda05.PaymentRelatedInformation = "Currently string needs ASC X12 Interchange Control Structures" + addenda05.SequenceNumber = 1 + addenda05.EntryDetailSequenceNumber = 1234567 + + if err := addenda05.Validate(); err != nil { t.Errorf("%T: %s", err, err) } } -func TestBuildFileParam(t *testing.T) { +func TestBuildFile(t *testing.T) { // To create a file file := NewFile() file.SetHeader(mockFileHeader()) @@ -82,13 +84,17 @@ func TestBuildFileParam(t *testing.T) { batch, _ := NewBatch(mockBatchHeader()) // To create an entry - entry := mockEntryDemandDebit() + entry := mockPPDEntryDetail() // To add one or more optional addenda records for an entry - addenda, _ := NewAddenda(AddendaParam{ - PaymentRelatedInfo: "bonus pay for amazing work on #OSS"}) - entry.AddAddenda(addenda) + addendaPPD := NewAddenda05() + addendaPPD.PaymentRelatedInformation = "Currently string needs ASC X12 Interchange Control Structures" + addendaPPD.SequenceNumber = 1 + addendaPPD.EntryDetailSequenceNumber = 1234567 + + // Add the addenda record to the detail entry + entry.AddAddenda(addendaPPD) // Entries are added to batches like so: @@ -113,11 +119,14 @@ func TestBuildFileParam(t *testing.T) { entry = mockWEBEntryDetail() - addenda, _ = NewAddenda(AddendaParam{ - PaymentRelatedInfo: "Monthly Membership Subscription"}) - // add the entry to the batch - entry.AddAddenda(addenda) + addendaWEB := NewAddenda05() + addendaWEB.PaymentRelatedInformation = "Monthly Membership Subscription" + addendaWEB.SequenceNumber = 1 + addendaWEB.EntryDetailSequenceNumber = 1234568 + + // Add the addenda record to the detail entry + entry.AddAddenda(addendaWEB) // add the second batch to the file From 6acdf5e63b53de69996d3a076f516217a26f7445 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Apr 2018 10:52:23 -0400 Subject: [PATCH 0055/1694] #164 Remove AddendaParam README #164 Remove AddendaParam README --- README.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 58d1d2cb7..59377fc93 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,14 @@ entry.Category = ach.CategoryForward To add one or more optional addenda records for an entry ```go -addenda := ach.NewAddenda(ach.AddendaParam{ - PaymentRelatedInfo: "bonus pay for amazing work on #OSS"}) +addenda := NewAddenda05() +addenda.PaymentRelatedInformation = "Bonus pay for amazing work on #OSS" +addenda.SequenceNumber = 1 +addenda.EntryDetailSequenceNumber = 1234567 +``` +Add the addenda record to the detail entry + + ```go entry.AddAddenda(addenda) ``` @@ -190,8 +196,11 @@ entry2.IdentificationNumber = "#123456" entry.DiscretionaryData = "R" entry2.Category = ach.CategoryForward -addenda2 := ach.NewAddenda(ach.AddendaParam{ - PaymentRelatedInfo: "Monthly Membership Subscription"}) + +addenda2 := NewAddenda05() +addenda2.PaymentRelatedInformation = "Monthly Membership Subscription" +addenda2.SequenceNumber = 1 +addenda2.EntryDetailSequenceNumber = 1234568 ``` Add the entry to the batch From 432b1aed757377a7004017f6ca321a4aa7356679 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 18 Apr 2018 13:38:40 -0400 Subject: [PATCH 0056/1694] # 164 AddendaParam Removed addenda.go, addendaNOC.go, and addendaReturn.go. Replaced with addenda05.go, addenda98.go, and addenda99.go --- README.md | 12 +- addenda.go | 132 ----------------- addenda05.go | 10 +- addenda05_test.go | 10 +- addendaNOC.go => addenda98.go | 102 ++++++------- addenda98_test.go | 155 ++++++++++++++++++++ addendaReturn.go => addenda99.go | 96 ++++++------ addenda99_test.go | 150 +++++++++++++++++++ addendaNOC_test.go | 194 ------------------------- addendaReturn_test.go | 194 ------------------------- addenda_internal_test.go | 160 -------------------- batch.go | 4 +- batchCCD_test.go | 8 +- batchCOR.go | 18 +-- batchCOR_test.go | 10 +- batchPPD_test.go | 2 +- batchTEL_test.go | 2 +- batchWEB_test.go | 6 +- batch_test.go | 26 ++-- entryDetail.go | 10 +- entryDetail_internal_test.go | 31 ++-- file.go | 1 + file_test.go | 2 +- reader.go | 30 +++- reader_test.go | 66 +++++++-- recordParams_test.go => record_test.go | 6 - writer_test.go | 2 +- 27 files changed, 548 insertions(+), 891 deletions(-) delete mode 100644 addenda.go rename addendaNOC.go => addenda98.go (57%) create mode 100644 addenda98_test.go rename addendaReturn.go => addenda99.go (76%) create mode 100644 addenda99_test.go delete mode 100644 addendaNOC_test.go delete mode 100644 addendaReturn_test.go delete mode 100644 addenda_internal_test.go rename recordParams_test.go => record_test.go (95%) diff --git a/README.md b/README.md index 59377fc93..0c61fa2aa 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,14 @@ if achFile.Validate(); err != nil { // Check if any Notifications Of Change exist in the file if len(achFile.NotificationOfChange) > 0 { for _, batch := range achFile.NotificationOfChange { - aNOC := batch.GetEntries()[0].Addendum[0].(*AddendaNOC) - println(aNOC.CorrectedData) + a98 := batch.GetEntries()[0].Addendum[0].(*Addenda98) + println(a98.CorrectedData) } } // Check if any Return Entries exist in the file if len(achFile.ReturnEntries) > 0 { for _, batch := range achFile.ReturnEntries { - aReturn := batch.GetEntries()[0].Addendum[0].(*AddendaReturn) + aReturn := batch.GetEntries()[0].Addendum[0].(*Addenda99) println(aReturn.ReturnCode) } } @@ -136,8 +136,6 @@ To add one or more optional addenda records for an entry ```go addenda := NewAddenda05() addenda.PaymentRelatedInformation = "Bonus pay for amazing work on #OSS" -addenda.SequenceNumber = 1 -addenda.EntryDetailSequenceNumber = 1234567 ``` Add the addenda record to the detail entry @@ -165,7 +163,7 @@ And batches are added to files much the same way: file.AddBatch(batch) ``` -Now add a new batch for accepting payments on the web +Now add a new batch for accepting payments on the WEB ```go func mockBatchWEBHeader() *BatchHeader { @@ -199,8 +197,6 @@ entry2.Category = ach.CategoryForward addenda2 := NewAddenda05() addenda2.PaymentRelatedInformation = "Monthly Membership Subscription" -addenda2.SequenceNumber = 1 -addenda2.EntryDetailSequenceNumber = 1234568 ``` Add the entry to the batch diff --git a/addenda.go b/addenda.go deleted file mode 100644 index 72565e1f4..000000000 --- a/addenda.go +++ /dev/null @@ -1,132 +0,0 @@ -package ach - -import ( - "fmt" - "strings" -) - -// Addenda provides business transaction information in a machine -// readable format. It is usually formatted according to ANSI, ASC, X12 Standard. -// This can be used as a model for addenda records, however going forward the library -// will contain Type Code specific addenda types. Addenda05 has been added to the -// library, and should be used for Type Code "05" addenda records. - -type Addenda struct { - // RecordType defines the type of record in the block. entryAddendaPos 7 - recordType string - // TypeCode Addenda types code '05' - typeCode string - // PaymentRelatedInformation - PaymentRelatedInformation string - // SequenceNumber is consecutively assigned to each Addenda Record following - // an Entry Detail Record. The first addenda sequence number must always - // be a "1". - SequenceNumber int - // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry - // Detail or Corporate Entry Detail Record's trace number This number is - // the same as the last seven digits of the trace number of the related - // Entry Detail Record or Corporate Entry Detail Record. - EntryDetailSequenceNumber int - // validator is composed for data validation - validator - // converters is composed for ACH to GoLang Converters - converters -} - -func NewAddenda() *Addenda { - addenda := new(Addenda) - addenda.recordType = "7" - addenda.typeCode = "05" - return addenda -} - - -// Parse takes the input record string and parses the Addenda values -func (addenda *Addenda) Parse(record string) { - // 1-1 Always "7" - addenda.recordType = "7" - // 2-3 Defines the specific explanation and format for the addenda information contained in the same record - addenda.typeCode = record[1:3] - // 4-83 Based on the information entered (04-83) 80 alphanumeric - addenda.PaymentRelatedInformation = strings.TrimSpace(record[3:83]) - // 84-87 SequenceNumber is consecutively assigned to each Addenda Record following - // an Entry Detail Record - addenda.SequenceNumber = addenda.parseNumField(record[83:87]) - // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record - addenda.EntryDetailSequenceNumber = addenda.parseNumField(record[87:94]) -} - -// String writes the Addenda struct to a 94 character string. -func (addenda *Addenda) String() string { - return fmt.Sprintf("%v%v%v%v%v", - addenda.recordType, - addenda.typeCode, - addenda.PaymentRelatedInformationField(), - addenda.SequenceNumberField(), - addenda.EntryDetailSequenceNumberField()) -} - -// SetPaymentRealtedInformation -func (addenda *Addenda) SetPaymentRelatedInformation(s string) *Addenda { - addenda.PaymentRelatedInformation = s - - return addenda -} - -// Validate performs NACHA format rule checks on the record and returns an error if not Validated -// The first error encountered is returned and stops that parsing. -func (addenda *Addenda) Validate() error { - if err := addenda.fieldInclusion(); err != nil { - return err - } - if addenda.recordType != "7" { - msg := fmt.Sprintf(msgRecordType, 7) - return &FieldError{FieldName: "recordType", Value: addenda.recordType, Msg: msg} - } - if err := addenda.isTypeCode(addenda.typeCode); err != nil { - return &FieldError{FieldName: "TypeCode", Value: addenda.typeCode, Msg: err.Error()} - } - if err := addenda.isAlphanumeric(addenda.PaymentRelatedInformation); err != nil { - return &FieldError{FieldName: "PaymentRelatedInformation", Value: addenda.PaymentRelatedInformation, Msg: err.Error()} - } - - return nil -} - -// fieldInclusion validate mandatory fields are not default values. If fields are -// invalid the ACH transfer will be returned. -func (addenda *Addenda) fieldInclusion() error { - if addenda.recordType == "" { - return &FieldError{FieldName: "recordType", Value: addenda.recordType, Msg: msgFieldInclusion} - } - if addenda.typeCode == "" { - return &FieldError{FieldName: "TypeCode", Value: addenda.typeCode, Msg: msgFieldInclusion} - } - if addenda.SequenceNumber == 0 { - return &FieldError{FieldName: "SequenceNumber", Value: addenda.SequenceNumberField(), Msg: msgFieldInclusion} - } - if addenda.EntryDetailSequenceNumber == 0 { - return &FieldError{FieldName: "EntryDetailSequenceNumber", Value: addenda.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} - } - return nil -} - -// PaymentRelatedInformationField returns a zero padded PaymentRelatedInformation string -func (addenda *Addenda) PaymentRelatedInformationField() string { - return addenda.alphaField(addenda.PaymentRelatedInformation, 80) -} - -// SequenceNumberField returns a zero padded SequenceNumber string -func (addenda *Addenda) SequenceNumberField() string { - return addenda.numericField(addenda.SequenceNumber, 4) -} - -// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string -func (addenda *Addenda) EntryDetailSequenceNumberField() string { - return addenda.numericField(addenda.EntryDetailSequenceNumber, 7) -} - -// TypeCode Defines the specific explanation and format for the addenda information -func (addenda *Addenda) TypeCode() string { - return addenda.typeCode -} diff --git a/addenda05.go b/addenda05.go index 5bfb82303..9ff3191d8 100644 --- a/addenda05.go +++ b/addenda05.go @@ -5,10 +5,9 @@ import ( "strings" ) -// Addenda05 provides business transaction information for Addenda Type Code 05 in a machine -// readable format. It is usually formatted according to ANSI, ASC, X12 Standard. -// This should be used in place addenda.go. Future development to allow for use case -// specific 05 addenda records. +// Addenda05 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 05 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// Future development to allow for use case specific 05 addenda records. type Addenda05 struct { // RecordType defines the type of record in the block. entryAddenda05 Pos 7 @@ -34,7 +33,7 @@ type Addenda05 struct { // NewAddenda05 returns a new Addenda05 with default values for none exported fields -func NewAddenda05() (*Addenda05) { +func NewAddenda05() *Addenda05 { addenda05 := new(Addenda05) addenda05.recordType = "7" addenda05.typeCode = "05" @@ -130,4 +129,3 @@ func (addenda05 *Addenda05) EntryDetailSequenceNumberField() string { func (addenda05 *Addenda05) TypeCode() string { return addenda05.typeCode } - diff --git a/addenda05_test.go b/addenda05_test.go index 1f99e7f39..29d3b9854 100644 --- a/addenda05_test.go +++ b/addenda05_test.go @@ -5,14 +5,14 @@ package ach import ( - "testing" "strings" + "testing" ) func mockAddenda05() *Addenda05 { addenda05 := NewAddenda05() - addenda05.SequenceNumber = 1 - addenda05.EntryDetailSequenceNumber = 1234567 + addenda05.SequenceNumber = 1 + addenda05.EntryDetailSequenceNumber = 0000001 return addenda05 } @@ -22,14 +22,13 @@ func TestMockAddenda05(t *testing.T) { if err := addenda05.Validate(); err != nil { t.Error("mockAddenda05 does not validate and will break other tests") } - if addenda05.EntryDetailSequenceNumber != 1234567 { + if addenda05.EntryDetailSequenceNumber != 0000001 { t.Error("EntryDetailSequenceNumber dependent default value has changed") } } func TestParseAddenda05(t *testing.T) { addendaPPD := NewAddenda05() - //var line = "705WEB DIEGO MAY 00010000001" var line = "705PPD DIEGO MAY 00010000001" addendaPPD.Parse(line) @@ -148,4 +147,3 @@ func TestAddenda05TyeCodeNil(t *testing.T) { } } } - diff --git a/addendaNOC.go b/addenda98.go similarity index 57% rename from addendaNOC.go rename to addenda98.go index 420c4e809..3e732d749 100644 --- a/addendaNOC.go +++ b/addenda98.go @@ -5,9 +5,9 @@ import ( "strings" ) -// AddendaNOC is a Addendumer addenda record format for Notification OF Change(NOC) +// Addenda98 is a Addendumer addenda record format for Notification OF Change(98) // The field contents for Notification of Change Entries must match the field contents of the original Entries -type AddendaNOC struct { +type Addenda98 struct { // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string // TypeCode Addenda types code '98' @@ -17,7 +17,7 @@ type AddendaNOC struct { ChangeCode string // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, - // in the Addenda Record of an NOC, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. + // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. OriginalTrace int // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. OriginalDFI int @@ -35,10 +35,10 @@ type AddendaNOC struct { var ( changeCodeDict = map[string]*changeCode{} - // Error messages specific to AddendaNOC - msgAddendaNOCChangeCode = "found is not a valid addenda Change Code" - msgAddendaNOCTypeCode = "is not AddendaNOC type code of 98" - msgAddendaNOCCorrectedData = "must contain the corrected information corresponding to the Change Code" + // Error messages specific to Addenda98 + msgAddenda98ChangeCode = "found is not a valid addenda Change Code" + msgAddenda98TypeCode = "is not Addenda98 type code of 98" + msgAddenda98CorrectedData = "must contain the corrected information corresponding to the Change Code" ) func init() { @@ -52,96 +52,96 @@ type changeCode struct { Code, Reason, Description string } -// NewAddendaNOC returns an reference to an instantiated AddendaNOC with default values -func NewAddendaNOC() *AddendaNOC { - addendaNOC := &AddendaNOC{ +// NewAddenda98 returns an reference to an instantiated Addenda98 with default values +func NewAddenda98() *Addenda98 { + addenda98 := &Addenda98{ recordType: "7", typeCode: "98", } - return addendaNOC + return addenda98 } -// Parse takes the input record string and parses the AddendaNOC values -func (addendaNOC *AddendaNOC) Parse(record string) { +// Parse takes the input record string and parses the Addenda98 values +func (addenda98 *Addenda98) Parse(record string) { // 1-1 Always "7" - addendaNOC.recordType = "7" + addenda98.recordType = "7" // 2-3 Always "98" - addendaNOC.typeCode = record[1:3] + addenda98.typeCode = record[1:3] // 4-6 - addendaNOC.ChangeCode = record[3:6] + addenda98.ChangeCode = record[3:6] // 7-21 - addendaNOC.OriginalTrace = addendaNOC.parseNumField(record[6:21]) + addenda98.OriginalTrace = addenda98.parseNumField(record[6:21]) // 28-35 - addendaNOC.OriginalDFI = addendaNOC.parseNumField(record[27:35]) + addenda98.OriginalDFI = addenda98.parseNumField(record[27:35]) // 36-64 - addendaNOC.CorrectedData = strings.TrimSpace(record[35:64]) + addenda98.CorrectedData = strings.TrimSpace(record[35:64]) // 80-94 - addendaNOC.TraceNumber = addendaNOC.parseNumField(record[79:94]) + addenda98.TraceNumber = addenda98.parseNumField(record[79:94]) } -// String writes the AddendaNOC struct to a 94 character string -func (addendaNOC *AddendaNOC) String() string { +// String writes the Addenda98 struct to a 94 character string +func (addenda98 *Addenda98) String() string { return fmt.Sprintf("%v%v%v%v%v%v%v%v%v", - addendaNOC.recordType, - addendaNOC.TypeCode(), - addendaNOC.ChangeCode, - addendaNOC.OriginalTraceField(), + addenda98.recordType, + addenda98.TypeCode(), + addenda98.ChangeCode, + addenda98.OriginalTraceField(), " ", //6 char reserved field - addendaNOC.OriginalDFIField(), - addendaNOC.CorrectedDataField(), + addenda98.OriginalDFIField(), + addenda98.CorrectedDataField(), " ", // 15 char reserved field - addendaNOC.TraceNumberField(), + addenda98.TraceNumberField(), ) } -// Validate verifies NACHA rules for AddendaNOC -func (addendaNOC *AddendaNOC) Validate() error { - if addendaNOC.recordType != "7" { +// Validate verifies NACHA rules for Addenda98 +func (addenda98 *Addenda98) Validate() error { + if addenda98.recordType != "7" { msg := fmt.Sprintf(msgRecordType, 7) - return &FieldError{FieldName: "recordType", Value: addendaNOC.recordType, Msg: msg} + return &FieldError{FieldName: "recordType", Value: addenda98.recordType, Msg: msg} } // Type Code must be 98 - if addendaNOC.typeCode != "98" { - return &FieldError{FieldName: "TypeCode", Value: addendaNOC.typeCode, Msg: msgAddendaTypeCode} + if addenda98.typeCode != "98" { + return &FieldError{FieldName: "TypeCode", Value: addenda98.typeCode, Msg: msgAddendaTypeCode} } - // AddendaNOC requires a valid ChangeCode - _, ok := changeCodeDict[addendaNOC.ChangeCode] + // Addenda98 requires a valid ChangeCode + _, ok := changeCodeDict[addenda98.ChangeCode] if !ok { - return &FieldError{FieldName: "ChangeCode", Value: addendaNOC.ChangeCode, Msg: msgAddendaNOCChangeCode} + return &FieldError{FieldName: "ChangeCode", Value: addenda98.ChangeCode, Msg: msgAddenda98ChangeCode} } - // AddendaNOC Record must contain the corrected information corresponding to the Change Code used - if addendaNOC.CorrectedData == "" { - return &FieldError{FieldName: "CorrectedData", Value: addendaNOC.CorrectedData, Msg: msgAddendaNOCCorrectedData} + // Addenda98 Record must contain the corrected information corresponding to the Change Code used + if addenda98.CorrectedData == "" { + return &FieldError{FieldName: "CorrectedData", Value: addenda98.CorrectedData, Msg: msgAddenda98CorrectedData} } return nil } // TypeCode defines the format of the underlying addenda record -func (addendaNOC *AddendaNOC) TypeCode() string { - return addendaNOC.typeCode +func (addenda98 *Addenda98) TypeCode() string { + return addenda98.typeCode } // OriginalTraceField returns a zero padded OriginalTrace string -func (addendaNOC *AddendaNOC) OriginalTraceField() string { - return addendaNOC.numericField(addendaNOC.OriginalTrace, 15) +func (addenda98 *Addenda98) OriginalTraceField() string { + return addenda98.numericField(addenda98.OriginalTrace, 15) } // OriginalDFIField returns a zero padded OriginalDFI string -func (addendaNOC *AddendaNOC) OriginalDFIField() string { - return addendaNOC.numericField(addendaNOC.OriginalDFI, 8) +func (addenda98 *Addenda98) OriginalDFIField() string { + return addenda98.numericField(addenda98.OriginalDFI, 8) } //CorrectedDataField returns a space padded CorrectedData string -func (addendaNOC *AddendaNOC) CorrectedDataField() string { - return addendaNOC.alphaField(addendaNOC.CorrectedData, 29) +func (addenda98 *Addenda98) CorrectedDataField() string { + return addenda98.alphaField(addenda98.CorrectedData, 29) } // TraceNumberField returns a zero padded traceNumber string -func (addendaNOC *AddendaNOC) TraceNumberField() string { - return addendaNOC.numericField(addendaNOC.TraceNumber, 15) +func (addenda98 *Addenda98) TraceNumberField() string { + return addenda98.numericField(addenda98.TraceNumber, 15) } func makeChangeCodeDict() map[string]*changeCode { diff --git a/addenda98_test.go b/addenda98_test.go new file mode 100644 index 000000000..cfa346aa8 --- /dev/null +++ b/addenda98_test.go @@ -0,0 +1,155 @@ +package ach + +import ( + "testing" +) + +func mockAddenda98() *Addenda98 { + addenda98 := NewAddenda98() + addenda98.ChangeCode = "C01" + addenda98.OriginalTrace = 12345 + addenda98.OriginalDFI = 9101298 + addenda98.CorrectedData = "1918171614" + addenda98.TraceNumber = 91012980000088 + + return addenda98 +} + +func TestAddenda98Parse(t *testing.T) { + addenda98 := NewAddenda98() + line := "798C01099912340000015 091012981918171614 091012980000088" + addenda98.Parse(line) + // walk the Addenda98 struct + if addenda98.recordType != "7" { + t.Errorf("expected %v got %v", "7", addenda98.recordType) + } + if addenda98.typeCode != "98" { + t.Errorf("expected %v got %v", "98", addenda98.typeCode) + } + if addenda98.ChangeCode != "C01" { + t.Errorf("expected %v got %v", "C01", addenda98.ChangeCode) + } + if addenda98.OriginalTrace != 99912340000015 { + t.Errorf("expected %v got %v", 99912340000015, addenda98.OriginalTrace) + } + if addenda98.OriginalDFI != 9101298 { + t.Errorf("expected %v got %v", 9101298, addenda98.OriginalDFI) + } + if addenda98.CorrectedData != "1918171614" { + t.Errorf("expected %v got %v", "1918171614", addenda98.CorrectedData) + } + if addenda98.TraceNumber != 91012980000088 { + t.Errorf("expected %v got %v", 91012980000088, addenda98.TraceNumber) + } +} + +func TestAddenda98String(t *testing.T) { + addenda98 := NewAddenda98() + line := "798C01099912340000015 091012981918171614 091012980000088" + addenda98.Parse(line) + + if addenda98.String() != line { + t.Errorf("\n expected: %v\n got : %v", line, addenda98.String()) + } +} + +func TestAddenda98ValidRecordType(t *testing.T) { + addenda98 := mockAddenda98() + addenda98.recordType = "63" + if err := addenda98.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddenda98ValidTypeCode(t *testing.T) { + addenda98 := mockAddenda98() + addenda98.typeCode = "05" + if err := addenda98.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddenda98ValidCorrectedData(t *testing.T) { + addenda98 := mockAddenda98() + addenda98.CorrectedData = "" + if err := addenda98.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CorrectedData" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddenda98ValidateTrue(t *testing.T) { + addenda98 := mockAddenda98() + addenda98.ChangeCode = "C11" + if err := addenda98.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ChangeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} +func TestAddenda98ValidateChangeCodeFalse(t *testing.T) { + addenda98 := mockAddenda98() + addenda98.ChangeCode = "C63" + if err := addenda98.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ChangeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddenda98OriginalTraceField(t *testing.T) { + addenda98 := mockAddenda98() + exp := "000000000012345" + if addenda98.OriginalTraceField() != exp { + t.Errorf("expected %v received %v", exp, addenda98.OriginalTraceField()) + } +} + +func TestAddenda98OriginalDFIField(t *testing.T) { + addenda98 := mockAddenda98() + exp := "09101298" + if addenda98.OriginalDFIField() != exp { + t.Errorf("expected %v received %v", exp, addenda98.OriginalDFIField()) + } +} + +func TestAddenda98CorrectedDataField(t *testing.T) { + addenda98 := mockAddenda98() + exp := "1918171614 " // 29 char + if addenda98.CorrectedDataField() != exp { + t.Errorf("expected %v received %v", exp, addenda98.CorrectedDataField()) + } +} + +func TestAddenda98TraceNumberField(t *testing.T) { + addenda98 := mockAddenda98() + exp := "091012980000088" + if addenda98.TraceNumberField() != exp { + t.Errorf("expected %v received %v", exp, addenda98.TraceNumberField()) + } +} diff --git a/addendaReturn.go b/addenda99.go similarity index 76% rename from addendaReturn.go rename to addenda99.go index 79d504cfd..5e825dc43 100644 --- a/addendaReturn.go +++ b/addenda99.go @@ -18,7 +18,7 @@ var ( returnCodeDict = map[string]*returnCode{} // Error messages specific to Return Addenda - msgAddendaReturnReturnCode = "found is not a valid return code" + msgAddenda99ReturnCode = "found is not a valid return code" ) func init() { @@ -26,8 +26,8 @@ func init() { returnCodeDict = makeReturnCodeDict() } -// AddendaReturn utilized for Notification of Change Entry (COR) and Return types. -type AddendaReturn struct { +// Addenda99 utilized for Notification of Change Entry (COR) and Return types. +type Addenda99 struct { // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string // TypeCode Addenda types code '99' @@ -37,7 +37,7 @@ type AddendaReturn struct { ReturnCode string // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, - // in the Addenda Record of an NOC, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. + // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. OriginalTrace int // DateOfDeath The field date of death is to be supplied on Entries being returned for reason of death (return reason codes R14 and R15). DateOfDeath time.Time @@ -61,99 +61,99 @@ type returnCode struct { Code, Reason, Description string } -// NewAddendaReturn returns a new AddendaReturn with default values for none exported fields -func NewAddendaReturn() *AddendaReturn { - addendaReturn := &AddendaReturn{ +// NewAddenda99 returns a new Addenda99 with default values for none exported fields +func NewAddenda99() *Addenda99 { + Addenda99 := &Addenda99{ recordType: "7", typeCode: "99", } - return addendaReturn + return Addenda99 } -// Parse takes the input record string and parses the AddendaReturn values -func (addendaReturn *AddendaReturn) Parse(record string) { +// Parse takes the input record string and parses the Addenda99 values +func (Addenda99 *Addenda99) Parse(record string) { // 1-1 Always "7" - addendaReturn.recordType = "7" + Addenda99.recordType = "7" // 2-3 Defines the specific explanation and format for the addenda information contained in the same record - addendaReturn.typeCode = record[1:3] + Addenda99.typeCode = record[1:3] // 4-6 - addendaReturn.ReturnCode = record[3:6] + Addenda99.ReturnCode = record[3:6] // 7-21 - addendaReturn.OriginalTrace = addendaReturn.parseNumField(record[6:21]) + Addenda99.OriginalTrace = Addenda99.parseNumField(record[6:21]) // 22-27, might be a date or blank - addendaReturn.DateOfDeath = addendaReturn.parseSimpleDate(record[21:27]) + Addenda99.DateOfDeath = Addenda99.parseSimpleDate(record[21:27]) // 28-35 - addendaReturn.OriginalDFI = addendaReturn.parseNumField(record[27:35]) + Addenda99.OriginalDFI = Addenda99.parseNumField(record[27:35]) // 36-79 - addendaReturn.AddendaInformation = strings.TrimSpace(record[35:79]) + Addenda99.AddendaInformation = strings.TrimSpace(record[35:79]) // 80-94 - addendaReturn.TraceNumber = addendaReturn.parseNumField(record[79:94]) + Addenda99.TraceNumber = Addenda99.parseNumField(record[79:94]) } -// String writes the AddendaReturn struct to a 94 character string -func (addendaReturn *AddendaReturn) String() string { +// String writes the Addenda99 struct to a 94 character string +func (Addenda99 *Addenda99) String() string { return fmt.Sprintf("%v%v%v%v%v%v%v%v", - addendaReturn.recordType, - addendaReturn.TypeCode(), - addendaReturn.ReturnCode, - addendaReturn.OriginalTraceField(), - addendaReturn.DateOfDeathField(), - addendaReturn.OriginalDFIField(), - addendaReturn.AddendaInformationField(), - addendaReturn.TraceNumberField(), + Addenda99.recordType, + Addenda99.TypeCode(), + Addenda99.ReturnCode, + Addenda99.OriginalTraceField(), + Addenda99.DateOfDeathField(), + Addenda99.OriginalDFIField(), + Addenda99.AddendaInformationField(), + Addenda99.TraceNumberField(), ) } -// Validate verifies NACHA rules for AddendaReturn -func (addendaReturn *AddendaReturn) Validate() error { +// Validate verifies NACHA rules for Addenda99 +func (Addenda99 *Addenda99) Validate() error { - if addendaReturn.recordType != "7" { + if Addenda99.recordType != "7" { msg := fmt.Sprintf(msgRecordType, 7) - return &FieldError{FieldName: "recordType", Value: addendaReturn.recordType, Msg: msg} + return &FieldError{FieldName: "recordType", Value: Addenda99.recordType, Msg: msg} } // @TODO Type Code should be 99. - _, ok := returnCodeDict[addendaReturn.ReturnCode] + _, ok := returnCodeDict[Addenda99.ReturnCode] if !ok { // Return Addenda requires a valid ReturnCode - return &FieldError{FieldName: "ReturnCode", Value: addendaReturn.ReturnCode, Msg: msgAddendaReturnReturnCode} + return &FieldError{FieldName: "ReturnCode", Value: Addenda99.ReturnCode, Msg: msgAddenda99ReturnCode} } return nil } // TypeCode defines the format of the underlying addenda record -func (addendaReturn *AddendaReturn) TypeCode() string { - return addendaReturn.typeCode +func (Addenda99 *Addenda99) TypeCode() string { + return Addenda99.typeCode } // OriginalTraceField returns a zero padded OriginalTrace string -func (addendaReturn *AddendaReturn) OriginalTraceField() string { - return addendaReturn.numericField(addendaReturn.OriginalTrace, 15) +func (Addenda99 *Addenda99) OriginalTraceField() string { + return Addenda99.numericField(Addenda99.OriginalTrace, 15) } // DateOfDeathField returns a space padded DateOfDeath string -func (addendaReturn *AddendaReturn) DateOfDeathField() string { +func (Addenda99 *Addenda99) DateOfDeathField() string { // Return space padded 6 characters if it is a zero value of DateOfDeath - if addendaReturn.DateOfDeath.IsZero() { - return addendaReturn.alphaField("", 6) + if Addenda99.DateOfDeath.IsZero() { + return Addenda99.alphaField("", 6) } // YYMMDD - return addendaReturn.formatSimpleDate(addendaReturn.DateOfDeath) + return Addenda99.formatSimpleDate(Addenda99.DateOfDeath) } // OriginalDFIField returns a zero padded OriginalDFI string -func (addendaReturn *AddendaReturn) OriginalDFIField() string { - return addendaReturn.numericField(addendaReturn.OriginalDFI, 8) +func (Addenda99 *Addenda99) OriginalDFIField() string { + return Addenda99.numericField(Addenda99.OriginalDFI, 8) } //AddendaInformationField returns a space padded AddendaInformation string -func (addendaReturn *AddendaReturn) AddendaInformationField() string { - return addendaReturn.alphaField(addendaReturn.AddendaInformation, 44) +func (Addenda99 *Addenda99) AddendaInformationField() string { + return Addenda99.alphaField(Addenda99.AddendaInformation, 44) } // TraceNumberField returns a zero padded traceNumber string -func (addendaReturn *AddendaReturn) TraceNumberField() string { - return addendaReturn.numericField(addendaReturn.TraceNumber, 15) +func (Addenda99 *Addenda99) TraceNumberField() string { + return Addenda99.numericField(Addenda99.TraceNumber, 15) } func makeReturnCodeDict() map[string]*returnCode { diff --git a/addenda99_test.go b/addenda99_test.go new file mode 100644 index 000000000..bf341ca44 --- /dev/null +++ b/addenda99_test.go @@ -0,0 +1,150 @@ +// Copyright 2017 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" + "time" +) + +func mockAddenda99() *Addenda99 { + addenda99 := NewAddenda99() + addenda99.ReturnCode = "R07" + addenda99.OriginalTrace = 99912340000015 + addenda99.AddendaInformation = "Authorization Revoked" + addenda99.OriginalDFI = 9101298 + + return addenda99 +} + +func TestAddenda99Parse(t *testing.T) { + addenda99 := NewAddenda99() + line := "799R07099912340000015 09101298Authorization revoked 091012980000066" + addenda99.Parse(line) + // walk the Addenda99 struct + if addenda99.recordType != "7" { + t.Errorf("expected %v got %v", "7", addenda99.recordType) + } + if addenda99.typeCode != "99" { + t.Errorf("expected %v got %v", "99", addenda99.typeCode) + } + if addenda99.ReturnCode != "R07" { + t.Errorf("expected %v got %v", "R07", addenda99.ReturnCode) + } + if addenda99.OriginalTrace != 99912340000015 { + t.Errorf("expected: %v got: %v", 99912340000015, addenda99.OriginalTrace) + } + if addenda99.DateOfDeath.IsZero() != true { + t.Errorf("expected: %v got: %v", time.Time{}, addenda99.DateOfDeath) + } + if addenda99.OriginalDFI != 9101298 { + t.Errorf("expected: %v got: %v", 9101298, addenda99.OriginalDFI) + } + if addenda99.AddendaInformation != "Authorization revoked" { + t.Errorf("expected: %v got: %v", "Authorization revoked", addenda99.AddendaInformation) + } + if addenda99.TraceNumber != 91012980000066 { + t.Errorf("expected: %v got: %v", 91012980000066, addenda99.TraceNumber) + } +} + +func TestAddenda99String(t *testing.T) { + addenda99 := NewAddenda99() + line := "799R07099912340000015 09101298Authorization revoked 091012980000066" + addenda99.Parse(line) + + if addenda99.String() != line { + t.Errorf("\n expected: %v\n got : %v", line, addenda99.String()) + } +} + +// This is not an exported function but utilized for validation +func TestAddenda99MakeReturnCodeDict(t *testing.T) { + codes := makeReturnCodeDict() + // check if known code is present + _, prs := codes["R01"] + if !prs { + t.Error("Return Code R01 was not found in the ReturnCodeDict") + } + // check if invalid code is present + _, prs = codes["ABC"] + if prs { + t.Error("Valid return for an invalid return code key") + } +} + +func TestAddenda99ValidateTrue(t *testing.T) { + addenda99 := mockAddenda99() + addenda99.ReturnCode = "R13" + if err := addenda99.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReturnCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddenda99ValidateReturnCodeFalse(t *testing.T) { + addenda99 := mockAddenda99() + addenda99.ReturnCode = "" + if err := addenda99.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReturnCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAddenda99OriginalTraceField(t *testing.T) { + addenda99 := mockAddenda99() + addenda99.OriginalTrace = 12345 + if addenda99.OriginalTraceField() != "000000000012345" { + t.Errorf("expected %v received %v", "000000000012345", addenda99.OriginalTraceField()) + } +} + +func TestAddenda99DateOfDeathField(t *testing.T) { + addenda99 := mockAddenda99() + // Check for all zeros + if addenda99.DateOfDeathField() != " " { + t.Errorf("expected %v received %v", " ", addenda99.DateOfDeathField()) + } + // Year: 1978 Month: October Day: 23 + addenda99.DateOfDeath = time.Date(1978, time.October, 23, 0, 0, 0, 0, time.UTC) + if addenda99.DateOfDeathField() != "781023" { + t.Errorf("expected %v received %v", "781023", addenda99.DateOfDeathField()) + } +} + +func TestAddenda99OriginalDFIField(t *testing.T) { + addenda99 := mockAddenda99() + exp := "09101298" + if addenda99.OriginalDFIField() != exp { + t.Errorf("expected %v received %v", exp, addenda99.OriginalDFIField()) + } +} + +func TestAddenda99AddendaInformationField(t *testing.T) { + addenda99 := mockAddenda99() + exp := "Authorization Revoked " + if addenda99.AddendaInformationField() != exp { + t.Errorf("expected %v received %v", exp, addenda99.AddendaInformationField()) + } +} + +func TestAddenda99TraceNumberField(t *testing.T) { + addenda99 := mockAddenda99() + addenda99.TraceNumber = 91012980000066 + exp := "091012980000066" + if addenda99.TraceNumberField() != exp { + t.Errorf("expected %v received %v", exp, addenda99.TraceNumberField()) + } +} diff --git a/addendaNOC_test.go b/addendaNOC_test.go deleted file mode 100644 index 92a9ded47..000000000 --- a/addendaNOC_test.go +++ /dev/null @@ -1,194 +0,0 @@ -package ach - -import ( - "testing" -) - -func mockAddendaNOC() *AddendaNOC { - aNOC := NewAddendaNOC() - aNOC.ChangeCode = "C01" - aNOC.OriginalTrace = 12345 - aNOC.OriginalDFI = 9101298 - aNOC.CorrectedData = "1918171614" - aNOC.TraceNumber = 91012980000088 - - return aNOC -} - -func TestAddendaNOCParse(t *testing.T) { - aNOC := NewAddendaNOC() - line := "798C01099912340000015 091012981918171614 091012980000088" - aNOC.Parse(line) - // walk the AddendaNOC struct - if aNOC.recordType != "7" { - t.Errorf("expected %v got %v", "7", aNOC.recordType) - } - if aNOC.typeCode != "98" { - t.Errorf("expected %v got %v", "98", aNOC.typeCode) - } - if aNOC.ChangeCode != "C01" { - t.Errorf("expected %v got %v", "C01", aNOC.ChangeCode) - } - if aNOC.OriginalTrace != 99912340000015 { - t.Errorf("expected %v got %v", 99912340000015, aNOC.OriginalTrace) - } - if aNOC.OriginalDFI != 9101298 { - t.Errorf("expected %v got %v", 9101298, aNOC.OriginalDFI) - } - if aNOC.CorrectedData != "1918171614" { - t.Errorf("expected %v got %v", "1918171614", aNOC.CorrectedData) - } - if aNOC.TraceNumber != 91012980000088 { - t.Errorf("expected %v got %v", 91012980000088, aNOC.TraceNumber) - } -} - -func TestAddendaNOCString(t *testing.T) { - aNOC := NewAddendaNOC() - line := "798C01099912340000015 091012981918171614 091012980000088" - aNOC.Parse(line) - - if aNOC.String() != line { - t.Errorf("\n expected: %v\n got : %v", line, aNOC.String()) - } -} - -func TestAddendaNOCValidRecordType(t *testing.T) { - aNOC := mockAddendaNOC() - aNOC.recordType = "63" - if err := aNOC.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestAddendaNOCValidTypeCode(t *testing.T) { - aNOC := mockAddendaNOC() - aNOC.typeCode = "05" - if err := aNOC.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "TypeCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestAddendaNOCValidCorrectedData(t *testing.T) { - aNOC := mockAddendaNOC() - aNOC.CorrectedData = "" - if err := aNOC.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "CorrectedData" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestAddendaNOCValidateTrue(t *testing.T) { - aNOC := mockAddendaNOC() - aNOC.ChangeCode = "C11" - if err := aNOC.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "ChangeCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} -func TestAddendaNOCValidateChangeCodeFalse(t *testing.T) { - aNOC := mockAddendaNOC() - aNOC.ChangeCode = "C63" - if err := aNOC.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "ChangeCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestAddendaNOCOriginalTraceField(t *testing.T) { - aNOC := mockAddendaNOC() - exp := "000000000012345" - if aNOC.OriginalTraceField() != exp { - t.Errorf("expected %v received %v", exp, aNOC.OriginalTraceField()) - } -} - -func TestAddendaNOCOriginalDFIField(t *testing.T) { - aNOC := mockAddendaNOC() - exp := "09101298" - if aNOC.OriginalDFIField() != exp { - t.Errorf("expected %v received %v", exp, aNOC.OriginalDFIField()) - } -} - -func TestAddendaNOCCorrectedDataField(t *testing.T) { - aNOC := mockAddendaNOC() - exp := "1918171614 " // 29 char - if aNOC.CorrectedDataField() != exp { - t.Errorf("expected %v received %v", exp, aNOC.CorrectedDataField()) - } -} - -func TestAddendaNOCTraceNumberField(t *testing.T) { - aNOC := mockAddendaNOC() - exp := "091012980000088" - if aNOC.TraceNumberField() != exp { - t.Errorf("expected %v received %v", exp, aNOC.TraceNumberField()) - } -} - -/*func TestAddendaNOCNewAddendaParam(t *testing.T) { - aParam := AddendaParam{ - TypeCode: "98", - ChangeCode: "C01", - OriginalTrace: "12345", - OriginalDFI: "9101298", - CorrectedData: "1918171614", - TraceNumber: "91012980000088", - } - - a, err := NewAddenda(aParam) - if err != nil { - t.Errorf("AddendaNOC from NewAddenda: %v", err) - } - aNOC, ok := a.(*AddendaNOC) - if !ok { - t.Errorf("expecting *AddendaNOC received %T ", a) - } - if aNOC.TypeCode() != aParam.TypeCode { - t.Errorf("expected %v got %v", aParam.TypeCode, aNOC.TypeCode()) - } - if aNOC.ChangeCode != aParam.ChangeCode { - t.Errorf("expected %v got %v", aParam.ChangeCode, aNOC.ChangeCode) - } - if !strings.Contains(aNOC.OriginalTraceField(), aParam.OriginalTrace) { - t.Errorf("expected %v got %v", aParam.OriginalTrace, aNOC.OriginalTrace) - } - if !strings.Contains(aNOC.OriginalDFIField(), aParam.OriginalDFI) { - t.Errorf("expected %v got %v", aParam.OriginalDFI, aNOC.OriginalDFI) - } - if aNOC.CorrectedData != aParam.CorrectedData { - t.Errorf("expected %v got %v", aParam.CorrectedData, aNOC.CorrectedData) - } - if !strings.Contains(aNOC.TraceNumberField(), aParam.TraceNumber) { - t.Errorf("expected %v got %v", aParam.TraceNumber, aNOC.TraceNumber) - } -} -*/ \ No newline at end of file diff --git a/addendaReturn_test.go b/addendaReturn_test.go deleted file mode 100644 index 9f58b6174..000000000 --- a/addendaReturn_test.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2017 The ACH Authors -// Use of this source code is governed by an Apache License -// license that can be found in the LICENSE file. - -package ach - -import ( - "testing" - "time" -) - -func mockAddendaReturn() *AddendaReturn { - addendaReturn := NewAddendaReturn() - addendaReturn.typeCode = "99" - addendaReturn.ReturnCode = "R07" - addendaReturn.OriginalTrace = 99912340000015 - addendaReturn.AddendaInformation = "Authorization Revoked" - addendaReturn.OriginalDFI = 9101298 - - return addendaReturn -} - -func TestMockAddendaReturn(t *testing.T) { - // TODO: build a mock addenda -} - -func TestAddendaReturnParse(t *testing.T) { - addendaReturn := NewAddendaReturn() - line := "799R07099912340000015 09101298Authorization revoked 091012980000066" - addendaReturn.Parse(line) - // walk the addendaReturn struct - if addendaReturn.recordType != "7" { - t.Errorf("expected %v got %v", "7", addendaReturn.recordType) - } - if addendaReturn.typeCode != "99" { - t.Errorf("expected %v got %v", "99", addendaReturn.typeCode) - } - if addendaReturn.ReturnCode != "R07" { - t.Errorf("expected %v got %v", "R07", addendaReturn.ReturnCode) - } - if addendaReturn.OriginalTrace != 99912340000015 { - t.Errorf("expected: %v got: %v", 99912340000015, addendaReturn.OriginalTrace) - } - if addendaReturn.DateOfDeath.IsZero() != true { - t.Errorf("expected: %v got: %v", time.Time{}, addendaReturn.DateOfDeath) - } - if addendaReturn.OriginalDFI != 9101298 { - t.Errorf("expected: %v got: %v", 9101298, addendaReturn.OriginalDFI) - } - if addendaReturn.AddendaInformation != "Authorization revoked" { - t.Errorf("expected: %v got: %v", "Authorization revoked", addendaReturn.AddendaInformation) - } - if addendaReturn.TraceNumber != 91012980000066 { - t.Errorf("expected: %v got: %v", 91012980000066, addendaReturn.TraceNumber) - } -} - -func TestAddendaReturnString(t *testing.T) { - addendaReturn := NewAddendaReturn() - line := "799R07099912340000015 09101298Authorization revoked 091012980000066" - addendaReturn.Parse(line) - - if addendaReturn.String() != line { - t.Errorf("\n expected: %v\n got : %v", line, addendaReturn.String()) - } -} - -// This is not an exported function but utilized for validation -func TestAddendaReturnMakeReturnCodeDict(t *testing.T) { - codes := makeReturnCodeDict() - // check if known code is present - _, prs := codes["R01"] - if !prs { - t.Error("Return Code R01 was not found in the ReturnCodeDict") - } - // check if invalid code is present - _, prs = codes["ABC"] - if prs { - t.Error("Valid return for an invalid return code key") - } -} - -func TestAddendaReturnValidateTrue(t *testing.T) { - addendaReturn := mockAddendaReturn() - addendaReturn.ReturnCode = "R13" - if err := addendaReturn.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "ReturnCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestAddendaReturnValidateReturnCodeFalse(t *testing.T) { - addendaReturn := mockAddendaReturn() - addendaReturn.ReturnCode = "" - if err := addendaReturn.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "ReturnCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -func TestAddendaReturnOriginalTraceField(t *testing.T) { - addendaReturn := mockAddendaReturn() - addendaReturn.OriginalTrace = 12345 - if addendaReturn.OriginalTraceField() != "000000000012345" { - t.Errorf("expected %v received %v", "000000000012345", addendaReturn.OriginalTraceField()) - } -} - -func TestAddendaReturnDateOfDeathField(t *testing.T) { - addendaReturn := mockAddendaReturn() - // Check for all zeros - if addendaReturn.DateOfDeathField() != " " { - t.Errorf("expected %v received %v", " ", addendaReturn.DateOfDeathField()) - } - // Year: 1978 Month: October Day: 23 - addendaReturn.DateOfDeath = time.Date(1978, time.October, 23, 0, 0, 0, 0, time.UTC) - if addendaReturn.DateOfDeathField() != "781023" { - t.Errorf("expected %v received %v", "781023", addendaReturn.DateOfDeathField()) - } -} - -func TestAddendaReturnOriginalDFIField(t *testing.T) { - addendaReturn := mockAddendaReturn() - exp := "09101298" - if addendaReturn.OriginalDFIField() != exp { - t.Errorf("expected %v received %v", exp, addendaReturn.OriginalDFIField()) - } -} - -func TestAddendaReturnAddendaInformationField(t *testing.T) { - addendaReturn := mockAddendaReturn() - exp := "Authorization Revoked " - if addendaReturn.AddendaInformationField() != exp { - t.Errorf("expected %v received %v", exp, addendaReturn.AddendaInformationField()) - } -} - -func TestAddendaReturnTraceNumberField(t *testing.T) { - addendaReturn := mockAddendaReturn() - addendaReturn.TraceNumber = 91012980000066 - exp := "091012980000066" - if addendaReturn.TraceNumberField() != exp { - t.Errorf("expected %v received %v", exp, addendaReturn.TraceNumberField()) - } -} - -/*func TestAddendaReturnNewAddendaParam(t *testing.T) { - aParam := AddendaParam{ - TypeCode: "99", - ReturnCode: "R07", - OriginalTrace: "99912340000015", - OriginalDFI: "09101298", - AddendaInfo: "Authorization Revoked", - TraceNumber: "091012980000066", - } - - a, err := NewAddenda(aParam) - if err != nil { - t.Errorf("addendaReturn from NewAddeda: %v", err) - } - addendaReturn, ok := a.(*AddendaReturn) - if !ok { - t.Errorf("expecting *AddendaReturn received %T ", a) - } - if addendaReturn.TypeCode() != aParam.TypeCode { - t.Errorf("expected %v got %v", aParam.TypeCode, addendaReturn.TypeCode()) - } - if addendaReturn.ReturnCode != aParam.ReturnCode { - t.Errorf("expected %v got %v", aParam.ReturnCode, addendaReturn.ReturnCode) - } - if !strings.Contains(addendaReturn.OriginalTraceField(), aParam.OriginalTrace) { - t.Errorf("expected %v got %v", aParam.OriginalTrace, addendaReturn.OriginalTrace) - } - if !strings.Contains(addendaReturn.OriginalDFIField(), aParam.OriginalDFI) { - t.Errorf("expected %v got %v", aParam.OriginalDFI, addendaReturn.OriginalDFI) - } - if addendaReturn.AddendaInformation != aParam.AddendaInfo { - t.Errorf("expected %v got %v", aParam.AddendaInfo, addendaReturn.AddendaInformation) - } - if !strings.Contains(addendaReturn.TraceNumberField(), aParam.TraceNumber) { - t.Errorf("expected %v got %v", aParam.TraceNumber, addendaReturn.TraceNumber) - } -} -*/ \ No newline at end of file diff --git a/addenda_internal_test.go b/addenda_internal_test.go deleted file mode 100644 index 9c601a505..000000000 --- a/addenda_internal_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2017 The ACH Authors -// Use of this source code is governed by an Apache License -// license that can be found in the LICENSE file. - -package ach - -import ( - "strings" - "testing" -) - -func mockAddenda() *Addenda { - addenda := Addenda{ - recordType: "7", - typeCode: "05", - SequenceNumber: 1, - EntryDetailSequenceNumber: 1234567, - } - return &addenda -} - - -func TestMockAddenda(t *testing.T) { - addenda := mockAddenda() - - if err := addenda.Validate(); err != nil { - t.Error("mockAddenda does not validate and will break other tests") - } - if addenda.EntryDetailSequenceNumber != 1234567 { - t.Error("EntryDetailSequenceNumber dependent default value has changed") - } -} - -func TestParseAddenda(t *testing.T) { - addendaWEB := NewAddenda() - var line = "705WEB DIEGO MAY 00010000001" - addendaWEB.Parse(line) - - r := NewReader(strings.NewReader(line)) - - //Add a new BatchWEB - r.addCurrentBatch(NewBatchWEB(mockBatchWEBHeader())) - - //Add a WEB EntryDetail - entryDetail := mockWEBEntryDetail() - - //Add an addenda to the WEB EntryDetail - entryDetail.AddAddenda(addendaWEB) - - // add the WEB entry detail to the batch - r.currentBatch.AddEntry(entryDetail) - - record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda) - - if record.recordType != "7" { - t.Errorf("RecordType Expected '7' got: %v", record.recordType) - } - if record.TypeCode() != "05" { - t.Errorf("TypeCode Expected 10 got: %v", record.TypeCode()) - } - if record.PaymentRelatedInformationField() != "WEB DIEGO MAY " { - t.Errorf("PaymentRelatedInformation Expected 'WEB DIEGO MAY ' got: %v", record.PaymentRelatedInformationField()) - } - if record.SequenceNumberField() != "0001" { - t.Errorf("SequenceNumber Expected '0001' got: %v", record.SequenceNumberField()) - } - if record.EntryDetailSequenceNumberField() != "0000001" { - t.Errorf("EntryDetailSequenceNumber Expected '0000001' got: %v", record.EntryDetailSequenceNumberField()) - } -} - -// TestAddendaString validates that a known parsed file can be return to a string of the same value -func TestAddendaString(t *testing.T) { - var line = "705WEB DIEGO MAY 00010000001" - r := NewReader(strings.NewReader(line)) - r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) - r.currentBatch.GetHeader().StandardEntryClassCode = "PPD" - r.currentBatch.AddEntry(&EntryDetail{AddendaRecordIndicator: 1}) - r.line = line - err := r.parseAddenda() - if err != nil { - t.Errorf("%T: %s", err, err) - } - record := r.currentBatch.GetEntries()[0].Addendum[0] - if record.String() != line { - t.Errorf("Strings do not match") - } -} - -func TestValidateAddendaRecordType(t *testing.T) { - addenda := mockAddenda() - addenda.recordType = "2" - if err := addenda.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } - } -} - -func TestValidateAddendaTypeCode(t *testing.T) { - addenda := mockAddenda() - addenda.typeCode = "23" - if err := addenda.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "TypeCode" { - t.Errorf("%T: %s", err, err) - } - } - } -} - -func TestAddendaFieldInclusion(t *testing.T) { - addenda := mockAddenda() - addenda.EntryDetailSequenceNumber = 0 - if err := addenda.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "EntryDetailSequenceNumber" { - t.Errorf("%T: %s", err, err) - } - } - } -} - -func TestAddendaFieldInclusionRecordType(t *testing.T) { - addenda := mockAddenda() - addenda.recordType = "" - if err := addenda.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { - t.Errorf("%T: %s", err, err) - } - } - } -} - -func TestAddendaPaymentRelatedInformationAlphaNumeric(t *testing.T) { - addenda := mockAddenda() - addenda.PaymentRelatedInformation = "®©" - if err := addenda.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "PaymentRelatedInformation" { - t.Errorf("%T: %s", err, err) - } - } - } -} - -func TestAddendaTyeCodeNil(t *testing.T) { - addenda := mockAddenda() - addenda.typeCode = "" - if err := addenda.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "TypeCode" { - t.Errorf("%T: %s", err, err) - } - } - } -} diff --git a/batch.go b/batch.go index 80c8d3aad..251d25bdb 100644 --- a/batch.go +++ b/batch.go @@ -130,7 +130,7 @@ func (batch *batch) build() error { addendaSeq := 1 for x := range entry.Addendum { // sequences don't exist in NOC or Return addenda - if a, ok := batch.entries[i].Addendum[x].(*Addenda); ok { + if a, ok := batch.entries[i].Addendum[x].(*Addenda05); ok { a.SequenceNumber = addendaSeq a.EntryDetailSequenceNumber = batch.parseNumField(batch.entries[i].TraceNumberField()[8:]) } @@ -325,7 +325,7 @@ func (batch *batch) isAddendaSequence() error { // check if sequence is assending for _, addenda := range entry.Addendum { // sequences don't exist in NOC or Return addenda - if a, ok := addenda.(*Addenda); ok { + if a, ok := addenda.(*Addenda05); ok { if a.SequenceNumber < lastSeq { msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastSeq) diff --git a/batchCCD_test.go b/batchCCD_test.go index 07ee5138f..b4806a26a 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -31,7 +31,7 @@ func mockCCDEntryDetail() *EntryDetail { func mockBatchCCD() *BatchCCD { mockBatch := NewBatchCCD(mockBatchCCDHeader()) mockBatch.AddEntry(mockCCDEntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) if err := mockBatch.Create(); err != nil { panic(err) } @@ -52,7 +52,7 @@ func TestBatchCCDHeader(t *testing.T) { func TestBatchCCDAddendumCount(t *testing.T) { mockBatch := mockBatchCCD() // Adding a second addenda to the mock entry - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "EntryAddendaCount" { @@ -83,7 +83,7 @@ func TestBatchCCDReceivingCompanyName(t *testing.T) { // verify addenda type code is 05 func TestBatchCCDAddendaTypeCode(t *testing.T) { mockBatch := mockBatchCCD() - mockBatch.GetEntries()[0].Addendum[0].(*Addenda).typeCode = "07" + mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).typeCode = "07" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "TypeCode" { @@ -112,7 +112,7 @@ func TestBatchCCDSEC(t *testing.T) { func TestBatchCCDAddendaCount(t *testing.T) { mockBatch := mockBatchCCD() - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) mockBatch.Create() if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { diff --git a/batchCOR.go b/batchCOR.go index b5ce90016..1646abf5d 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -12,8 +12,8 @@ type BatchCOR struct { } var msgBatchCORAmount = "debit:%v credit:%v entry detail amount fields must be zero for SEC type COR" -var msgBatchCORAddenda = "found and 1 AddendaNOC is required for SEC Type COR" -var msgBatchCORAddendaType = "%T found where AddendaNOC is required for SEC type NOC" +var msgBatchCORAddenda = "found and 1 Addenda98 is required for SEC Type COR" +var msgBatchCORAddendaType = "%T found where Addenda98 is required for SEC type NOC" // NewBatchCOR returns a *BatchCOR func NewBatchCOR(bh *BatchHeader) *BatchCOR { @@ -31,7 +31,7 @@ func (batch *BatchCOR) Validate() error { } // Add configuration based validation for this type. // Web can have up to one addenda per entry record - if err := batch.isAddendaNOC(); err != nil { + if err := batch.isAddenda98(); err != nil { return err } @@ -64,21 +64,21 @@ func (batch *BatchCOR) Create() error { return nil } -// isAddendaNOC verifies that a AddendaNoc exists for each EntryDetail and is Validated -func (batch *BatchCOR) isAddendaNOC() error { +// isAddenda98 verifies that a Addenda98 exists for each EntryDetail and is Validated +func (batch *BatchCOR) isAddenda98() error { for _, entry := range batch.entries { // Addenda type must be equal to 1 if len(entry.Addendum) != 1 { return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Addendum", Msg: msgBatchCORAddenda} } - // Addenda type assertion must be AddendaNOC - aNOC, ok := entry.Addendum[0].(*AddendaNOC) + // Addenda type assertion must be Addenda98 + addenda98, ok := entry.Addendum[0].(*Addenda98) if !ok { msg := fmt.Sprintf(msgBatchCORAddendaType, entry.Addendum[0]) return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Addendum", Msg: msg} } - // AddendaNOC must be Validated - if err := aNOC.Validate(); err != nil { + // Addenda98 must be Validated + if err := addenda98.Validate(); err != nil { // convert the field error in to a batch error for a consistent api if e, ok := err.(*FieldError); ok { return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} diff --git a/batchCOR_test.go b/batchCOR_test.go index 6754f5d8d..8db3c563a 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -33,7 +33,7 @@ func mockCOREntryDetail() *EntryDetail { func mockBatchCOR() *BatchCOR { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddendaNOC()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda98()) if err := mockBatch.Create(); err != nil { panic(err) } @@ -67,7 +67,7 @@ func TestBatchCORSEC(t *testing.T) { func TestBatchCORAddendumCountTwo(t *testing.T) { mockBatch := mockBatchCOR() // Adding a second addenda to the mock entry - mockBatch.GetEntries()[0].AddAddenda(mockAddendaNOC()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda98()) if err := mockBatch.Create(); err != nil { // fmt.Printf("err: %v \n", err) @@ -95,11 +95,11 @@ func TestBatchCORAddendaCountZero(t *testing.T) { } } -// check that Addendum is of type AddendaNOC +// check that Addendum is of type Addenda98 func TestBatchCORAddendaType(t *testing.T) { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) if err := mockBatch.Create(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "Addendum" { @@ -113,7 +113,7 @@ func TestBatchCORAddendaType(t *testing.T) { func TestBatchCORAddendaTypeCode(t *testing.T) { mockBatch := mockBatchCOR() - mockBatch.GetEntries()[0].Addendum[0].(*AddendaNOC).typeCode = "07" + mockBatch.GetEntries()[0].Addendum[0].(*Addenda98).typeCode = "07" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "TypeCode" { diff --git a/batchPPD_test.go b/batchPPD_test.go index d8566d05a..1e140bc20 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -108,7 +108,7 @@ func TestBatchPPDCreate(t *testing.T) { func TestBatchPPDTypeCode(t *testing.T) { mockBatch := mockBatchPPD() // change an addendum to an invalid type code - a := mockAddenda() + a := mockAddenda05() a.typeCode = "63" mockBatch.GetEntries()[0].AddAddenda(a) mockBatch.Create() diff --git a/batchTEL_test.go b/batchTEL_test.go index fd6a9b5be..a1dbc2e38 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -64,7 +64,7 @@ func TestBatchTELCreate(t *testing.T) { func TestBatchTELAddendaCount(t *testing.T) { mockBatch := mockBatchTEL() // TEL can not have an addendum - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) mockBatch.Create() if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { diff --git a/batchWEB_test.go b/batchWEB_test.go index a5f53fe3d..6a704c303 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -28,7 +28,7 @@ func mockWEBEntryDetail() *EntryDetail { func mockBatchWEB() *BatchWEB { mockBatch := NewBatchWEB(mockBatchWEBHeader()) mockBatch.AddEntry(mockWEBEntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) if err := mockBatch.Create(); err != nil { panic(err) } @@ -40,7 +40,7 @@ func mockBatchWEB() *BatchWEB { func TestBatchWebAddenda(t *testing.T) { mockBatch := mockBatchWEB() // mock batch already has one addenda. Creating two addenda should error - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) if err := mockBatch.Create(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "AddendaCount" { @@ -71,7 +71,7 @@ func TestBatchWebIndividualNameRequired(t *testing.T) { // verify addenda type code is 05 func TestBatchWEBAddendaTypeCode(t *testing.T) { mockBatch := mockBatchWEB() - mockBatch.GetEntries()[0].Addendum[0].(*Addenda).typeCode = "07" + mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).typeCode = "07" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "TypeCode" { diff --git a/batch_test.go b/batch_test.go index 0b7dbfdca..2837a28e3 100644 --- a/batch_test.go +++ b/batch_test.go @@ -118,8 +118,8 @@ func TestBatchDNEMismatch(t *testing.T) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) ed := mockBatch.GetEntries()[0] - ed.AddAddenda(mockAddenda()) - ed.AddAddenda(mockAddenda()) + ed.AddAddenda(mockAddenda05()) + ed.AddAddenda(mockAddenda05()) mockBatch.build() mockBatch.GetHeader().OriginatorStatusCode = 1 @@ -153,7 +153,7 @@ func TestBatchEntryCountEquality(t *testing.T) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) e := mockEntryDetail() - a := mockAddenda() + a := mockAddenda05() e.AddAddenda(a) mockBatch.AddEntry(e) if err := mockBatch.build(); err != nil { @@ -174,7 +174,7 @@ func TestBatchEntryCountEquality(t *testing.T) { func TestBatchAddendaIndicator(t *testing.T) { mockBatch := mockBatch() - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) mockBatch.GetEntries()[0].AddendaRecordIndicator = 0 mockBatch.GetControl().EntryAddendaCount = 2 if err := mockBatch.verify(); err != nil { @@ -191,12 +191,12 @@ func TestBatchAddendaIndicator(t *testing.T) { func TestBatchIsAddendaSeqAscending(t *testing.T) { mockBatch := mockBatch() ed := mockBatch.GetEntries()[0] - ed.AddAddenda(mockAddenda()) - ed.AddAddenda(mockAddenda()) + ed.AddAddenda(mockAddenda05()) + ed.AddAddenda(mockAddenda05()) mockBatch.build() - mockBatch.GetEntries()[0].Addendum[0].(*Addenda).SequenceNumber = 2 - mockBatch.GetEntries()[0].Addendum[1].(*Addenda).SequenceNumber = 1 + mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).SequenceNumber = 2 + mockBatch.GetEntries()[0].Addendum[1].(*Addenda05).SequenceNumber = 1 if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "SequenceNumber" { @@ -228,12 +228,12 @@ func TestBatchIsSequenceAscending(t *testing.T) { func TestBatchAddendaTraceNumber(t *testing.T) { mockBatch := mockBatch() - mockBatch.GetEntries()[0].AddAddenda(mockAddenda()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) } - mockBatch.GetEntries()[0].Addendum[0].(*Addenda).EntryDetailSequenceNumber = 99 + mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).EntryDetailSequenceNumber = 99 if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "TraceNumber" { @@ -261,7 +261,7 @@ func TestBatchCategory(t *testing.T) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch entry := mockEntryDetail() - entry.AddAddenda(mockAddendaReturn()) + entry.AddAddenda(mockAddenda99()) mockBatch.AddEntry(entry) if err := mockBatch.build(); err != nil { @@ -269,7 +269,7 @@ func TestBatchCategory(t *testing.T) { } if mockBatch.Category() != CategoryReturn { - t.Errorf("AddendaReturn added to batch and category is %s", mockBatch.Category()) + t.Errorf("Addenda99 added to batch and category is %s", mockBatch.Category()) } } @@ -277,7 +277,7 @@ func TestBatchCategoryForwardReturn(t *testing.T) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch entry := mockEntryDetail() - entry.AddAddenda(mockAddendaReturn()) + entry.AddAddenda(mockAddenda99()) entry.TraceNumber = entry.TraceNumber + 10 mockBatch.AddEntry(entry) if err := mockBatch.build(); err != nil { diff --git a/entryDetail.go b/entryDetail.go index e33f4f6f2..77a354b1b 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -206,21 +206,17 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.AddendaRecordIndicator = 1 // checks to make sure that we only have either or, not both switch addenda.(type) { - case *AddendaReturn: + case *Addenda99: ed.Category = CategoryReturn ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum - case *AddendaNOC: + case *Addenda98: ed.Category = CategoryNOC ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum - case *Addenda05: - ed.Category = CategoryForward - ed.Addendum = nil - ed.Addendum = append(ed.Addendum, addenda) - return ed.Addendum + // default is current *Addenda05 default: ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index 72f94a463..46194a552 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -22,19 +22,6 @@ func mockEntryDetail() *EntryDetail { return entry } -func mockEntryDemandDebit() *EntryDetail { - entry := NewEntryDetail() - entry.TransactionCode = 27 - entry.SetRDFI(102001017) - entry.DFIAccountNumber = "5343121" - entry.Amount = 17500 - entry.IndividualName = "Robert Smith" - entry.SetTraceNumber(mockBatchHeader().ODFIIdentification, 1) - entry.IdentificationNumber = "ABC##jvkdjfuiwn" - entry.Category = CategoryForward - return entry -} - func TestMockEntryDetail(t *testing.T) { entry := mockEntryDetail() if err := entry.Validate(); err != nil { @@ -303,28 +290,28 @@ func TestEDFieldInclusionTraceNumber(t *testing.T) { } } -func TestEDAddAddendaAddendaReturn(t *testing.T) { +func TestEDAddAddendaAddenda99(t *testing.T) { entry := mockEntryDetail() - entry.AddAddenda(mockAddendaReturn()) + entry.AddAddenda(mockAddenda99()) if entry.Category != CategoryReturn { - t.Error("AddendaReturn added and isReturn is false") + t.Error("Addenda99 added and isReturn is false") } if entry.AddendaRecordIndicator != 1 { - t.Error("AddendaReturn added and record indicator is not 1") + t.Error("Addenda99 added and record indicator is not 1") } } -func TestEDAddAddendaAddendaReturnTwice(t *testing.T) { +func TestEDAddAddendaAddenda99Twice(t *testing.T) { entry := mockEntryDetail() - entry.AddAddenda(mockAddendaReturn()) - entry.AddAddenda(mockAddendaReturn()) + entry.AddAddenda(mockAddenda99()) + entry.AddAddenda(mockAddenda99()) if entry.Category != CategoryReturn { - t.Error("AddendaReturn added and Category is not CategoryReturn") + t.Error("Addenda99 added and Category is not CategoryReturn") } if len(entry.Addendum) != 1 { - t.Error("AddendaReturn added and isReturn is false") + t.Error("Addenda99 added and isReturn is false") } } diff --git a/file.go b/file.go index 7d944aa8a..5021e7a0b 100644 --- a/file.go +++ b/file.go @@ -33,6 +33,7 @@ const ( web = "WEB" ccd = "CCD" cor = "COR" + tel = "TEL" ) // Errors strings specific to parsing a Batch container diff --git a/file_test.go b/file_test.go index 83cd82002..bf4b5828c 100644 --- a/file_test.go +++ b/file_test.go @@ -180,7 +180,7 @@ func TestFileReturnEntries(t *testing.T) { // create or copy the entry to be returned record entry := mockEntryDetail() // Add the addenda return with appropriate ReturnCode and addenda information - entry.AddAddenda(mockAddendaReturn()) + entry.AddAddenda(mockAddenda99()) // create or copy the previous batch header of the item being returned batchHeader := mockBatchHeader() // create or copy the batch to be returned diff --git a/reader.go b/reader.go index ea707e7f8..eda7129eb 100644 --- a/reader.go +++ b/reader.go @@ -234,15 +234,29 @@ func (r *Reader) parseAddenda() error { entry := r.currentBatch.GetEntries()[entryIndex] if entry.AddendaRecordIndicator == 1 { - // Passing TypeCode type into NewAddenda creates a Addendumer of type copde type. - - addenda05 := NewAddenda05() - - addenda05.Parse(r.line) - if err := addenda05.Validate(); err != nil { - return r.error(err) + switch r.line[1:3] { + case "05": + addenda05 := NewAddenda05() + addenda05.Parse(r.line) + if err := addenda05.Validate(); err != nil { + return r.error(err) + } + r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda05) + case "98": + addenda98 := NewAddenda98() + addenda98.Parse(r.line) + if err := addenda98.Validate(); err != nil { + return r.error(err) + } + r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda98) + case "99": + addenda99 := NewAddenda99() + addenda99.Parse(r.line) + if err := addenda99.Validate(); err != nil { + return r.error(err) + } + r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda99) } - r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda05) } else { msg := fmt.Sprintf(msgBatchAddendaIndicator) return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) diff --git a/reader_test.go b/reader_test.go index 40fa398cb..8355a5c01 100644 --- a/reader_test.go +++ b/reader_test.go @@ -8,6 +8,8 @@ import ( "os" "strings" "testing" + "bytes" + "fmt" ) func TestParseError(t *testing.T) { @@ -246,11 +248,11 @@ func TestFileEntryDetail(t *testing.T) { } } -// TestFileAddenda validation error populates through the reader -func TestFileAddenda(t *testing.T) { +// TestFileAddenda05 validation error populates through the reader +func TestFileAddenda05(t *testing.T) { bh := mockBatchHeader() ed := mockEntryDetail() - addenda := mockAddenda() + addenda := mockAddenda05() addenda.SequenceNumber = 0 ed.AddAddenda(addenda) line := bh.String() + "\n" + ed.String() + "\n" + ed.Addendum[0].String() @@ -269,9 +271,58 @@ func TestFileAddenda(t *testing.T) { } } +func TestFileAddenda98(t *testing.T) { + bh := mockBatchHeader() + ed := mockEntryDetail() + addenda := mockAddenda98() + + addenda.TraceNumber = 0000001 + addenda.ChangeCode = "C10" + addenda.CorrectedData = "ACME One Corporation" + ed.AddAddenda(addenda) + line := bh.String() + "\n" + ed.String() + "\n" + ed.Addendum[0].String() + r := NewReader(strings.NewReader(line)) + _, err := r.Read() + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestFileAddenda99(t *testing.T) { + bh := mockBatchHeader() + ed := mockEntryDetail() + addenda := mockAddenda99() + addenda.TraceNumber = 0000001 + addenda.ReturnCode = "R02" + ed.AddAddenda(addenda) + line := bh.String() + "\n" + ed.String() + "\n" + ed.Addendum[0].String() + r := NewReader(strings.NewReader(line)) + _, err := r.Read() + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + + // TestFileAddendaOutsideBatch validation error populates through the reader func TestFileAddendaOutsideBatch(t *testing.T) { - addenda := mockAddenda() + addenda := mockAddenda05() r := NewReader(strings.NewReader(addenda.String())) _, err := r.Read() if err != nil { @@ -291,7 +342,7 @@ func TestFileAddendaOutsideBatch(t *testing.T) { func TestFileAddendaNoIndicator(t *testing.T) { bh := mockBatchHeader() ed := mockEntryDetail() - addenda := mockAddenda() + addenda := mockAddenda05() line := bh.String() + "\n" + ed.String() + "\n" + addenda.String() r := NewReader(strings.NewReader(line)) _, err := r.Read() @@ -387,10 +438,8 @@ func TestFileAddBatchValidation(t *testing.T) { } } -/** func TestFileHeaderExists(t *testing.T) { file := mockFilePPD() - file.SetHeader(FileHeader{}) buf := new(bytes.Buffer) w := NewWriter(buf) w.Write(file) @@ -412,7 +461,6 @@ func TestFileHeaderExists(t *testing.T) { fmt.Println(f.Header.String()) } } -**/ // TestFileLongErr Batch Header Service Class is 000 which does not validate func TestFileLongErr(t *testing.T) { @@ -430,7 +478,7 @@ func TestFileLongErr(t *testing.T) { func TestFileAddendaOutsideEntry(t *testing.T) { bh := mockBatchHeader() - addenda := mockAddenda() + addenda := mockAddenda05() line := bh.String() + "\n" + addenda.String() r := NewReader(strings.NewReader(line)) _, err := r.Read() diff --git a/recordParams_test.go b/record_test.go similarity index 95% rename from recordParams_test.go rename to record_test.go index 5e24b121a..08806a090 100644 --- a/recordParams_test.go +++ b/record_test.go @@ -87,11 +87,8 @@ func TestBuildFile(t *testing.T) { entry := mockPPDEntryDetail() // To add one or more optional addenda records for an entry - addendaPPD := NewAddenda05() addendaPPD.PaymentRelatedInformation = "Currently string needs ASC X12 Interchange Control Structures" - addendaPPD.SequenceNumber = 1 - addendaPPD.EntryDetailSequenceNumber = 1234567 // Add the addenda record to the detail entry entry.AddAddenda(addendaPPD) @@ -119,11 +116,8 @@ func TestBuildFile(t *testing.T) { entry = mockWEBEntryDetail() - addendaWEB := NewAddenda05() addendaWEB.PaymentRelatedInformation = "Monthly Membership Subscription" - addendaWEB.SequenceNumber = 1 - addendaWEB.EntryDetailSequenceNumber = 1234568 // Add the addenda record to the detail entry entry.AddAddenda(addendaWEB) diff --git a/writer_test.go b/writer_test.go index bb5d934d1..b2e14fd79 100644 --- a/writer_test.go +++ b/writer_test.go @@ -9,7 +9,7 @@ import ( func TestPPDWrite(t *testing.T) { file := NewFile().SetHeader(mockFileHeader()) entry := mockEntryDetail() - entry.AddAddenda(mockAddenda()) + entry.AddAddenda(mockAddenda05()) batch := NewBatchPPD(mockBatchPPDHeader()) batch.SetHeader(mockBatchHeader()) batch.AddEntry(entry) From 3ac9f7e44a2de545a7d14c0df7c0d279930bd2a6 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 20 Apr 2018 11:47:04 -0400 Subject: [PATCH 0057/1694] #164 Remove Addenda Param # 164 Remove Addenda Param --- example/simple-file-creation/main.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index 2417c8511..74e7a6ede 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -45,8 +45,9 @@ func main() { entry.Category = ach.CategoryForward // To add one or more optional addenda records for an entry - addenda, _ := ach.NewAddenda(ach.AddendaParam{ - PaymentRelatedInfo: "bonus pay for amazing work on #OSS"}) + + addenda := ach.NewAddenda05() + addenda.PaymentRelatedInformation = "bonus pay for amazing work on #OSS" entry.AddAddenda(addenda) // Entries are added to batches like so: @@ -90,15 +91,15 @@ func main() { entry2.DiscretionaryData = "R" entry2.Category = ach.CategoryForward - addenda2, _ := ach.NewAddenda(ach.AddendaParam{ - PaymentRelatedInfo: "Monthly Membership Subscription"}) + // To add one or more optional addenda records for an entry + addenda2 := ach.NewAddenda05() + addenda2.PaymentRelatedInformation = "Monthly Membership Subscription" + entry2.AddAddenda(addenda2) // add the entry to the batch - entry2.AddAddenda(addenda2) + batch2.AddEntry(entry2) // Create and add the second batch - - batch2.AddEntry(entry2) if err := batch2.Create(); err != nil { fmt.Printf("%T: %s", err, err) } From 528a01444719ef34e9e9761f8b2ec1e8a3168a9b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 24 Apr 2018 19:22:14 -0400 Subject: [PATCH 0058/1694] #170 RTN int to string RTN int to string --- batch.go | 10 ++++++++-- batchCCD_test.go | 2 +- batchCOR_test.go | 2 +- batchControl.go | 8 ++++---- batchControl_internal_test.go | 10 +++++----- batchHeader.go | 8 ++++---- batchHeader_internal_test.go | 4 ++-- batchPPD_test.go | 6 +++--- batchTEL_test.go | 2 +- batchWEB_test.go | 2 +- batch_test.go | 4 ++-- converters.go | 14 ++++++++++++++ entryDetail.go | 4 ++-- example/ach-ppd-write/main.go | 9 ++++----- example/simple-file-creation/main.go | 15 +++++++-------- fileHeader.go | 24 ++++++++++++++++-------- fileHeader_internal_test.go | 14 ++++++++------ reader_test.go | 6 ++++-- 18 files changed, 87 insertions(+), 57 deletions(-) diff --git a/batch.go b/batch.go index 251d25bdb..95bf47076 100644 --- a/batch.go +++ b/batch.go @@ -2,8 +2,8 @@ package ach import ( "fmt" - "strconv" "strings" + "strconv" ) // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries @@ -122,8 +122,14 @@ func (batch *batch) build() error { if err != nil { return err } + + batchHeaderODFI, err := strconv.Atoi(batch.header.ODFIIdentificationField() [:8]) + if err != nil { + return err + } + // Add a sequenced TraceNumber if one is not already set. Have to keep original trance number Return and NOC entries - if currentTraceNumberODFI != batch.header.ODFIIdentification { + if currentTraceNumberODFI != batchHeaderODFI { batch.entries[i].SetTraceNumber(batch.header.ODFIIdentification, seq) } seq++ diff --git a/batchCCD_test.go b/batchCCD_test.go index b4806a26a..175111578 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -11,7 +11,7 @@ func mockBatchCCDHeader() *BatchHeader { bh.CompanyName = "Your Company, inc" bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "Vndr Pay" - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } diff --git a/batchCOR_test.go b/batchCOR_test.go index 8db3c563a..d56f12aac 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -13,7 +13,7 @@ func mockBatchCORHeader() *BatchHeader { bh.CompanyName = "Your Company, inc" bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "Vndr Pay" - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } diff --git a/batchControl.go b/batchControl.go index 74a016f73..35399e53c 100644 --- a/batchControl.go +++ b/batchControl.go @@ -56,7 +56,7 @@ type BatchControl struct { // Reserved for the future - Blank, 6 characters long reserved string // OdfiIdentification the routing number is used to identify the DFI originating entries within a given branch. - ODFIIdentification int + ODFIIdentification string // BatchNumber this number is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number // in the Batch Header Record and the Batch Control Record is the same, @@ -90,7 +90,7 @@ func (bc *BatchControl) Parse(record string) { // 74-79 Always blank (just fill with spaces) bc.reserved = " " // 80-87 This is the same as the "ODFI identification" field in previous Batch Header Record - bc.ODFIIdentification = bc.parseNumField(record[79:87]) + bc.ODFIIdentification = bc.parseStringField(record[79:87]) // 88-94 This is the same as the "Batch number" field in previous Batch Header Record bc.BatchNumber = bc.parseNumField(record[87:94]) } @@ -156,7 +156,7 @@ func (bc *BatchControl) fieldInclusion() error { if bc.ServiceClassCode == 0 { return &FieldError{FieldName: "ServiceClassCode", Value: strconv.Itoa(bc.ServiceClassCode), Msg: msgFieldInclusion} } - if bc.ODFIIdentification == 0 { + if bc.ODFIIdentification == "000000000" { return &FieldError{FieldName: "ODFIIdentification", Value: bc.ODFIIdentificationField(), Msg: msgFieldInclusion} } return nil @@ -194,7 +194,7 @@ func (bc *BatchControl) MessageAuthenticationCodeField() string { // ODFIIdentificationField get the odfi number zero padded func (bc *BatchControl) ODFIIdentificationField() string { - return bc.numericField(bc.ODFIIdentification, 8) + return bc.stringField(bc.ODFIIdentification, 8) } // BatchNumberField gets a string of the batch number zero padded diff --git a/batchControl_internal_test.go b/batchControl_internal_test.go index e6fa7cbf7..be9a3039e 100644 --- a/batchControl_internal_test.go +++ b/batchControl_internal_test.go @@ -13,7 +13,7 @@ func mockBatchControl() *BatchControl { bc := NewBatchControl() bc.ServiceClassCode = 220 bc.CompanyIdentification = "123456789" - bc.ODFIIdentification = 6200001 + bc.ODFIIdentification = "6200001" return bc } @@ -28,7 +28,7 @@ func TestMockBatchControl(t *testing.T) { if bc.CompanyIdentification != "123456789" { t.Error("CompanyIdentification depedendent default value has changed") } - if bc.ODFIIdentification != 6200001 { + if bc.ODFIIdentification != "6200001" { t.Error("ODFIIdentification depedendent default value has changed") } } @@ -41,7 +41,7 @@ func TestParseBatchControl(t *testing.T) { bh := BatchHeader{BatchNumber: 1, ServiceClassCode: 225, CompanyIdentification: "origid", - ODFIIdentification: 7640125} + ODFIIdentification: "7640125"} r.addCurrentBatch(NewBatchPPD(&bh)) r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: 5320001, TraceNumber: 76401255655291}) @@ -93,7 +93,7 @@ func TestBCString(t *testing.T) { bh := BatchHeader{BatchNumber: 1, ServiceClassCode: 225, CompanyIdentification: "origid", - ODFIIdentification: 7640125} + ODFIIdentification: "7640125"} r.addCurrentBatch(NewBatchPPD(&bh)) r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: 5320001, TraceNumber: 76401255655291}) @@ -194,7 +194,7 @@ func TestBCFieldInclusionServiceClassCode(t *testing.T) { func TestBCFieldInclusionODFIIdentification(t *testing.T) { bc := mockBatchControl() - bc.ODFIIdentification = 0 + bc.ODFIIdentification = "000000000" if err := bc.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.Msg != msgFieldInclusion { diff --git a/batchHeader.go b/batchHeader.go index 65d0580e2..9a9ff318f 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -88,7 +88,7 @@ type BatchHeader struct { OriginatorStatusCode int //ODFIIdentification First 8 digits of the originating DFI transit routing number - ODFIIdentification int + ODFIIdentification string // BatchNumber is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number @@ -146,7 +146,7 @@ func (bh *BatchHeader) Parse(record string) { bh.OriginatorStatusCode = bh.parseNumField(record[78:79]) // 80-87 Your ODFI's routing number without the last digit. The last digit is simply a // checksum digit, which is why it is not necessary - bh.ODFIIdentification = bh.parseNumField(record[79:87]) + bh.ODFIIdentification = bh.parseStringField(record[79:87]) // 88-94 Sequential number of this Batch Header Record // For example, put "1" if this is the first Batch Header Record in the file bh.BatchNumber = bh.parseNumField(record[87:94]) @@ -226,7 +226,7 @@ func (bh *BatchHeader) fieldInclusion() error { if bh.CompanyEntryDescription == "" { return &FieldError{FieldName: "CompanyEntryDescription", Value: bh.CompanyEntryDescription, Msg: msgFieldInclusion} } - if bh.ODFIIdentification == 0 { + if bh.ODFIIdentification == "000000000" { return &FieldError{FieldName: "ODFIIdentification", Value: bh.ODFIIdentificationField(), Msg: msgFieldInclusion} } return nil @@ -264,7 +264,7 @@ func (bh *BatchHeader) EffectiveEntryDateField() string { // ODFIIdentificationField get the odfi number zero padded func (bh *BatchHeader) ODFIIdentificationField() string { - return bh.numericField(bh.ODFIIdentification, 8) + return bh.stringField(bh.ODFIIdentification, 8) } // BatchNumberField get the batch number zero padded diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index cab77c8a9..938741965 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -16,7 +16,7 @@ func mockBatchHeader() *BatchHeader { bh.CompanyName = "ACME Corporation" bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "PAYROLL" - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } @@ -40,7 +40,7 @@ func TestMockBatchHeader(t *testing.T) { if bh.CompanyEntryDescription != "PAYROLL" { t.Error("CompanyEntryDescription dependent default value has changed") } - if bh.ODFIIdentification != 6200001 { + if bh.ODFIIdentification != "6200001" { t.Error("ODFIIdentification dependent default value has changed") } } diff --git a/batchPPD_test.go b/batchPPD_test.go index 1e140bc20..d9158643f 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -17,7 +17,7 @@ func mockBatchPPDHeader() *BatchHeader { bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } @@ -42,7 +42,7 @@ func mockBatchPPDHeader2() *BatchHeader { bh.StandardEntryClassCode = "PPD" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = 109991234 + bh.ODFIIdentification = "109991234" return bh } @@ -139,7 +139,7 @@ func TestBatchCompanyIdentification(t *testing.T) { func TestBatchODFIIDMismatch(t *testing.T) { mockBatch := mockBatchPPD() - mockBatch.GetControl().ODFIIdentification = 987654321 + mockBatch.GetControl().ODFIIdentification = "987654321" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "ODFIIdentification" { diff --git a/batchTEL_test.go b/batchTEL_test.go index a1dbc2e38..7c90e1b66 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -11,7 +11,7 @@ func mockBatchTELHeader() *BatchHeader { bh.CompanyName = "Your Company, inc" bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "Vndr Pay" - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } diff --git a/batchWEB_test.go b/batchWEB_test.go index 6a704c303..3e495da81 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -9,7 +9,7 @@ func mockBatchWEBHeader() *BatchHeader { bh.CompanyName = "Your Company, inc" bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "Online Order" - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } diff --git a/batch_test.go b/batch_test.go index 2837a28e3..caabd172e 100644 --- a/batch_test.go +++ b/batch_test.go @@ -25,7 +25,7 @@ func mockBatchInvalidSECHeader() *BatchHeader { bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } @@ -137,7 +137,7 @@ func TestBatchDNEMismatch(t *testing.T) { func TestBatchTraceNumberNotODFI(t *testing.T) { mockBatch := mockBatch() - mockBatch.GetEntries()[0].SetTraceNumber(12345678, 1) + mockBatch.GetEntries()[0].SetTraceNumber("12345678", 1) if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "ODFIIdentificationField" { diff --git a/converters.go b/converters.go index 0f74b2255..9dce6b9f6 100644 --- a/converters.go +++ b/converters.go @@ -18,6 +18,11 @@ func (c *converters) parseNumField(r string) (s int) { return s } +func (c *converters) parseStringField(r string) (s string) { + s = strings.TrimSpace(r) + return s +} + // formatSimpleDate takes a time.Time and returns a string of YYMMDD func (c *converters) formatSimpleDate(t time.Time) string { return t.Format("060102") @@ -62,3 +67,12 @@ func (c *converters) numericField(n int, max uint) string { s = strings.Repeat("0", int(max-ln)) + s return s } + +func (c *converters) stringField(s string, max uint) string { + ln := uint(len(s)) + if ln > max { + return s[ln-max:] + } + s = strings.Repeat("0", int(max-ln)) + s + return s +} \ No newline at end of file diff --git a/entryDetail.go b/entryDetail.go index 77a354b1b..70b7ae7c3 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -233,8 +233,8 @@ func (ed *EntryDetail) SetRDFI(rdfi int) *EntryDetail { } // SetTraceNumber takes first 8 digits of ODFI and concatenates a sequence number onto the TraceNumber -func (ed *EntryDetail) SetTraceNumber(ODFIIdentification int, seq int) { - trace := ed.numericField(ODFIIdentification, 8) + ed.numericField(seq, 7) +func (ed *EntryDetail) SetTraceNumber(ODFIIdentification string, seq int) { + trace := ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7) ed.TraceNumber = ed.parseNumField(trace) } diff --git a/example/ach-ppd-write/main.go b/example/ach-ppd-write/main.go index 5c0a838cb..451ce829a 100644 --- a/example/ach-ppd-write/main.go +++ b/example/ach-ppd-write/main.go @@ -3,7 +3,6 @@ package main import ( "log" "os" - "strconv" "time" "github.com/moov-io/ach" @@ -16,8 +15,8 @@ func main() { // Set originator bank ODFI and destination Operator for the financial institution // this is the funding/receiving source of the transfer fh := ach.NewFileHeader() - fh.ImmediateDestination = 9876543210 // A blank space followed by your ODFI's transit/routing number - fh.ImmediateOrigin = 1234567890 // Organization or Company FED ID usually 1 and FEIN/SSN. Assigned by your ODFI + fh.ImmediateDestination = "9876543210" // A blank space followed by your ODFI's transit/routing number + fh.ImmediateOrigin = "1234567890" // Organization or Company FED ID usually 1 and FEIN/SSN. Assigned by your ODFI fh.FileCreationDate = time.Now() // Todays Date fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name" @@ -26,11 +25,11 @@ func main() { bh := ach.NewBatchHeader() bh.ServiceClassCode = 220 // ACH credit pushes money out, 225 debits/pulls money in. bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver - bh.CompanyIdentification = strconv.Itoa(fh.ImmediateOrigin) + bh.CompanyIdentification = fh.ImmediateOrigin bh.StandardEntryClassCode = "PPD" // Consumer destination vs Company CCD bh.CompanyEntryDescription = "REG.SALARY" // will be on receiving accounts statement bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) - bh.ODFIIdentification = 12345678 // first 8 digits of your bank account + bh.ODFIIdentification = "12345678" // first 8 digits of your bank account // Identifies the receivers account information // can be multiple entry's per batch diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index 74e7a6ede..e920f73ba 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -5,15 +5,14 @@ import ( "os" "github.com/moov-io/ach" - "strconv" "time" ) func main() { // To create a file fh := ach.NewFileHeader() - fh.ImmediateDestination = 9876543210 - fh.ImmediateOrigin = 1234567890 + fh.ImmediateDestination = "987654321" + fh.ImmediateOrigin = "123456789" fh.FileCreationDate = time.Now() fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name" @@ -25,11 +24,11 @@ func main() { bh := ach.NewBatchHeader() bh.ServiceClassCode = 200 bh.CompanyName = "Your Company" - bh.CompanyIdentification = strconv.Itoa(file.Header.ImmediateOrigin) + bh.CompanyIdentification = file.Header.ImmediateOrigin bh.StandardEntryClassCode = "PPD" bh.CompanyEntryDescription = "Trans. Description" bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) - bh.ODFIIdentification = 123456789 + bh.ODFIIdentification = "12345678" batch, _ := ach.NewBatch(bh) @@ -37,7 +36,7 @@ func main() { entry := ach.NewEntryDetail() entry.TransactionCode = 22 entry.SetRDFI(9101298) - entry.DFIAccountNumber = "123456789" + entry.DFIAccountNumber = "999555121" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" entry.SetTraceNumber(bh.ODFIIdentification, 1) @@ -69,11 +68,11 @@ func main() { bh2 := ach.NewBatchHeader() bh2.ServiceClassCode = 220 bh2.CompanyName = "Your Company" - bh2.CompanyIdentification = strconv.Itoa(file.Header.ImmediateOrigin) + bh2.CompanyIdentification = file.Header.ImmediateOrigin bh2.StandardEntryClassCode = "WEB" bh2.CompanyEntryDescription = "Subscr" bh2.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) - bh2.ODFIIdentification = 123456789 + bh2.ODFIIdentification = "123456789" batch2, _ := ach.NewBatch(bh2) diff --git a/fileHeader.go b/fileHeader.go index 49d0f4d46..b8d53695d 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -35,14 +35,14 @@ type FileHeader struct { // a blank in the first position, followed by the four digit Federal Reserve // Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). - ImmediateDestination int + ImmediateDestination string // ImmediateOrigin contains the Routing Number of the ACH Operator or sending // point that is sending the file. The 10 character field begins with // a blank in the first position, followed by the four digit Federal Reserve // Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). - ImmediateOrigin int + ImmediateOrigin string // FileCreationDate is expressed in a "YYMMDD" format. The File Creation // Date is the date on which the file is prepared by an ODFI (ACH input files) @@ -111,9 +111,9 @@ func (fh *FileHeader) Parse(record string) { // (2-3) Always "01" fh.priorityCode = "01" // (4-13) A blank space followed by your ODFI's routing number. For example: " 121140399" - fh.ImmediateDestination = fh.parseNumField(record[3:13]) + fh.ImmediateDestination = fh.parseStringField(record[3:13]) // (14-23) A 10-digit number assigned to you by the ODFI once they approve you to originate ACH files through them - fh.ImmediateOrigin = fh.parseNumField(record[13:23]) + fh.ImmediateOrigin = fh.parseStringField(record[13:23]) // 24-29 Today's date in YYMMDD format // must be after todays date. fh.FileCreationDate = fh.parseSimpleDate(record[23:29]) @@ -185,6 +185,14 @@ func (fh *FileHeader) Validate() error { if err := fh.isAlphanumeric(fh.ImmediateDestinationName); err != nil { return &FieldError{FieldName: "ImmediateDestinationName", Value: fh.ImmediateDestinationName, Msg: err.Error()} } + //TODO: Verify these are the type of checks we want + if fh.ImmediateOrigin == "000000000" { + return &FieldError{FieldName: "ImmediateOrigin", Value: fh.ImmediateOrigin, Msg: msgFieldInclusion} + } + //TODO: Verify these are the type of checks we want + if fh.ImmediateDestination == "000000000" { + return &FieldError{FieldName: "ImmediateDestination", Value: fh.ImmediateDestination, Msg: msgFieldInclusion} + } if err := fh.isAlphanumeric(fh.ImmediateOriginName); err != nil { return &FieldError{FieldName: "ImmediateOriginName", Value: fh.ImmediateOriginName, Msg: err.Error()} } @@ -207,10 +215,10 @@ func (fh *FileHeader) fieldInclusion() error { if fh.recordType == "" { return &FieldError{FieldName: "recordType", Value: fh.recordType, Msg: msgFieldInclusion} } - if fh.ImmediateDestination == 0 { + if fh.ImmediateDestination == "" { return &FieldError{FieldName: "ImmediateDestination", Value: fh.ImmediateDestinationField(), Msg: msgFieldInclusion} } - if fh.ImmediateOrigin == 0 { + if fh.ImmediateOrigin == "" { return &FieldError{FieldName: "ImmediateOrigin", Value: fh.ImmediateOriginField(), Msg: msgFieldInclusion} } if fh.FileCreationDate.IsZero() { @@ -233,12 +241,12 @@ func (fh *FileHeader) fieldInclusion() error { // ImmediateDestinationField gets the immediate destination number with zero padding func (fh *FileHeader) ImmediateDestinationField() string { - return " " + fh.numericField(fh.ImmediateDestination, 9) + return " " + fh.stringField(fh.ImmediateDestination, 9) } // ImmediateOriginField gets the immediate origin number with 0 padding func (fh *FileHeader) ImmediateOriginField() string { - return " " + fh.numericField(fh.ImmediateOrigin, 9) + return " " + fh.stringField(fh.ImmediateOrigin, 9) } // FileCreationDateField gets the file creation date in YYMMDD format diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index 5cfe9a45a..6d28ba5fb 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -13,8 +13,8 @@ import ( // mockFileHeader build a validate File Header for tests func mockFileHeader() FileHeader { fh := NewFileHeader() - fh.ImmediateDestination = 9876543210 - fh.ImmediateOrigin = 1234567890 + fh.ImmediateDestination = "9876543210" + fh.ImmediateOrigin = "1234567890" fh.FileCreationDate = time.Now() fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name" @@ -26,10 +26,10 @@ func TestMockFileHeader(t *testing.T) { if err := fh.Validate(); err != nil { t.Error("mockFileHeader does not validate and will break other tests") } - if fh.ImmediateDestination != 9876543210 { + if fh.ImmediateDestination != "9876543210" { t.Error("ImmediateDestination depedendent default value has changed") } - if fh.ImmediateOrigin != 1234567890 { + if fh.ImmediateOrigin != "1234567890" { t.Error("ImmediateOrigin depedendent default value has changed") } if fh.ImmediateDestinationName != "Federal Reserve Bank" { @@ -175,7 +175,8 @@ func TestFormatCode(t *testing.T) { func TestFHFieldInculsion(t *testing.T) { fh := mockFileHeader() - fh.ImmediateOrigin = 0 + //fh.ImmediateOrigin = "0" + fh.ImmediateOrigin = "" if err := fh.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.Msg != msgFieldInclusion { @@ -259,7 +260,8 @@ func TestFHFieldInclusionRecordType(t *testing.T) { func TestFHFieldInclusionImmediatDestination(t *testing.T) { fh := mockFileHeader() - fh.ImmediateDestination = 0 + //fh.ImmediateDestination = "0" + fh.ImmediateDestination = "" if err := fh.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.Msg != msgFieldInclusion { diff --git a/reader_test.go b/reader_test.go index 8355a5c01..7cddcd187 100644 --- a/reader_test.go +++ b/reader_test.go @@ -159,7 +159,8 @@ func TestFileLineLong(t *testing.T) { // TestFileFileHeaderErr ensure a parse validation error flows back from the parser. func TestFileFileHeaderErr(t *testing.T) { fh := mockFileHeader() - fh.ImmediateOrigin = 0 + //fh.ImmediateOrigin = "0" + fh.ImmediateOrigin = "" r := NewReader(strings.NewReader(fh.String())) // necessary to have a file control not nil r.File.Control = mockFileControl() @@ -178,7 +179,8 @@ func TestFileFileHeaderErr(t *testing.T) { // TestFileBatchHeaderErr ensure a parse validation error flows back from the parser. func TestFileBatchHeaderErr(t *testing.T) { bh := mockBatchHeader() - bh.ODFIIdentification = 0 + //bh.ODFIIdentification = 0 + bh.ODFIIdentification = "" r := NewReader(strings.NewReader(bh.String())) _, err := r.Read() if p, ok := err.(*ParseError); ok { From 09fc324946f432cfc492e4b7a208be13889c4a39 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 27 Apr 2018 16:44:19 -0400 Subject: [PATCH 0059/1694] #170 RTN int to string #170 additional modifications Addenda, Entry Detail, Batch, File Header --- README.md | 2 +- addenda98.go | 6 ++--- addenda98_test.go | 6 ++--- addenda99.go | 6 ++--- addenda99_test.go | 6 ++--- batch.go | 9 ++++--- batchCCD_test.go | 6 ++--- batchCOR_test.go | 6 ++--- batchControl.go | 2 +- batchControl_internal_test.go | 12 +++++----- batchHeader.go | 2 +- batchHeader_internal_test.go | 8 +++---- batchPPD_test.go | 18 +++++++------- batchTEL_test.go | 6 ++--- batchWEB_test.go | 6 ++--- batch_test.go | 2 +- converters.go | 9 ++++--- converters_test.go | 35 ++++++++++++++++++++++++++++ entryDetail.go | 34 ++++++++++++++++----------- entryDetail_internal_test.go | 16 ++++++------- example/ach-ppd-write/main.go | 12 +++++----- example/simple-file-creation/main.go | 20 ++++++++-------- fileHeader.go | 22 +++++++++-------- writer.go | 2 +- 24 files changed, 150 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 0c61fa2aa..958c5ff92 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ To create an entry ```go entry := ach.NewEntryDetail() entry.TransactionCode = 22 -entry.SetRDFI(9101298) +entry.SetRDFI("009101298") entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" diff --git a/addenda98.go b/addenda98.go index 3e732d749..815e41f41 100644 --- a/addenda98.go +++ b/addenda98.go @@ -20,7 +20,7 @@ type Addenda98 struct { // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. OriginalTrace int // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI int + OriginalDFI string // CorrectedData CorrectedData string // TraceNumber matches the Entry Detail Trace Number of the entry being returned. @@ -72,7 +72,7 @@ func (addenda98 *Addenda98) Parse(record string) { // 7-21 addenda98.OriginalTrace = addenda98.parseNumField(record[6:21]) // 28-35 - addenda98.OriginalDFI = addenda98.parseNumField(record[27:35]) + addenda98.OriginalDFI = addenda98.parseStringField(record[27:35]) // 36-64 addenda98.CorrectedData = strings.TrimSpace(record[35:64]) // 80-94 @@ -131,7 +131,7 @@ func (addenda98 *Addenda98) OriginalTraceField() string { // OriginalDFIField returns a zero padded OriginalDFI string func (addenda98 *Addenda98) OriginalDFIField() string { - return addenda98.numericField(addenda98.OriginalDFI, 8) + return addenda98.stringRTNField(addenda98.OriginalDFI, 8) } //CorrectedDataField returns a space padded CorrectedData string diff --git a/addenda98_test.go b/addenda98_test.go index cfa346aa8..c09576cc1 100644 --- a/addenda98_test.go +++ b/addenda98_test.go @@ -8,7 +8,7 @@ func mockAddenda98() *Addenda98 { addenda98 := NewAddenda98() addenda98.ChangeCode = "C01" addenda98.OriginalTrace = 12345 - addenda98.OriginalDFI = 9101298 + addenda98.OriginalDFI = "9101298" addenda98.CorrectedData = "1918171614" addenda98.TraceNumber = 91012980000088 @@ -32,8 +32,8 @@ func TestAddenda98Parse(t *testing.T) { if addenda98.OriginalTrace != 99912340000015 { t.Errorf("expected %v got %v", 99912340000015, addenda98.OriginalTrace) } - if addenda98.OriginalDFI != 9101298 { - t.Errorf("expected %v got %v", 9101298, addenda98.OriginalDFI) + if addenda98.OriginalDFI != "09101298" { + t.Errorf("expected %s got %s", "09101298", addenda98.OriginalDFI) } if addenda98.CorrectedData != "1918171614" { t.Errorf("expected %v got %v", "1918171614", addenda98.CorrectedData) diff --git a/addenda99.go b/addenda99.go index 5e825dc43..250238742 100644 --- a/addenda99.go +++ b/addenda99.go @@ -42,7 +42,7 @@ type Addenda99 struct { // DateOfDeath The field date of death is to be supplied on Entries being returned for reason of death (return reason codes R14 and R15). DateOfDeath time.Time // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI int + OriginalDFI string // AddendaInformation AddendaInformation string // TraceNumber matches the Entry Detail Trace Number of the entry being returned. @@ -83,7 +83,7 @@ func (Addenda99 *Addenda99) Parse(record string) { // 22-27, might be a date or blank Addenda99.DateOfDeath = Addenda99.parseSimpleDate(record[21:27]) // 28-35 - Addenda99.OriginalDFI = Addenda99.parseNumField(record[27:35]) + Addenda99.OriginalDFI = Addenda99.parseStringField(record[27:35]) // 36-79 Addenda99.AddendaInformation = strings.TrimSpace(record[35:79]) // 80-94 @@ -143,7 +143,7 @@ func (Addenda99 *Addenda99) DateOfDeathField() string { // OriginalDFIField returns a zero padded OriginalDFI string func (Addenda99 *Addenda99) OriginalDFIField() string { - return Addenda99.numericField(Addenda99.OriginalDFI, 8) + return Addenda99.stringRTNField(Addenda99.OriginalDFI, 8) } //AddendaInformationField returns a space padded AddendaInformation string diff --git a/addenda99_test.go b/addenda99_test.go index bf341ca44..51356e55c 100644 --- a/addenda99_test.go +++ b/addenda99_test.go @@ -14,7 +14,7 @@ func mockAddenda99() *Addenda99 { addenda99.ReturnCode = "R07" addenda99.OriginalTrace = 99912340000015 addenda99.AddendaInformation = "Authorization Revoked" - addenda99.OriginalDFI = 9101298 + addenda99.OriginalDFI = "9101298" return addenda99 } @@ -39,8 +39,8 @@ func TestAddenda99Parse(t *testing.T) { if addenda99.DateOfDeath.IsZero() != true { t.Errorf("expected: %v got: %v", time.Time{}, addenda99.DateOfDeath) } - if addenda99.OriginalDFI != 9101298 { - t.Errorf("expected: %v got: %v", 9101298, addenda99.OriginalDFI) + if addenda99.OriginalDFI != "09101298" { + t.Errorf("expected: %s got: %s", "09101298", addenda99.OriginalDFI) } if addenda99.AddendaInformation != "Authorization revoked" { t.Errorf("expected: %v got: %v", "Authorization revoked", addenda99.AddendaInformation) diff --git a/batch.go b/batch.go index 95bf47076..34d17870c 100644 --- a/batch.go +++ b/batch.go @@ -2,8 +2,8 @@ package ach import ( "fmt" - "strings" "strconv" + "strings" ) // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries @@ -123,7 +123,7 @@ func (batch *batch) build() error { return err } - batchHeaderODFI, err := strconv.Atoi(batch.header.ODFIIdentificationField() [:8]) + batchHeaderODFI, err := strconv.Atoi(batch.header.ODFIIdentificationField()[:8]) if err != nil { return err } @@ -288,7 +288,10 @@ func (batch *batch) isEntryHash() error { func (batch *batch) calculateEntryHash() string { hash := 0 for _, entry := range batch.entries { - hash = hash + entry.RDFIIdentification + + entryRDFI, _ := strconv.Atoi(entry.RDFIIdentification) + + hash = hash + entryRDFI } return batch.numericField(hash, 10) } diff --git a/batchCCD_test.go b/batchCCD_test.go index 175111578..d4c96e24d 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -9,16 +9,16 @@ func mockBatchCCDHeader() *BatchHeader { bh.ServiceClassCode = 220 bh.StandardEntryClassCode = "CCD" bh.CompanyName = "Your Company, inc" - bh.CompanyIdentification = "123456789" + bh.CompanyIdentification = "121042882" bh.CompanyEntryDescription = "Vndr Pay" - bh.ODFIIdentification = "6200001" + bh.ODFIIdentification = "121042882" return bh } func mockCCDEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 - entry.SetRDFI(9101298) + entry.SetRDFI("231380104") entry.DFIAccountNumber = "744-5678-99" entry.Amount = 5000000 entry.IdentificationNumber = "location #23" diff --git a/batchCOR_test.go b/batchCOR_test.go index d56f12aac..98e478eb7 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -11,16 +11,16 @@ func mockBatchCORHeader() *BatchHeader { bh.ServiceClassCode = 220 bh.StandardEntryClassCode = "COR" bh.CompanyName = "Your Company, inc" - bh.CompanyIdentification = "123456789" + bh.CompanyIdentification = "121042882" bh.CompanyEntryDescription = "Vndr Pay" - bh.ODFIIdentification = "6200001" + bh.ODFIIdentification = "121042882" return bh } func mockCOREntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 - entry.SetRDFI(9101298) + entry.SetRDFI("231380104") entry.DFIAccountNumber = "744-5678-99" entry.Amount = 0 entry.IdentificationNumber = "location #23" diff --git a/batchControl.go b/batchControl.go index 35399e53c..a5003e232 100644 --- a/batchControl.go +++ b/batchControl.go @@ -194,7 +194,7 @@ func (bc *BatchControl) MessageAuthenticationCodeField() string { // ODFIIdentificationField get the odfi number zero padded func (bc *BatchControl) ODFIIdentificationField() string { - return bc.stringField(bc.ODFIIdentification, 8) + return bc.stringRTNField(bc.ODFIIdentification, 8) } // BatchNumberField gets a string of the batch number zero padded diff --git a/batchControl_internal_test.go b/batchControl_internal_test.go index be9a3039e..2b85409b1 100644 --- a/batchControl_internal_test.go +++ b/batchControl_internal_test.go @@ -12,8 +12,8 @@ import ( func mockBatchControl() *BatchControl { bc := NewBatchControl() bc.ServiceClassCode = 220 - bc.CompanyIdentification = "123456789" - bc.ODFIIdentification = "6200001" + bc.CompanyIdentification = "121042882" + bc.ODFIIdentification = "12104288" return bc } @@ -25,10 +25,10 @@ func TestMockBatchControl(t *testing.T) { if bc.ServiceClassCode != 220 { t.Error("ServiceClassCode depedendent default value has changed") } - if bc.CompanyIdentification != "123456789" { + if bc.CompanyIdentification != "121042882" { t.Error("CompanyIdentification depedendent default value has changed") } - if bc.ODFIIdentification != "6200001" { + if bc.ODFIIdentification != "12104288" { t.Error("ODFIIdentification depedendent default value has changed") } } @@ -44,7 +44,7 @@ func TestParseBatchControl(t *testing.T) { ODFIIdentification: "7640125"} r.addCurrentBatch(NewBatchPPD(&bh)) - r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: 5320001, TraceNumber: 76401255655291}) + r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: "5320001", TraceNumber: 76401255655291}) if err := r.parseBatchControl(); err != nil { t.Errorf("%T: %s", err, err) } @@ -96,7 +96,7 @@ func TestBCString(t *testing.T) { ODFIIdentification: "7640125"} r.addCurrentBatch(NewBatchPPD(&bh)) - r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: 5320001, TraceNumber: 76401255655291}) + r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: "5320001", TraceNumber: 76401255655291}) if err := r.parseBatchControl(); err != nil { t.Errorf("%T: %s", err, err) } diff --git a/batchHeader.go b/batchHeader.go index 9a9ff318f..d1fe26360 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -264,7 +264,7 @@ func (bh *BatchHeader) EffectiveEntryDateField() string { // ODFIIdentificationField get the odfi number zero padded func (bh *BatchHeader) ODFIIdentificationField() string { - return bh.stringField(bh.ODFIIdentification, 8) + return bh.stringRTNField(bh.ODFIIdentification, 8) } // BatchNumberField get the batch number zero padded diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index 938741965..122f958c4 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -14,9 +14,9 @@ func mockBatchHeader() *BatchHeader { bh.ServiceClassCode = 220 bh.StandardEntryClassCode = "PPD" bh.CompanyName = "ACME Corporation" - bh.CompanyIdentification = "123456789" + bh.CompanyIdentification = "121042882" bh.CompanyEntryDescription = "PAYROLL" - bh.ODFIIdentification = "6200001" + bh.ODFIIdentification = "12104288" return bh } @@ -34,13 +34,13 @@ func TestMockBatchHeader(t *testing.T) { if bh.CompanyName != "ACME Corporation" { t.Error("CompanyName dependent default value has changed") } - if bh.CompanyIdentification != "123456789" { + if bh.CompanyIdentification != "121042882" { t.Error("CompanyIdentification dependent default value has changed") } if bh.CompanyEntryDescription != "PAYROLL" { t.Error("CompanyEntryDescription dependent default value has changed") } - if bh.ODFIIdentification != "6200001" { + if bh.ODFIIdentification != "12104288" { t.Error("ODFIIdentification dependent default value has changed") } } diff --git a/batchPPD_test.go b/batchPPD_test.go index d9158643f..2ace3b126 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -14,17 +14,17 @@ func mockBatchPPDHeader() *BatchHeader { bh.ServiceClassCode = 220 bh.StandardEntryClassCode = "PPD" bh.CompanyName = "ACME Corporation" - bh.CompanyIdentification = "123456789" + bh.CompanyIdentification = "121042882" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = "6200001" + bh.ODFIIdentification = "12104288" return bh } func mockPPDEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 - entry.SetRDFI(9101298) + entry.SetRDFI("231380104") entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" @@ -38,20 +38,20 @@ func mockBatchPPDHeader2() *BatchHeader { bh.ServiceClassCode = 200 bh.CompanyName = "MY BEST COMP." bh.CompanyDiscretionaryData = "INCLUDES OVERTIME" - bh.CompanyIdentification = "1419871234" + bh.CompanyIdentification = "121042882" bh.StandardEntryClassCode = "PPD" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = "109991234" + bh.ODFIIdentification = "12104288" return bh } func mockPPDEntry2() *EntryDetail { entry := NewEntryDetail() - entry.TransactionCode = 22 // ACH Credit - entry.SetRDFI(81086674) // scottrade bank routing number - entry.DFIAccountNumber = "62292250" // scottrade account number - entry.Amount = 1000000 // 1k dollars + entry.TransactionCode = 22 // ACH Credit + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "62292250" // account number + entry.Amount = 100000 // 1k dollars entry.IdentificationNumber = "658-888-2468" // Unique ID for payment entry.IndividualName = "Wade Arnold" entry.SetTraceNumber(mockBatchPPDHeader2().ODFIIdentification, 1) diff --git a/batchTEL_test.go b/batchTEL_test.go index 7c90e1b66..16967ceb7 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -9,16 +9,16 @@ func mockBatchTELHeader() *BatchHeader { bh.ServiceClassCode = 225 bh.StandardEntryClassCode = "TEL" bh.CompanyName = "Your Company, inc" - bh.CompanyIdentification = "123456789" + bh.CompanyIdentification = "121042882" bh.CompanyEntryDescription = "Vndr Pay" - bh.ODFIIdentification = "6200001" + bh.ODFIIdentification = "12104288" return bh } func mockTELEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 - entry.SetRDFI(9101298) + entry.SetRDFI("231380104") entry.DFIAccountNumber = "744-5678-99" entry.Amount = 5000000 entry.IdentificationNumber = "Phone 333-2222" diff --git a/batchWEB_test.go b/batchWEB_test.go index 3e495da81..e1814e094 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -7,16 +7,16 @@ func mockBatchWEBHeader() *BatchHeader { bh.ServiceClassCode = 220 bh.StandardEntryClassCode = "WEB" bh.CompanyName = "Your Company, inc" - bh.CompanyIdentification = "123456789" + bh.CompanyIdentification = "121042882" bh.CompanyEntryDescription = "Online Order" - bh.ODFIIdentification = "6200001" + bh.ODFIIdentification = "12104288" return bh } func mockWEBEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 - entry.SetRDFI(9101298) + entry.SetRDFI("231380104") entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" diff --git a/batch_test.go b/batch_test.go index caabd172e..d011de828 100644 --- a/batch_test.go +++ b/batch_test.go @@ -25,7 +25,7 @@ func mockBatchInvalidSECHeader() *BatchHeader { bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = "6200001" + bh.ODFIIdentification = "123456789" return bh } diff --git a/converters.go b/converters.go index 9dce6b9f6..a80962d83 100644 --- a/converters.go +++ b/converters.go @@ -45,8 +45,6 @@ func (c *converters) parseSimpleTime(s string) time.Time { return t } -//func (v *Converters) numericField() - // alphaField Alphanumeric and Alphabetic fields are left-justified and space filled. func (c *converters) alphaField(s string, max uint) string { ln := uint(len(s)) @@ -68,11 +66,12 @@ func (c *converters) numericField(n int, max uint) string { return s } -func (c *converters) stringField(s string, max uint) string { +// stringField right-justified and zero filled +func (c *converters) stringRTNField(s string, max uint) string { ln := uint(len(s)) if ln > max { - return s[ln-max:] + return s[:max] } s = strings.Repeat("0", int(max-ln)) + s return s -} \ No newline at end of file +} diff --git a/converters_test.go b/converters_test.go index cf57646b8..53ab199f1 100644 --- a/converters_test.go +++ b/converters_test.go @@ -50,3 +50,38 @@ func TestParseNumField(t *testing.T) { t.Errorf("Right justified zero got: '%v'", result) } } + +//TestParseNumField handle zero and spaces in number conversion +func TestParseStringField(t *testing.T) { + c := converters{} + result := c.parseStringField(" 012345") + if result != "012345" { + t.Errorf("Trim spaces: '%v'", result) + } +} + +// TestNumericFieldShort ensures zero padding and right justified +func TestRTNFieldShort(t *testing.T) { + c := converters{} + result := c.stringRTNField("123456", 8) + if result != "00123456" { + t.Errorf("Zero padding 8 character string : '%v'", result) + } +} + +// TestNumericFieldLong right justified and sliced to max length +func TestRTNFieldLong(t *testing.T) { + c := converters{} + result := c.stringRTNField("1234567899", 8) + if result != "12345678" { + t.Errorf("first 8 character string: '%v'", result) + } +} + +func TestRTNFieldExact(t *testing.T) { + c := converters{} + result := c.stringRTNField("12345678", 8) + if result != "12345678" { + t.Errorf("first 8 character string: '%v'", result) + } +} diff --git a/entryDetail.go b/entryDetail.go index 70b7ae7c3..a59aa2a3b 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -30,10 +30,10 @@ type EntryDetail struct { // RDFIIdentification is the RDFI's routing number without the last digit. // Receiving Depository Financial Institution - RDFIIdentification int + RDFIIdentification string // CheckDigit the last digit of the RDFI's routing number - CheckDigit int + CheckDigit string // DFIAccountNumber is the receiver's bank account number you are crediting/debiting. // It important to note that this is an alphanumeric field, so its space padded, no zero padded @@ -106,9 +106,9 @@ func (ed *EntryDetail) Parse(record string) { // 2-3 is checking credit 22 debit 27 savings credit 32 debit 37 ed.TransactionCode = ed.parseNumField(record[1:3]) // 4-11 the RDFI's routing number without the last digit. - ed.RDFIIdentification = ed.parseNumField(record[3:11]) + ed.RDFIIdentification = ed.parseStringField(record[3:11]) // 12-12 The last digit of the RDFI's routing number - ed.CheckDigit = ed.parseNumField(record[11:12]) + ed.CheckDigit = ed.parseStringField(record[11:12]) // 13-29 The receiver's bank account number you are crediting/debiting ed.DFIAccountNumber = record[12:29] // 30-39 Number of cents you are debiting/crediting this account @@ -169,10 +169,18 @@ func (ed *EntryDetail) Validate() error { if err := ed.isAlphanumeric(ed.DiscretionaryData); err != nil { return &FieldError{FieldName: "DiscretionaryData", Value: ed.DiscretionaryData, Msg: err.Error()} } + calculated := ed.CalculateCheckDigit(ed.RDFIIdentificationField()) - if calculated != ed.CheckDigit { + + edCheckDigit, err := strconv.Atoi(ed.CheckDigit) + + if err != nil { + msg := fmt.Sprintf(msgValidCheckDigit, calculated) + return &FieldError{FieldName: "RDFIIdentification", Value: ed.CheckDigit, Msg: msg} + } + if calculated != edCheckDigit { msg := fmt.Sprintf(msgValidCheckDigit, calculated) - return &FieldError{FieldName: "RDFIIdentification", Value: strconv.Itoa(ed.CheckDigit), Msg: msg} + return &FieldError{FieldName: "RDFIIdentification", Value: ed.CheckDigit, Msg: msg} } return nil } @@ -186,7 +194,7 @@ func (ed *EntryDetail) fieldInclusion() error { if ed.TransactionCode == 0 { return &FieldError{FieldName: "TransactionCode", Value: strconv.Itoa(ed.TransactionCode), Msg: msgFieldInclusion} } - if ed.RDFIIdentification == 0 { + if ed.RDFIIdentification == "" { return &FieldError{FieldName: "RDFIIdentification", Value: ed.RDFIIdentificationField(), Msg: msgFieldInclusion} } if ed.DFIAccountNumber == "" { @@ -225,22 +233,22 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { } // SetRDFI takes the 9 digit RDFI account number and separates it for RDFIIdentification and CheckDigit -func (ed *EntryDetail) SetRDFI(rdfi int) *EntryDetail { - s := ed.numericField(rdfi, 9) - ed.RDFIIdentification = ed.parseNumField(s[:8]) - ed.CheckDigit = ed.parseNumField(s[8:9]) +func (ed *EntryDetail) SetRDFI(rdfi string) *EntryDetail { + s := ed.stringRTNField(rdfi, 9) + ed.RDFIIdentification = ed.parseStringField(s[:8]) + ed.CheckDigit = ed.parseStringField(s[8:9]) return ed } // SetTraceNumber takes first 8 digits of ODFI and concatenates a sequence number onto the TraceNumber func (ed *EntryDetail) SetTraceNumber(ODFIIdentification string, seq int) { - trace := ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7) + trace := ed.stringRTNField(ODFIIdentification, 8) + ed.numericField(seq, 7) ed.TraceNumber = ed.parseNumField(trace) } // RDFIIdentificationField get the rdfiIdentification with zero padding func (ed *EntryDetail) RDFIIdentificationField() string { - return ed.numericField(ed.RDFIIdentification, 8) + return ed.stringRTNField(ed.RDFIIdentification, 8) } // DFIAccountNumberField gets the DFIAccountNumber with space padding diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index 46194a552..b8c22b2c4 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -12,7 +12,7 @@ import ( func mockEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 - entry.SetRDFI(9101298) + entry.SetRDFI("121042882") entry.DFIAccountNumber = "123456789" entry.Amount = 100000000 entry.IndividualName = "Wade Arnold" @@ -39,7 +39,7 @@ func TestMockEntryDetail(t *testing.T) { if entry.IndividualName != "Wade Arnold" { t.Error("IndividualName dependent default value has changed") } - if entry.TraceNumber != 62000010000001 { + if entry.TraceNumber != 121042880000001 { t.Errorf("TraceNumber dependent default value has changed %v", entry.TraceNumber) } } @@ -65,7 +65,7 @@ func TestParseEntryDetail(t *testing.T) { if record.RDFIIdentificationField() != "05320001" { t.Errorf("RDFIIdentification Expected '05320001' got: '%v'", record.RDFIIdentificationField()) } - if record.CheckDigit != 9 { + if record.CheckDigit != "9" { t.Errorf("CheckDigit Expected '9' got: %v", record.CheckDigit) } if record.DFIAccountNumberField() != "12345 " { @@ -197,7 +197,7 @@ func TestEDDiscretionaryDataAlphaNumeric(t *testing.T) { func TestEDisCheckDigit(t *testing.T) { ed := mockEntryDetail() - ed.CheckDigit = 1 + ed.CheckDigit = "1" if err := ed.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.FieldName != "RDFIIdentification" { @@ -209,11 +209,11 @@ func TestEDisCheckDigit(t *testing.T) { func TestEDSetRDFI(t *testing.T) { ed := NewEntryDetail() - ed.SetRDFI(81086674) - if ed.RDFIIdentification != 8108667 { + ed.SetRDFI("810866774") + if ed.RDFIIdentification != "81086677" { t.Error("RDFI identification") } - if ed.CheckDigit != 4 { + if ed.CheckDigit != "4" { t.Error("Unexpected check digit") } } @@ -244,7 +244,7 @@ func TestEDFieldInclusionTransactionCode(t *testing.T) { func TestEDFieldInclusionRDFIIdentification(t *testing.T) { entry := mockEntryDetail() - entry.RDFIIdentification = 0 + entry.RDFIIdentification = "" if err := entry.Validate(); err != nil { if e, ok := err.(*FieldError); ok { if e.Msg != msgFieldInclusion { diff --git a/example/ach-ppd-write/main.go b/example/ach-ppd-write/main.go index 451ce829a..443e9d475 100644 --- a/example/ach-ppd-write/main.go +++ b/example/ach-ppd-write/main.go @@ -5,7 +5,7 @@ import ( "os" "time" - "github.com/moov-io/ach" + "github.com/bkmoovio/ach" ) func main() { @@ -15,9 +15,9 @@ func main() { // Set originator bank ODFI and destination Operator for the financial institution // this is the funding/receiving source of the transfer fh := ach.NewFileHeader() - fh.ImmediateDestination = "9876543210" // A blank space followed by your ODFI's transit/routing number - fh.ImmediateOrigin = "1234567890" // Organization or Company FED ID usually 1 and FEIN/SSN. Assigned by your ODFI - fh.FileCreationDate = time.Now() // Todays Date + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Todays Date fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name" @@ -29,14 +29,14 @@ func main() { bh.StandardEntryClassCode = "PPD" // Consumer destination vs Company CCD bh.CompanyEntryDescription = "REG.SALARY" // will be on receiving accounts statement bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) - bh.ODFIIdentification = "12345678" // first 8 digits of your bank account + bh.ODFIIdentification = "121042882" // Originating Routing Number // Identifies the receivers account information // can be multiple entry's per batch entry := ach.NewEntryDetail() // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) entry.TransactionCode = 22 // Code 22: Credit (deposit) to checking account - entry.SetRDFI(9101298) // Receivers bank transit routing number + entry.SetRDFI("231380104") // Receivers bank transit routing number entry.DFIAccountNumber = "12345678" // Receivers bank account number entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 entry.SetTraceNumber(bh.ODFIIdentification, 1) diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index e920f73ba..d53369046 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -4,15 +4,15 @@ import ( "fmt" "os" - "github.com/moov-io/ach" + "github.com/bkmoovio/ach" "time" ) func main() { // To create a file fh := ach.NewFileHeader() - fh.ImmediateDestination = "987654321" - fh.ImmediateOrigin = "123456789" + fh.ImmediateDestination = "231380104" + fh.ImmediateOrigin = "121042882" fh.FileCreationDate = time.Now() fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name" @@ -28,16 +28,16 @@ func main() { bh.StandardEntryClassCode = "PPD" bh.CompanyEntryDescription = "Trans. Description" bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) - bh.ODFIIdentification = "12345678" + bh.ODFIIdentification = "121042882" batch, _ := ach.NewBatch(bh) // To create an entry entry := ach.NewEntryDetail() entry.TransactionCode = 22 - entry.SetRDFI(9101298) - entry.DFIAccountNumber = "999555121" - entry.Amount = 100000000 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "81967038518" + entry.Amount = 1000000 entry.IndividualName = "Wade Arnold" entry.SetTraceNumber(bh.ODFIIdentification, 1) entry.IdentificationNumber = "ABC##jvkdjfuiwn" @@ -72,7 +72,7 @@ func main() { bh2.StandardEntryClassCode = "WEB" bh2.CompanyEntryDescription = "Subscr" bh2.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) - bh2.ODFIIdentification = "123456789" + bh2.ODFIIdentification = "121042882" batch2, _ := ach.NewBatch(bh2) @@ -81,8 +81,8 @@ func main() { entry2 := ach.NewEntryDetail() entry2.TransactionCode = 22 - entry2.SetRDFI(102001017) - entry2.DFIAccountNumber = "5343121" + entry2.SetRDFI("231380104") + entry2.DFIAccountNumber = "81967038518" entry2.Amount = 799 entry2.IndividualName = "Wade Arnold" entry2.SetTraceNumber(bh2.ODFIIdentification, 2) diff --git a/fileHeader.go b/fileHeader.go index b8d53695d..93d51af4e 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -31,17 +31,19 @@ type FileHeader struct { priorityCode string // ImmediateDestination contains the Routing Number of the ACH Operator or receiving - // point to which the file is being sent. The 10 character field begins with - // a blank in the first position, followed by the four digit Federal Reserve - // Routing Symbol, the four digit ABA Institution Identifier, and the Check - // Digit (bTTTTAAAAC). + // point to which the file is being sent. The ach file format specifies a 10 character + // field begins with a blank space in the first position, followed by the four digit + // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check + // Digit (bTTTTAAAAC). ImmediateDestinationField() will append the blank space to the + // routing number. ImmediateDestination string // ImmediateOrigin contains the Routing Number of the ACH Operator or sending - // point that is sending the file. The 10 character field begins with - // a blank in the first position, followed by the four digit Federal Reserve - // Routing Symbol, the four digit ABA Institution Identifier, and the Check - // Digit (bTTTTAAAAC). + // point that is sending the file. The ach file format specifies a 10 character field + // which begins with a blank space in the first position, followed by the four digit + // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check + // Digit (bTTTTAAAAC). ImmediateOriginField() will append the blank space to the routing + // number. ImmediateOrigin string // FileCreationDate is expressed in a "YYMMDD" format. The File Creation @@ -241,12 +243,12 @@ func (fh *FileHeader) fieldInclusion() error { // ImmediateDestinationField gets the immediate destination number with zero padding func (fh *FileHeader) ImmediateDestinationField() string { - return " " + fh.stringField(fh.ImmediateDestination, 9) + return " " + fh.stringRTNField(fh.ImmediateDestination, 9) } // ImmediateOriginField gets the immediate origin number with 0 padding func (fh *FileHeader) ImmediateOriginField() string { - return " " + fh.stringField(fh.ImmediateOrigin, 9) + return " " + fh.stringRTNField(fh.ImmediateOrigin, 9) } // FileCreationDateField gets the file creation date in YYMMDD format diff --git a/writer.go b/writer.go index 023499763..e359b3cdb 100644 --- a/writer.go +++ b/writer.go @@ -89,7 +89,7 @@ func (w *Writer) Error() error { return err } -// WriteAll writes multiple ach.fieles to w using Write and then calls Flush. +// WriteAll writes multiple ach.files to w using Write and then calls Flush. func (w *Writer) WriteAll(files []*File) error { for _, file := range files { err := w.Write(file) From 576d52504bfd7553124eee8b3160eb675bf0f18a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 09:49:25 -0400 Subject: [PATCH 0060/1694] 170 int to string moov-io 170 int to string moov-io --- example/ach-ppd-write/main.go | 2 +- example/simple-file-creation/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/ach-ppd-write/main.go b/example/ach-ppd-write/main.go index 443e9d475..96b7d805d 100644 --- a/example/ach-ppd-write/main.go +++ b/example/ach-ppd-write/main.go @@ -5,7 +5,7 @@ import ( "os" "time" - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" ) func main() { diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index d53369046..9a7441937 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "time" ) From 8f9f94e62886594bf647e7387e2cb4f0e4a37163 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 09:53:58 -0400 Subject: [PATCH 0061/1694] 170 int to string READMe 170 int to string READMe --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 958c5ff92..14c6aadf7 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,8 @@ The following is based on [simple file creation](https://github.com/moov-io/ach/ ```go fh := ach.NewFileHeader() - fh.ImmediateDestination = 9876543210 // A blank space followed by your ODFI's transit/routing number - fh.ImmediateOrigin = 1234567890 // Organization or Company FED ID usually 1 and FEIN/SSN. Assigned by your ODFI + fh.ImmediateDestination = "9876543210" // A blank space followed by your ODFI's transit/routing number + fh.ImmediateOrigin = "1234567890" // Organization or Company FED ID usually 1 and FEIN/SSN. Assigned by your ODFI fh.FileCreationDate = time.Now() // Todays Date fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name") @@ -91,7 +91,7 @@ func mockBatchPPDHeader() *BatchHeader { bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } @@ -109,7 +109,7 @@ func mockBatchPPDHeader() *BatchHeader { bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "PAYROLL" bh.EffectiveEntryDate = time.Now() - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } @@ -173,7 +173,7 @@ func mockBatchWEBHeader() *BatchHeader { bh.CompanyName = "Your Company, inc" bh.CompanyIdentification = "123456789" bh.CompanyEntryDescription = "Online Order" - bh.ODFIIdentification = 6200001 + bh.ODFIIdentification = "6200001" return bh } From 6dc22b8c8396f7d4b00df1bcc695acfb4ca59f3f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 10:19:00 -0400 Subject: [PATCH 0062/1694] 170 int to string bh.ODFIIdentification 170 int to string bh.ODFIIdentification --- batchHeader.go | 2 +- batchHeader_internal_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/batchHeader.go b/batchHeader.go index d1fe26360..9c475c8f8 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -226,7 +226,7 @@ func (bh *BatchHeader) fieldInclusion() error { if bh.CompanyEntryDescription == "" { return &FieldError{FieldName: "CompanyEntryDescription", Value: bh.CompanyEntryDescription, Msg: msgFieldInclusion} } - if bh.ODFIIdentification == "000000000" { + if bh.ODFIIdentification == "" { return &FieldError{FieldName: "ODFIIdentification", Value: bh.ODFIIdentificationField(), Msg: msgFieldInclusion} } return nil diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index 122f958c4..33b2317d5 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -294,3 +294,15 @@ func TestBHFieldInclusionOriginatorStatusCode(t *testing.T) { } } } + +func TestBHFieldInclusionODFIIdentification(t *testing.T) { + bh := mockBatchHeader() + bh.ODFIIdentification = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} \ No newline at end of file From 68dad55ad8e99895b082e1f9cb92e6bfdfe7f839 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 10:36:39 -0400 Subject: [PATCH 0063/1694] 170 Add test for fh.FileCreationDate 170 Add test for fh.FileCreationDate --- fileHeader_internal_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index 6d28ba5fb..4f8823b1a 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -318,3 +318,15 @@ func TestFHFieldInclusionFormatCode(t *testing.T) { } } } + +func TestFHFieldInclusionCreationDate(t *testing.T) { + fh := mockFileHeader() + fh.FileCreationDate = time.Time{} + if err := fh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} From 22c0aeca625baa45f3b4a5f8fe1f33f18ed93021 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 10:39:39 -0400 Subject: [PATCH 0064/1694] 170 Add test for fh.ImmediateDestination 170 Add test for fh.ImmediateDestination --- fileHeader_internal_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index 4f8823b1a..e4be7be27 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -173,9 +173,8 @@ func TestFormatCode(t *testing.T) { } } -func TestFHFieldInculsion(t *testing.T) { +func TestFHFieldInclusion(t *testing.T) { fh := mockFileHeader() - //fh.ImmediateOrigin = "0" fh.ImmediateOrigin = "" if err := fh.Validate(); err != nil { if e, ok := err.(*FieldError); ok { @@ -330,3 +329,15 @@ func TestFHFieldInclusionCreationDate(t *testing.T) { } } } + +func TestFHFieldInclusionImmediateDestination(t *testing.T) { + fh := mockFileHeader() + fh.ImmediateDestination = "" + if err := fh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} \ No newline at end of file From 94fd21e94c874f9ac5bf2b1f065f930e291b5955 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 11:02:14 -0400 Subject: [PATCH 0065/1694] 170 Add Test for Batch Field Inclusion bh.ODFIIdentification 170 Add Test for Batch Field Inclusion bh.ODFIIdentification --- batch_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/batch_test.go b/batch_test.go index d011de828..db50babdd 100644 --- a/batch_test.go +++ b/batch_test.go @@ -306,3 +306,17 @@ func TestBatchTraceNumberExists(t *testing.T) { t.Errorf("Trace number was set to %v before batch.build and is now %v\n", traceBefore, traceAfter) } } + +func TestBatchFieldInclusion(t *testing.T) { + mockBatch := mockBatch() + mockBatch.header.ODFIIdentification = "" + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} From 8ecb9fac2b1d0445ae365f105b11f1b418808360 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 11:32:15 -0400 Subject: [PATCH 0066/1694] 170 Mismatched Trace Number ODFI 170 Mismatched Trace Number ODFI --- batch_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/batch_test.go b/batch_test.go index db50babdd..0676080f7 100644 --- a/batch_test.go +++ b/batch_test.go @@ -16,6 +16,29 @@ func mockBatch() *batch { return mockBatch } +// Batch with mismatched Trace Number ODFI +func mockBatchInvalidTraceNumberODFI() *batch { + mockBatch := &batch{} + mockBatch.SetHeader(mockBatchHeader()) + mockBatch.AddEntry(mockEntryDetailInvalidTraceNumberODFI()) + return mockBatch +} + +// Entry Detail with mismatched Trace Number ODFI +func mockEntryDetailInvalidTraceNumberODFI() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("121042882") + entry.DFIAccountNumber = "123456789" + entry.Amount = 100000000 + entry.IndividualName = "Wade Arnold" + entry.SetTraceNumber("9928272", 1) + entry.IdentificationNumber = "ABC##jvkdjfuiwn" + entry.Category = CategoryForward + return entry +} + + // Invalid SEC CODE Batch Header func mockBatchInvalidSECHeader() *BatchHeader { bh := NewBatchHeader() @@ -320,3 +343,12 @@ func TestBatchFieldInclusion(t *testing.T) { } } } + + +func TestBatchInvalidTraceNumberODFI(t *testing.T) { + mockBatch := mockBatchInvalidTraceNumberODFI() + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + +} From c26c245a853229b4e3d19ea20c158db2de588f4b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 11:44:47 -0400 Subject: [PATCH 0067/1694] 170 Add Batch tests 170 Add Batch No Entry test and Batch Control test --- batch_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/batch_test.go b/batch_test.go index 0676080f7..79afbc875 100644 --- a/batch_test.go +++ b/batch_test.go @@ -39,6 +39,13 @@ func mockEntryDetailInvalidTraceNumberODFI() *EntryDetail { } +// Batch with no entries +func mockBatchNoEntry() *batch { + mockBatch := &batch{} + mockBatch.SetHeader(mockBatchHeader()) + return mockBatch +} + // Invalid SEC CODE Batch Header func mockBatchInvalidSECHeader() *BatchHeader { bh := NewBatchHeader() @@ -350,5 +357,31 @@ func TestBatchInvalidTraceNumberODFI(t *testing.T) { if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) } +} +func TestBatchNoEntry(t *testing.T) { + mockBatch := mockBatchNoEntry() + if err := mockBatch.build(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } } + +func TestBatchControl(t *testing.T) { + mockBatch := mockBatch() + mockBatch.control.ODFIIdentification = "" + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} \ No newline at end of file From 520b636c9324f59f97269d6026e0eab56cb4e9dc Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 13:01:37 -0400 Subject: [PATCH 0068/1694] 170 Removed TODO 170 Removed TODO --- fileHeader.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/fileHeader.go b/fileHeader.go index 93d51af4e..0b24c9f95 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -187,11 +187,9 @@ func (fh *FileHeader) Validate() error { if err := fh.isAlphanumeric(fh.ImmediateDestinationName); err != nil { return &FieldError{FieldName: "ImmediateDestinationName", Value: fh.ImmediateDestinationName, Msg: err.Error()} } - //TODO: Verify these are the type of checks we want if fh.ImmediateOrigin == "000000000" { return &FieldError{FieldName: "ImmediateOrigin", Value: fh.ImmediateOrigin, Msg: msgFieldInclusion} } - //TODO: Verify these are the type of checks we want if fh.ImmediateDestination == "000000000" { return &FieldError{FieldName: "ImmediateDestination", Value: fh.ImmediateDestination, Msg: msgFieldInclusion} } From d53d774df113eb315d550db8af4e0e39e3da9ba4 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 13:11:20 -0400 Subject: [PATCH 0069/1694] 170 Add FileFHImmediteDestination test 170 Add FileFHImmediteDestination test --- fileHeader_internal_test.go | 13 ------------- reader_test.go | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index e4be7be27..f567c42ca 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -259,7 +259,6 @@ func TestFHFieldInclusionRecordType(t *testing.T) { func TestFHFieldInclusionImmediatDestination(t *testing.T) { fh := mockFileHeader() - //fh.ImmediateDestination = "0" fh.ImmediateDestination = "" if err := fh.Validate(); err != nil { if e, ok := err.(*FieldError); ok { @@ -328,16 +327,4 @@ func TestFHFieldInclusionCreationDate(t *testing.T) { } } } -} - -func TestFHFieldInclusionImmediateDestination(t *testing.T) { - fh := mockFileHeader() - fh.ImmediateDestination = "" - if err := fh.Validate(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { - t.Errorf("%T: %s", err, err) - } - } - } } \ No newline at end of file diff --git a/reader_test.go b/reader_test.go index 7cddcd187..74b74059e 100644 --- a/reader_test.go +++ b/reader_test.go @@ -494,3 +494,22 @@ func TestFileAddendaOutsideEntry(t *testing.T) { t.Errorf("%T: %s", err, err) } } + + +func TestFileFHImmediateOrigin(t *testing.T) { + fh := mockFileHeader() + fh.ImmediateDestination = "" + r := NewReader(strings.NewReader(fh.String())) + // necessary to have a file control not nil + r.File.Control = mockFileControl() + _, err := r.Read() + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } +} \ No newline at end of file From d1e9fdf37e0910fab79b3d989e8da62e8b25ce8f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 13:21:14 -0400 Subject: [PATCH 0070/1694] 170 Modify return from strconv.Atoi 170 Modify return from strconv.Atoi --- entryDetail.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/entryDetail.go b/entryDetail.go index a59aa2a3b..fcb3ca7ee 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -173,11 +173,10 @@ func (ed *EntryDetail) Validate() error { calculated := ed.CalculateCheckDigit(ed.RDFIIdentificationField()) edCheckDigit, err := strconv.Atoi(ed.CheckDigit) - if err != nil { - msg := fmt.Sprintf(msgValidCheckDigit, calculated) - return &FieldError{FieldName: "RDFIIdentification", Value: ed.CheckDigit, Msg: msg} + return err } + if calculated != edCheckDigit { msg := fmt.Sprintf(msgValidCheckDigit, calculated) return &FieldError{FieldName: "RDFIIdentification", Value: ed.CheckDigit, Msg: msg} From 3dc73fe0d51c75fcd389ac66ae5828e35d7e6be4 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 1 May 2018 13:40:02 -0400 Subject: [PATCH 0071/1694] 170 Remove TestFileHeaderExists 170 Remove TestFileHeaderExists --- reader_test.go | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/reader_test.go b/reader_test.go index 74b74059e..127742fbe 100644 --- a/reader_test.go +++ b/reader_test.go @@ -8,8 +8,6 @@ import ( "os" "strings" "testing" - "bytes" - "fmt" ) func TestParseError(t *testing.T) { @@ -440,30 +438,6 @@ func TestFileAddBatchValidation(t *testing.T) { } } -func TestFileHeaderExists(t *testing.T) { - file := mockFilePPD() - buf := new(bytes.Buffer) - w := NewWriter(buf) - w.Write(file) - w.Flush() - r := NewReader(strings.NewReader(buf.String())) - f, err := r.Read() - if err != nil { - if p, ok := err.(*ParseError); ok { - if e, ok := p.Err.(*FileError); ok { - if e.Msg != msgFileHeader { - t.Errorf("%T: %s", e, e) - } - } - } else { - // error is nil if the file was parsed properly. - t.Errorf("%T: %s", err, err) - } - } else { - fmt.Println(f.Header.String()) - } -} - // TestFileLongErr Batch Header Service Class is 000 which does not validate func TestFileLongErr(t *testing.T) { line := "101 076401251 0764012510807291511A094101achdestname companyname 5000companyname origid PPDCHECKPAYMT000002080730 1076401250000001" From 223cdaa1239ea95af195408c0c33f8b12f7f18e2 Mon Sep 17 00:00:00 2001 From: Matias Insaurralde Date: Sun, 6 May 2018 18:04:46 -0400 Subject: [PATCH 0072/1694] Re-use compiled regular expressions when using the validator, related to #166 --- fileHeader_internal_test.go | 11 +++++++++++ validators.go | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index 5cfe9a45a..997190f90 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -42,6 +42,17 @@ func TestMockFileHeader(t *testing.T) { // TestParseFileHeader parses a known File Header Record string. func TestParseFileHeader(t *testing.T) { + parseFileHeader(t) +} + +func BenchmarkParseFileHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + parseFileHeader(b) + } +} + +func parseFileHeader(t testing.TB) { var line = "101 076401251 0764012510807291511A094101achdestname companyname " r := NewReader(strings.NewReader(line)) r.line = line diff --git a/validators.go b/validators.go index d7f71287a..3896a776f 100644 --- a/validators.go +++ b/validators.go @@ -12,6 +12,11 @@ import ( "strconv" ) +var ( + upperAlphanumericRegex = regexp.MustCompile(`[^ A-Z0-9!"#$%&'()*+,-.\\/:;<>=?@\[\]^_{}|~]+`) + alphanumericRegex = regexp.MustCompile(`[^ \w!"#$%&'()*+,-.\\/:;<>=?@\[\]^_{}|~]+`) +) + // validator is common validation and formating of golang types to ach type strings type validator struct{} @@ -241,7 +246,7 @@ func (v *validator) isOriginatorStatusCode(code int) error { // isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters func (v *validator) isUpperAlphanumeric(s string) error { - if regexp.MustCompile(`[^ A-Z0-9!"#$%&'()*+,-.\\/:;<>=?@\[\]^_{}|~]+`).MatchString(s) { + if upperAlphanumericRegex.MatchString(s) { return errors.New(msgUpperAlpha) } return nil @@ -249,7 +254,7 @@ func (v *validator) isUpperAlphanumeric(s string) error { // isAlphanumeric checks if a string only contains ASCII alphanumeric characters func (v *validator) isAlphanumeric(s string) error { - if regexp.MustCompile(`[^ \w!"#$%&'()*+,-.\\/:;<>=?@\[\]^_{}|~]+`).MatchString(s) { + if alphanumericRegex.MatchString(s) { // ^[ A-Za-z0-9_@./#&+-]*$/ return errors.New(msgAlphanumeric) } From d80d0e97ef0c4534d1e4bf0ea490f11305e0f3d5 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 15:24:58 -0600 Subject: [PATCH 0073/1694] Change SprintF to Sprint because we are not adding additional arguments --- reader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reader.go b/reader.go index eda7129eb..73ed0f47f 100644 --- a/reader.go +++ b/reader.go @@ -224,7 +224,7 @@ func (r *Reader) parseAddenda() error { r.recordName = "Addenda" if r.currentBatch == nil { - msg := fmt.Sprintf(msgFileBatchOutside) + msg := fmt.Sprint(msgFileBatchOutside) return r.error(&FileError{FieldName: "Addenda", Msg: msg}) } if len(r.currentBatch.GetEntries()) == 0 { @@ -258,7 +258,7 @@ func (r *Reader) parseAddenda() error { r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda99) } } else { - msg := fmt.Sprintf(msgBatchAddendaIndicator) + msg := fmt.Sprint(msgBatchAddendaIndicator) return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) } From a872836e0933b37f962def4e205e7a07da1039b1 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 15:26:22 -0600 Subject: [PATCH 0074/1694] should omit comparison to bool constant, can be simplified to w/ ! --- addenda99_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addenda99_test.go b/addenda99_test.go index bf341ca44..7e50c6c8a 100644 --- a/addenda99_test.go +++ b/addenda99_test.go @@ -36,7 +36,7 @@ func TestAddenda99Parse(t *testing.T) { if addenda99.OriginalTrace != 99912340000015 { t.Errorf("expected: %v got: %v", 99912340000015, addenda99.OriginalTrace) } - if addenda99.DateOfDeath.IsZero() != true { + if !addenda99.DateOfDeath.IsZero() { t.Errorf("expected: %v got: %v", time.Time{}, addenda99.DateOfDeath) } if addenda99.OriginalDFI != 9101298 { From a1ee40656b5949e5d4892be7a6abc45213b18289 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 15:28:41 -0600 Subject: [PATCH 0075/1694] Removed var msgAddenda98TypeCode is unused --- addenda98.go | 1 - 1 file changed, 1 deletion(-) diff --git a/addenda98.go b/addenda98.go index 3e732d749..b7d1c0abf 100644 --- a/addenda98.go +++ b/addenda98.go @@ -37,7 +37,6 @@ var ( // Error messages specific to Addenda98 msgAddenda98ChangeCode = "found is not a valid addenda Change Code" - msgAddenda98TypeCode = "is not Addenda98 type code of 98" msgAddenda98CorrectedData = "must contain the corrected information corresponding to the Change Code" ) From 5b7a55c923b268acff7d90fef395466e56500bd9 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 15:30:17 -0600 Subject: [PATCH 0076/1694] var msgBatchFieldInclusion is unused --- batcher.go | 1 - 1 file changed, 1 deletion(-) diff --git a/batcher.go b/batcher.go index 1ab88eff2..e68799d88 100644 --- a/batcher.go +++ b/batcher.go @@ -42,7 +42,6 @@ var ( msgBatchHeaderControlEquality = "header %v is not equal to control %v" msgBatchCalculatedControlEquality = "calculated %v is out-of-balance with control %v" msgBatchAscending = "%v is less than last %v. Must be in ascending order" - msgBatchFieldInclusion = "%v is a required field " // specific messages for error msgBatchOriginatorDNE = "%v is not “2” for DNE with entry transaction code of 23 or 33" msgBatchTraceNumberNotODFI = "%v in header does not match entry trace number %v" From fcc1beeca5d461df7c974996988557a29231f428 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 15:31:14 -0600 Subject: [PATCH 0077/1694] var msgFileControlEquality is unused --- file.go | 1 - 1 file changed, 1 deletion(-) diff --git a/file.go b/file.go index 5021e7a0b..51646ba9c 100644 --- a/file.go +++ b/file.go @@ -38,7 +38,6 @@ const ( // Errors strings specific to parsing a Batch container var ( - msgFileControlEquality = "header %v is not equal to control %v" msgFileCalculatedControlEquality = "calculated %v is out-of-balance with control %v" // specific messages msgRecordLength = "must be 94 characters and found %d" From c30ea499aed5381502b998c5d83e91aa627c632b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 15:32:22 -0600 Subject: [PATCH 0078/1694] var msgFileCreationDate is unused --- fileHeader.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fileHeader.go b/fileHeader.go index 49d0f4d46..a32ec496a 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -12,11 +12,10 @@ import ( // Errors specific to a File Header Record var ( - msgRecordType = "received expecting %d" - msgRecordSize = "is not 094" - msgBlockingFactor = "is not 10" - msgFormatCode = "is not 1" - msgFileCreationDate = "was created before " + time.Now().String() + msgRecordType = "received expecting %d" + msgRecordSize = "is not 094" + msgBlockingFactor = "is not 10" + msgFormatCode = "is not 1" ) // FileHeader is a Record designating physical file characteristics and identify From 4b0504533a6185155171b1588815158e93a25328 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 15:34:28 -0600 Subject: [PATCH 0079/1694] const ppd, web,ccd,cor,tel are unused --- file.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/file.go b/file.go index 51646ba9c..75fdfea41 100644 --- a/file.go +++ b/file.go @@ -27,15 +27,6 @@ const ( RecordLength = 94 ) -// currently supported SEC codes -const ( - ppd = "PPD" - web = "WEB" - ccd = "CCD" - cor = "COR" - tel = "TEL" -) - // Errors strings specific to parsing a Batch container var ( msgFileCalculatedControlEquality = "calculated %v is out-of-balance with control %v" From cb179f58ebb896bd22521cbbdd31f161d5eaae10 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 7 May 2018 16:23:12 -0600 Subject: [PATCH 0080/1694] Updated misspelled words --- README.md | 2 +- entryDetail.go | 2 +- validators.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14c6aadf7..36375ab72 100644 --- a/README.md +++ b/README.md @@ -359,7 +359,7 @@ Pull request require a batchMTE_test.go file that covers the logic of the type. ![ACH File Layout](https://github.com/moov-io/ach/blob/master/documentation/ach_file_structure_shg.gif) -## Insperation +## Inspiration * [ACH:Builder - Tools for Building ACH](http://search.cpan.org/~tkeefer/ACH-Builder-0.03/lib/ACH/Builder.pm) * [mosscode / ach](https://github.com/mosscode/ach) * [Helper for building ACH files in Ruby](https://github.com/jm81/ach) diff --git a/entryDetail.go b/entryDetail.go index fcb3ca7ee..0fad56f29 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -292,7 +292,7 @@ func (ed *EntryDetail) PaymentTypeField() string { return ed.DiscretionaryData } -// SetPaymentType as R (Reccuring) all other values will result in S (single) +// SetPaymentType as R (Recurring) all other values will result in S (single) func (ed *EntryDetail) SetPaymentType(t string) { t = strings.ToUpper(strings.TrimSpace(t)) if t == "R" { diff --git a/validators.go b/validators.go index d7f71287a..4430ce348 100644 --- a/validators.go +++ b/validators.go @@ -17,7 +17,7 @@ type validator struct{} // FieldError is returned for errors at a field level in a record type FieldError struct { - FieldName string // field name where error happend + FieldName string // field name where error happened Value string // value that cause error Msg string // context of the error. } From 7fee1b59b589b7fc09355c7c4b2d1a83b8955967 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 10 May 2018 16:17:56 -0400 Subject: [PATCH 0081/1694] #165 ach file write and read from command line #165 --- README.md | 33 +- cmd/readACH/201805101354.ach | 10010 ++++++++++++++++++++++++++++++++ cmd/readACH/main.go | 52 + cmd/readACH/main_test.go | 20 + cmd/readACH/old | 32 + cmd/readACH/readACH.pprof | Bin 0 -> 1009 bytes cmd/writeACH/201805101354.ach | 10010 ++++++++++++++++++++++++++++++++ cmd/writeACH/201805101355.ach | 10010 ++++++++++++++++++++++++++++++++ cmd/writeACH/main.go | 107 + cmd/writeACH/main_test.go | 23 + cmd/writeACH/old | 30 + cmd/writeACH/writeACH.pprof | Bin 0 -> 1306 bytes 12 files changed, 30326 insertions(+), 1 deletion(-) create mode 100644 cmd/readACH/201805101354.ach create mode 100644 cmd/readACH/main.go create mode 100644 cmd/readACH/main_test.go create mode 100644 cmd/readACH/old create mode 100644 cmd/readACH/readACH.pprof create mode 100644 cmd/writeACH/201805101354.ach create mode 100644 cmd/writeACH/201805101355.ach create mode 100644 cmd/writeACH/main.go create mode 100644 cmd/writeACH/main_test.go create mode 100644 cmd/writeACH/old create mode 100644 cmd/writeACH/writeACH.pprof diff --git a/README.md b/README.md index 36375ab72..12cda61ce 100644 --- a/README.md +++ b/README.md @@ -343,9 +343,40 @@ case "MTE": return NewBatchMTE(bh), nil //... ``` - Pull request require a batchMTE_test.go file that covers the logic of the type. +Command line ACH file write, read, test, benchmark, profiling + +**Write:** + +github.com/moov-io/ach/cmd/writeACH + +main.go will create an ACH file with 4 batches each containing 1250 detail and addenda records + +A custom path can be used by defining fPath ( e.g. -fPath=github.com/moov-io/_directory_ ) + +Benchmark + +github.com/moov-io/ach/cmd/writeACH>go test -bench=BenchmarkTestFileWrite -count=5 > old + +Profiling + +github.com/moov-io/ach/cmd/writeACH>main -cpuprofile=writeACH.pprof + +**Read:** + +github.com/moov-io/ach/cmd/readACH + +Use fPath to define the file to be read ( e.g. -fPath=github.com/moov-io/ach/cmd/readACH/_filename_ ) + +Benchmark + +github.com/moov-io/ach/cmd/readACH>go test -bench=BenchmarkTestFileRead -count=5 > old + +Profiling + +github.com/moov-io/ach/cmd/readACH>main -fPath=_filename_ -cpuprofile=ReadACH.pprof + ## References * [Wikipeda: Automated Clearing House](http://en.wikipedia.org/wiki/Automated_Clearing_House) * [Nacha ACH Network: How it Works](https://www.nacha.org/ach-network) diff --git a/cmd/readACH/201805101354.ach b/cmd/readACH/201805101354.ach new file mode 100644 index 000000000..7bab11094 --- /dev/null +++ b/cmd/readACH/201805101354.ach @@ -0,0 +1,10010 @@ +101 231380104 1210428821805100000A094101Citadel Wells Fargo +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000001 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#g2c4up44DsnU6#Lily Taylor 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Elijah Johnson 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Emma White 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Natalie Thompson 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Joshua Moore 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Aubrey Harris 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Aiden Taylor 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#caAIqhsZgOclH#Aubrey Harris 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#WUTU148g1HtSt#Abigail Miller 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#jkcOD2PnETieL#Noah Jones 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Isabella Williams 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ethan Thomas 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Madison Moore 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Alexander Thompson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Joshua Miller 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Sofia Williams 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000001 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000002 +62223138010481967038518 0000100000#3vKiBFiGxVHwO#Ethan Williams 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#3vKiBFiGxVHwO#Ethan Williams 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#3vKiBFiGxVHwO#Ethan Williams 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#9Yz8CRYozKku9#William Martin 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Zoey Johnson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Isabella Williams 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Ethan Thompson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#6GCinaos17WMn#Joshua Martinez 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Emily Moore 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Olivia Thomas 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Aiden Taylor 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Elizabeth Davis 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Chloe Anderson 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Mason Johnson 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Emma Garcia 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#W9k5pTgp350rB#Ava Brown 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Joshua Taylor 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Elizabeth Wilson 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#MMnbqxV85siJF#Charlotte Martinez 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Sofia Moore 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#Ava Brown 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Benjamin Martin 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#wBch3otsORgxX#Ava Brown 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#qRxYDnBse1idd#William Taylor 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000002 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000003 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#clkNfFCXHhThO#Sofia Harris 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Aiden Taylor 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Elizabeth Anderson 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Daniel Thomas 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#9fymHdW38k1tP#Elijah Thomas 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Emma Jackson 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#Elijah Brown 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#e6lK8hokrBoGK#William Smith 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Benjamin Martin 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Lily Jones 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#Zoey Brown 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#William Harris 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Anthony Davis 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Avery Jackson 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Mia Jackson 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#William Anderson 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000003 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000004 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Daniel White 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#yzTZKqaeAPlek#Addison Brown 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Sofia Wilson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Sophia Smith 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#6icCe6e1VijvW#Alexander White 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#H80hpwgN6RvCv#Addison Martinez 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#KWceVpQPacGbf#David Wilson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Avery Jackson 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#48D8RQSKunQt0#Matthew Thomas 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#8D7NKKP5yG9pt#Daniel Anderson 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#8D7NKKP5yG9pt#Daniel Anderson 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#lGlkAIAapidNq#Ella Garcia 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#cL2zLXGthWenB#Sophia Smith 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Abigail Miller 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#sb41synxZqze1#Emily Wilson 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000004 +9000004001001000100005690050000000000000000000500000000 diff --git a/cmd/readACH/main.go b/cmd/readACH/main.go new file mode 100644 index 000000000..1c8305a6a --- /dev/null +++ b/cmd/readACH/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "log" + "os" + + "flag" + "github.com/moov-io/ach" + "runtime/pprof" +) + +func main() { + + var fPath = flag.String("fPath", "201805101354.ach", "File Path") + var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + path := *fPath + + // open a file for reading. Any io.Reader Can be used + f, err := os.Open(path) + + if err != nil { + log.Panicf("Can not open file: %s: \n", err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formating is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("total amount debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("total amount credit: %v \n", achFile.Control.TotalCreditEntryDollarAmountInFile) +} diff --git a/cmd/readACH/main_test.go b/cmd/readACH/main_test.go new file mode 100644 index 000000000..a9eae0fd3 --- /dev/null +++ b/cmd/readACH/main_test.go @@ -0,0 +1,20 @@ +package main + +import "testing" + +func TestFileRead(t *testing.T) { + FileRead(t) +} + +//BenchmarkTestFileCreate benchmarks creating an ACH File +func BenchmarkTestFileRead(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + FileRead(b) + } +} + +// FileCreate creates an ACH File +func FileRead(t testing.TB) { + main() +} diff --git a/cmd/readACH/old b/cmd/readACH/old new file mode 100644 index 000000000..1000c961b --- /dev/null +++ b/cmd/readACH/old @@ -0,0 +1,32 @@ +total amount debit: 0 +total amount credit: 500000000 +C:\Users\Owner\AppData\Local\Temp\go-build543880490\b001\readACH.test.exe flag redefined: fPath +--- FAIL: TestFileRead (0.00s) +panic: C:\Users\Owner\AppData\Local\Temp\go-build543880490\b001\readACH.test.exe flag redefined: fPath [recovered] + panic: C:\Users\Owner\AppData\Local\Temp\go-build543880490\b001\readACH.test.exe flag redefined: fPath + +goroutine 20 [running]: +testing.tRunner.func1(0xc0420ac2d0) + C:/Go/src/testing/testing.go:742 +0x2a4 +panic(0x532f80, 0xc042369d70) + C:/Go/src/runtime/panic.go:505 +0x237 +flag.(*FlagSet).Var(0xc042092000, 0x587060, 0xc042369d30, 0x56988e, 0x5, 0x56a171, 0x9) + C:/Go/src/flag/flag.go:810 +0x547 +flag.(*FlagSet).StringVar(0xc042092000, 0xc042369d30, 0x56988e, 0x5, 0x56bb10, 0x10, 0x56a171, 0x9) + C:/Go/src/flag/flag.go:713 +0x92 +flag.(*FlagSet).String(0xc042092000, 0x56988e, 0x5, 0x56bb10, 0x10, 0x56a171, 0x9, 0xc0420b0000) + C:/Go/src/flag/flag.go:726 +0x92 +flag.String(0x56988e, 0x5, 0x56bb10, 0x10, 0x56a171, 0x9, 0x0) + C:/Go/src/flag/flag.go:733 +0x70 +github.com/bkmoovio/ach/cmd/readACH.main() + C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/readACH/main.go:15 +0x7c +github.com/bkmoovio/ach/cmd/readACH.FileRead(0x588100, 0xc0420ac2d0) + C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/readACH/main_test.go:20 +0x27 +github.com/bkmoovio/ach/cmd/readACH.TestFileRead(0xc0420ac2d0) + C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/readACH/main_test.go:6 +0x3e +testing.tRunner(0xc0420ac2d0, 0x575c00) + C:/Go/src/testing/testing.go:777 +0xd7 +created by testing.(*T).Run + C:/Go/src/testing/testing.go:824 +0x2e7 +exit status 2 +FAIL github.com/bkmoovio/ach/cmd/readACH 4.474s diff --git a/cmd/readACH/readACH.pprof b/cmd/readACH/readACH.pprof new file mode 100644 index 0000000000000000000000000000000000000000..7e210648a8dc4c062f6f7d6dd814ee0e1e2603d5 GIT binary patch literal 1009 zcmV~1#uBx#cAh$TDH8GS*KcuJFM zD~*PTf}WhrzD|bh&MY&tF}W!sf*@!Qy?6|QAg#0sB^KI?nh4rc6%~6DKYHlFgPw9w zo;M#2CL!jsEc1Kb-~V~u_c{0Hjn(_VUi|J-EuUryNaoWl1?lMzZeMm@m49D*^YSlg zU1bSex%XL8&#(l1kuVrbKn62fkFK&LUR%COBne*;NkSI08q-yl!u2axiKO7Cq`_DU za+s4Ojh}8xl7_XE!B`sdnAZxr$}+g|mn0eZh)4#CSd^p(H}75}TMw)d=>Y{5P1RMF z#m(Q>h-Bd{%E>|rOB(1ZWBBu}>qHnVQ*R8)Sk@}K%5wPi4S6O9KM=`56{}jWuChG- z@v$U%c$Y{XYFN{Hb%ho1?)z&=Jjb6$WuoJE5zr zgv(#b@k?-rR3#X~Avr$a`rq=((y*MW)OOTGwFG<3W&H5=w)uHoWffdmmp*{s$OkZt z!_rs9J3Ei_c}iDVFW$VeN<)<48>%eB2##o)uCN;Z_|7UdTY=LCV-+}wCne~^jkm88 zs6yRftO`2n+QYia>iEwm+n0MWQK|3rs+KEN>b1AlS1%M5{t(YbcDvIO5zjb5*N=J1 z>2!I)xBVazPT)5qJ`xI7EOxAD$&c+tbJRHRM{&cPvSVj%rt1qnGBIX78(2~3SfOym zZmHD?1J`r|KD=cxM~$}a%z3_Om~pst#&+i8(01ne(8Sn(1hYGgjt}lXzRUT*E}W6s z4YL`XT@+3?7996^#iDRXi3gmTqJj8{eMEkLF%CW7O5wyy1(`asw+09n19& zGFiKxi1N%s^UnHlxHKtZ+iNwl0%!X7^~Y)05k5jM<@9Y$hLGJo<| zd7;!$l=1(fE_lA+j~rP`?eSdO_I#6m`1p}s&Cb*7Z9Sglw41WE@riR5ou>wiVb_nn zw%8K(?^%hh@3i-F%PP^5@0g>;R5uoj4f8_a%=7w#a-vq77+HI_H)(WtkzElV@U_#8 z`D2HUJxfd6Fumv*PqdoneW%rpyujyA962@(y}h!Qv`|y0jtW{3q4)X{fArA0Gt|)Z f^yHpJeCqhUOixdmZt!ma00960P}>{?MF#)?93T7_ literal 0 HcmV?d00001 diff --git a/cmd/writeACH/201805101354.ach b/cmd/writeACH/201805101354.ach new file mode 100644 index 000000000..7bab11094 --- /dev/null +++ b/cmd/writeACH/201805101354.ach @@ -0,0 +1,10010 @@ +101 231380104 1210428821805100000A094101Citadel Wells Fargo +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000001 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#KnhT6dHNOpUN3#Lily Martin 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#g2c4up44DsnU6#Lily Taylor 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#g2c4up44DsnU6#Elizabeth Taylor 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#jMsroTi0Z44Rx#Olivia Jones 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#VPZDeKLSorlkP#Mia Wilson 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#Joy8Qmdc3V5JT#Elijah Jackson 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Elijah Johnson 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#OH8ap2hRc7v6S#Emma Johnson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Emma White 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#1qMcmmsMWFCHI#Addison White 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Natalie Thompson 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#9Bi5N80svoNJF#Joshua Thompson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Joshua Moore 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#lPIfNp4r6BV5F#Alexander Moore 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#uFeYOF3QSaSEB#Olivia Jones 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#husWmwDGavS10#Zoey Robinson 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Aubrey Harris 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#l1wVYNOIJiDRm#Anthony Harris 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Aiden Taylor 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#rxozmMB7HOpEy#Elizabeth Taylor 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#caAIqhsZgOclH#Aubrey Harris 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#caAIqhsZgOclH#Anthony Harris 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#x1jHmTjFyyYh4#Ella Thomas 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#WUTU148g1HtSt#Abigail Miller 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#WUTU148g1HtSt#Jayden Miller 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#jkcOD2PnETieL#Noah Jones 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#jkcOD2PnETieL#Olivia Jones 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Isabella Williams 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#hEnRfOAU59cIc#Ethan Williams 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ethan Thomas 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#fbWjGodTNtPGD#Ella Thomas 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#VnakyV86yqfKh#Daniel Anderson 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#Klz2TULxetARj#William Brown 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#4Tp1xCeUjGQlg#Lily Martin 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#o8KQbv7XgoeWm#Mia Wilson 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Madison Moore 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#IZ9w1ixd38wUo#Alexander Moore 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Alexander Thompson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#4LShQ8fqlNbRQ#Joshua Thompson 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Joshua Miller 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#K3kaZKZBLSVBS#Jayden Miller 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#LyWCFfgwoNhPk#Emily Davis 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#FnM5tBsNggfp5#Elizabeth Taylor 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#ptsoeILPGIxTL#Sofia Garcia 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Sofia Williams 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#z4XwTkKCrOeul#Ethan Williams 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000001 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000002 +62223138010481967038518 0000100000#3vKiBFiGxVHwO#Ethan Williams 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#3vKiBFiGxVHwO#Ethan Williams 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#3vKiBFiGxVHwO#Ethan Williams 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#FgpSck5eoiHaN#William Brown 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#KHLGE7vFlyb6K#William Brown 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#9Yz8CRYozKku9#William Martin 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#9Yz8CRYozKku9#Lily Martin 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#kNWslfnAm0HAt#Zoey Robinson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Zoey Johnson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#HDjHFVH5XnIFJ#Emma Johnson 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Isabella Williams 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#Y1tPhAyZcU04W#Ethan Williams 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Ethan Thompson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#7TU8vZeSUYhUO#Joshua Thompson 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#6GCinaos17WMn#Joshua Martinez 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#6GCinaos17WMn#David Martinez 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#aRl8w7fy4KslB#Lily Martin 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#COPODfOzBYFbH#Emily Davis 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Emily Moore 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#rApdrY6YfuPYg#Alexander Moore 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#1dCiTwfhUEFgu#Olivia Jones 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Olivia Thomas 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#A6agcpDEE2SxJ#Ella Thomas 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#POtziD6aUv2EV#William Brown 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#DDicHH92JTVzl#Jayden Miller 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Aiden Taylor 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#5mgzNGI4oQeA7#Elizabeth Taylor 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Elizabeth Davis 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#rJZzKcRZ3kOFp#Emily Davis 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#2XnXwH9aORiBf#Mia Wilson 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#ny5kRfeOxny5K#Sofia Garcia 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Chloe Anderson 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#8A51ft2CdoHGI#Daniel Anderson 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Mason Johnson 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#xLbDDXNecS1XW#Emma Johnson 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Emma Garcia 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#DHu7cITnNKaM6#Sofia Garcia 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#W9k5pTgp350rB#Ava Brown 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#W9k5pTgp350rB#William Brown 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#ylfzXyDdE05MM#Joshua Thompson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Joshua Taylor 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#SEPMKcDjlXmdz#Elizabeth Taylor 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Elizabeth Wilson 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#PEKmKjcZEu6xL#Mia Wilson 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#MMnbqxV85siJF#Charlotte Martinez 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#MMnbqxV85siJF#David Martinez 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#duqXFsoZeq7R4#Daniel Anderson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#VPNWYBxbq1cV0#Sofia Garcia 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Sofia Moore 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#XVQb0DJy22jLF#Alexander Moore 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#f9LmvDTl2RGEb#Joshua Thompson 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#bqSMS2gv0lFQA#Emma Johnson 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#Ava Brown 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#ZpqPerIgaRxcD#William Brown 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Benjamin Martin 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#NDXlpqjNXfmKe#Lily Martin 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#tUox6ECWq6HnU#Ella Thomas 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#wBch3otsORgxX#Ava Brown 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#wBch3otsORgxX#William Brown 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#qRxYDnBse1idd#William Taylor 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#qRxYDnBse1idd#Elizabeth Taylor 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#7pPQBzh0bKcYO#Ella Thomas 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#2FcuepymRrlFR#Daniel Anderson 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#DRCGmhLz3UDC6#Elijah Jackson 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000002 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000003 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#fFoB2FbyNAFTy#Emma Johnson 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#QZCX0FUPKnCAa#Sofia Garcia 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#clkNfFCXHhThO#Sofia Harris 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#clkNfFCXHhThO#Anthony Harris 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Aiden Taylor 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#OdD4so0Wkv63L#Elizabeth Taylor 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Elizabeth Anderson 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#aTDFftwcUd6tw#Daniel Anderson 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Daniel Thomas 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#6sLjFKzW9wlij#Ella Thomas 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#uk3SngQPJ420i#Elijah Jackson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#9fymHdW38k1tP#Elijah Thomas 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#9fymHdW38k1tP#Ella Thomas 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#cMg8ZWs4kjclh#Emma Johnson 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#kbSjaIDNtkEZC#Emily Davis 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#U0IpLWUXjSEZ1#Emma Johnson 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#cvMYzs72MOl9M#Emma Johnson 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Emma Jackson 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#skuhX7AXMmYb8#Elijah Jackson 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#Elijah Brown 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#b8pIyAGCdVzNj#William Brown 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#e6lK8hokrBoGK#William Smith 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#e6lK8hokrBoGK#Jacob Smith 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Benjamin Martin 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#9CML5ISKlX9JO#Lily Martin 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Lily Jones 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#3zYaj10ufgNOm#Olivia Jones 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#h7caX3Uo7w5O5#Zoey Robinson 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#Zoey Brown 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#9mcEFVyZNBqiG#William Brown 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#William Harris 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#fgtSrfFAx4mUp#Anthony Harris 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Anthony Davis 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#Ed6drEVxMb83r#Emily Davis 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#3jgxuB3igPWmP#Elizabeth Taylor 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Avery Jackson 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#Dl7Wa2FgRuN2S#Elijah Jackson 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#EOI6sG9rbGvbF#Mia Wilson 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Mia Jackson 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#vAIRRZPGXc0yZ#Elijah Jackson 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#CPkyeasBgoHkX#Olivia Jones 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#KaoRNhiH9VGaZ#Joshua Thompson 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#xOOu5t33WLpZQ#Emily Davis 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#syPORJb6kSrU8#William Brown 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#William Anderson 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#9SFX0fWVG8Tzn#Daniel Anderson 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#k44W4Dz1huW2W#Anthony Harris 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000003 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000004 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#Fg8U8Rt9qiqxr#Sofia Garcia 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#RDQEdcEJZpOcf#Daniel Anderson 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Daniel White 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#1oqPqxiM93gBj#Addison White 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#yzTZKqaeAPlek#Addison Brown 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#yzTZKqaeAPlek#William Brown 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#6qM79BQJvDDVe#Sofia Garcia 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Sofia Wilson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#fGGgRdtrx4YNB#Mia Wilson 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#PNk2Q9YM1HSVX#Ella Thomas 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Sophia Smith 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#NhWhHfdJXFyZb#Jacob Smith 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#MH6BMjPVo3ZoR#Alexander Moore 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#6icCe6e1VijvW#Alexander White 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#6icCe6e1VijvW#Addison White 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#H80hpwgN6RvCv#Addison Martinez 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#H80hpwgN6RvCv#David Martinez 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#KWceVpQPacGbf#David Wilson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#KWceVpQPacGbf#Mia Wilson 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Avery Jackson 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#67zVsGV2WeIvz#Elijah Jackson 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#48D8RQSKunQt0#Matthew Thomas 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#48D8RQSKunQt0#Ella Thomas 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#ktxiT9xpH9dWh#Emma Johnson 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#8D7NKKP5yG9pt#Daniel Anderson 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#8D7NKKP5yG9pt#Daniel Anderson 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#guCNUGWyo0sqJ#Alexander Moore 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#GR6Ob4BHIONPm#Ella Thomas 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#lGlkAIAapidNq#Ella Garcia 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#lGlkAIAapidNq#Sofia Garcia 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#cL2zLXGthWenB#Sophia Smith 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#cL2zLXGthWenB#Jacob Smith 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#V2XlW6LOSAOI4#Ethan Williams 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#GqLneLsSQniLY#Olivia Jones 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Abigail Miller 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#LUnvxWvasQEVp#Jayden Miller 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#j9YWW2NZE35Wn#Jayden Miller 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#odOqAKb0mIDOA#Emily Davis 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#sb41synxZqze1#Emily Wilson 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#sb41synxZqze1#Mia Wilson 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#mDuzsBSFnomN9#Jacob Smith 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#1QywIDvT0IuMR#Anthony Harris 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#Ozgz87ZccMqeg#Olivia Jones 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000004 +9000004001001000100005690050000000000000000000500000000 diff --git a/cmd/writeACH/201805101355.ach b/cmd/writeACH/201805101355.ach new file mode 100644 index 000000000..3ebc86628 --- /dev/null +++ b/cmd/writeACH/201805101355.ach @@ -0,0 +1,10010 @@ +101 231380104 1210428821805100000A094101Citadel Wells Fargo +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000001 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#RCP2bSsZWeCCE#Alexander Moore 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Alexander White 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#0wvMj6gEc2YVZ#Addison White 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#PdeE1oGEvIt9B#Elijah Jackson 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#DeKJU7RcZL5Sc#Elizabeth Taylor 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Natalie Thompson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#RqC9kaErtyTXc#Joshua Thompson 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Joshua Davis 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#zncFCSsFIQjLZ#Emily Davis 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#GeqMftiuLza0G#Lily Martin 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#PBLQ7P1ORpj8z#Joshua Thompson 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#6MioPHg74TgsP#Joshua Wilson 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#6MioPHg74TgsP#Mia Wilson 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#drJPHD6ZooZXd#Jacob Smith 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#8zLmN5tRw4DAs#Mia Wilson 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#3W68qcMvpkHe3#Jacob Smith 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Jacob Jones 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#njGsJ6Stjfewf#Olivia Jones 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#cWXuri972NX5k#Olivia Jones 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#8i08En4CENEic#Olivia Robinson 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#8i08En4CENEic#Zoey Robinson 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#mf29AAdc6JACS#Mia Wilson 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Isabella Williams 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#bZ8RL94WWphmf#Ethan Williams 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#RZxyJ30J4wKf5#Lily Martin 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Lily Williams 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#7jGxeM21hK35Y#Ethan Williams 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#u07sfEG88QdUQ#William Brown 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#rgTlx1F9gmHIR#Ella Thomas 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#QU1KwpOlDf6Ej#Jayden Miller 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Jayden Anderson 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#pOkxTlUtVJMqk#Daniel Anderson 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Liam Davis 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#bgj1OvxdA7QyC#Emily Davis 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#NkNbJ7t0FZ9Ku#Lily Martin 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Lily Anderson 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#akgfB6zmfisPZ#Daniel Anderson 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Andrew Garcia 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#kU6b9Wd8LaJnn#Sofia Garcia 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Sophia Smith 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#wjP6OzoCWmfd1#Jacob Smith 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Jacob Harris 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#AMkEl3GhL0wtL#Anthony Harris 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Noah Jones 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#9Ttu6gV9hoCGB#Olivia Jones 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#LhLH3f2WfAzQS#Addison White 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#XHzAppn5DzTT4#Jacob Smith 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#Scd8hPCmAXopW#Addison White 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#MVFHEGA606itb#Addison Martin 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#MVFHEGA606itb#Lily Martin 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Madison Moore 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#MUWKV1mQlttXp#Alexander Moore 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#kFDPQpFxBX2fy#Olivia Jones 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Olivia Harris 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Anthony Harris 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Anthony Harris 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Anthony Harris 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Anthony Harris 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Anthony Harris 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Anthony Harris 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#IzkrRSgEObtqr#Anthony Harris 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000001 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000002 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#UblwC2QbaSgXW#Elijah Jackson 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#fbSblT11POutp#Lily Martin 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Lily Garcia 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#ueU3dbZ2PSZwB#Sofia Garcia 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#JsJvCEjt2ccRG#Ethan Williams 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#S2pC69N62jFlf#Joshua Thompson 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Joshua Anderson 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#5UYSJD0qQPpGj#Daniel Anderson 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#CxVGsgyXGcfPW#Jacob Smith 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#x4eueXsuua05I#Anthony Harris 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#TvpBT2cs8n58E#David Martinez 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#zpvwqt4IJs137#Daniel Anderson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#qQSukNLhk140y#Daniel Moore 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#qQSukNLhk140y#Alexander Moore 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Alexander Jones 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#KSROnbEzOzZz5#Olivia Jones 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#qGfC4CEkKi5ty#Olivia Jones 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#Zf9gxgQdwtwjb#Elijah Jackson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#9CIelVIfeAByE#Aiden Taylor 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#9CIelVIfeAByE#Elizabeth Taylor 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Elizabeth Thompson 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#3M2BkWkB9nKTG#Joshua Thompson 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#Mk3vmQ29Wqp6P#David Martinez 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#David Brown 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#sAQg9HmBHf3P9#William Brown 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#yjryMojKJvGFH#Michael Wilson 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#yjryMojKJvGFH#Mia Wilson 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#UlYDGHuV1J6ZN#Joshua Thompson 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Joshua Martin 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#DLkXQKNIxxpsW#Lily Martin 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#Y9L2slAFJy3Ql#Olivia Jones 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Olivia Anderson 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#DhI6DBwlsf5EO#Daniel Anderson 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#xBDzlUlZ3R0jS#Sofia Garcia 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#ALvpOrtgTeGXn#William Brown 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#a2E5OeM3niFh0#Emma Johnson 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#n3JeXvOmMVOia#Charlotte Martinez 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#n3JeXvOmMVOia#David Martinez 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#wVroM4XbzKmLE#William Brown 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#William Thomas 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#MgSwIoaqd9HFn#Ella Thomas 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#0HIKgOVOQQKfU#Sofia Garcia 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#i78CAmFG0zV4o#Anthony Harris 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000002 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000003 +62223138010481967038518 0000100000#KSjXqrPKbL3QN#Daniel Anderson 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#KSjXqrPKbL3QN#Daniel Anderson 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#KSjXqrPKbL3QN#Daniel Anderson 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#KSjXqrPKbL3QN#Daniel Anderson 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Daniel Jones 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#zGKpc8VDEhX0N#Olivia Jones 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#Pcr57pdyMjUpk#William Brown 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#x4APZn9K6uofd#Michael Wilson 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#x4APZn9K6uofd#Mia Wilson 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#qBORKg23OkXFB#Mia Miller 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#qBORKg23OkXFB#Jayden Miller 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#H0tJGmF71ryM6#Elijah Jackson 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#4QaGLFW4kM4lV#Emily Davis 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#3vH8OQEFduqif#Emily Thompson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#3vH8OQEFduqif#Joshua Thompson 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#jyJybX88Qpndo#Sofia Garcia 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#gvbQRxek0Dn1v#Zoey Robinson 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#upJa3WQRvPggQ#Jayden Miller 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Jayden Robinson 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#dnh0jlYKCxuOO#Zoey Robinson 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#JCQdA4HRODPk3#Ethan Williams 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#0s1hyqkGho2Vo#Elijah Jackson 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#BA2xs4e7S9Seu#Anthony Harris 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#Anthony Martinez 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#FQDvrkpLXWUs0#David Martinez 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#fAI9xBZUPYye4#Ethan Williams 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#BkkafNgLCvTvR#Ella Thomas 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#P6shUsdPn8luO#Emma Johnson 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Abigail Miller 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#rsTxrXxzgSEAt#Jayden Miller 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#uGHgH7nEqmcET#Alexander Moore 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#fZdKWH6VnQ7Rs#Daniel Anderson 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Matthew Thomas 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#kaiUzHemlLqDk#Ella Thomas 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Ella White 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#jV5YV4idFE0ew#Addison White 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Addison Garcia 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#SaGqNIVH7tyeR#Sofia Garcia 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#cZEuVnhgitvJO#Lily Martin 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#cC4Vn4Ifx1ADV#Ethan Williams 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#3B78wXZgkf36m#Ethan Martin 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#3B78wXZgkf36m#Lily Martin 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000003 +5200Wells Fargo 121042882 PPDTrans. Des 180511 0121042880000004 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000001 +705bonus pay for amazing work on #OSS 00010000001 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000002 +705bonus pay for amazing work on #OSS 00010000002 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000003 +705bonus pay for amazing work on #OSS 00010000003 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000004 +705bonus pay for amazing work on #OSS 00010000004 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000005 +705bonus pay for amazing work on #OSS 00010000005 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000006 +705bonus pay for amazing work on #OSS 00010000006 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000007 +705bonus pay for amazing work on #OSS 00010000007 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000008 +705bonus pay for amazing work on #OSS 00010000008 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000009 +705bonus pay for amazing work on #OSS 00010000009 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000010 +705bonus pay for amazing work on #OSS 00010000010 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000011 +705bonus pay for amazing work on #OSS 00010000011 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000012 +705bonus pay for amazing work on #OSS 00010000012 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000013 +705bonus pay for amazing work on #OSS 00010000013 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000014 +705bonus pay for amazing work on #OSS 00010000014 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000015 +705bonus pay for amazing work on #OSS 00010000015 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000016 +705bonus pay for amazing work on #OSS 00010000016 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000017 +705bonus pay for amazing work on #OSS 00010000017 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000018 +705bonus pay for amazing work on #OSS 00010000018 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000019 +705bonus pay for amazing work on #OSS 00010000019 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000020 +705bonus pay for amazing work on #OSS 00010000020 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000021 +705bonus pay for amazing work on #OSS 00010000021 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000022 +705bonus pay for amazing work on #OSS 00010000022 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000023 +705bonus pay for amazing work on #OSS 00010000023 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000024 +705bonus pay for amazing work on #OSS 00010000024 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000025 +705bonus pay for amazing work on #OSS 00010000025 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000026 +705bonus pay for amazing work on #OSS 00010000026 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000027 +705bonus pay for amazing work on #OSS 00010000027 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000028 +705bonus pay for amazing work on #OSS 00010000028 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000029 +705bonus pay for amazing work on #OSS 00010000029 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000030 +705bonus pay for amazing work on #OSS 00010000030 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000031 +705bonus pay for amazing work on #OSS 00010000031 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000032 +705bonus pay for amazing work on #OSS 00010000032 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000033 +705bonus pay for amazing work on #OSS 00010000033 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000034 +705bonus pay for amazing work on #OSS 00010000034 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000035 +705bonus pay for amazing work on #OSS 00010000035 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000036 +705bonus pay for amazing work on #OSS 00010000036 +62223138010481967038518 0000100000#W12MwGwKDYYde#Elizabeth Taylor 1121042880000037 +705bonus pay for amazing work on #OSS 00010000037 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Elizabeth Robinson 1121042880000038 +705bonus pay for amazing work on #OSS 00010000038 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000039 +705bonus pay for amazing work on #OSS 00010000039 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000040 +705bonus pay for amazing work on #OSS 00010000040 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000041 +705bonus pay for amazing work on #OSS 00010000041 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000042 +705bonus pay for amazing work on #OSS 00010000042 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000043 +705bonus pay for amazing work on #OSS 00010000043 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000044 +705bonus pay for amazing work on #OSS 00010000044 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000045 +705bonus pay for amazing work on #OSS 00010000045 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000046 +705bonus pay for amazing work on #OSS 00010000046 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000047 +705bonus pay for amazing work on #OSS 00010000047 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000048 +705bonus pay for amazing work on #OSS 00010000048 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000049 +705bonus pay for amazing work on #OSS 00010000049 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000050 +705bonus pay for amazing work on #OSS 00010000050 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000051 +705bonus pay for amazing work on #OSS 00010000051 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000052 +705bonus pay for amazing work on #OSS 00010000052 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000053 +705bonus pay for amazing work on #OSS 00010000053 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000054 +705bonus pay for amazing work on #OSS 00010000054 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000055 +705bonus pay for amazing work on #OSS 00010000055 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000056 +705bonus pay for amazing work on #OSS 00010000056 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000057 +705bonus pay for amazing work on #OSS 00010000057 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000058 +705bonus pay for amazing work on #OSS 00010000058 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000059 +705bonus pay for amazing work on #OSS 00010000059 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000060 +705bonus pay for amazing work on #OSS 00010000060 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000061 +705bonus pay for amazing work on #OSS 00010000061 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000062 +705bonus pay for amazing work on #OSS 00010000062 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000063 +705bonus pay for amazing work on #OSS 00010000063 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000064 +705bonus pay for amazing work on #OSS 00010000064 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000065 +705bonus pay for amazing work on #OSS 00010000065 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000066 +705bonus pay for amazing work on #OSS 00010000066 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000067 +705bonus pay for amazing work on #OSS 00010000067 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000068 +705bonus pay for amazing work on #OSS 00010000068 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000069 +705bonus pay for amazing work on #OSS 00010000069 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000070 +705bonus pay for amazing work on #OSS 00010000070 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000071 +705bonus pay for amazing work on #OSS 00010000071 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000072 +705bonus pay for amazing work on #OSS 00010000072 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000073 +705bonus pay for amazing work on #OSS 00010000073 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000074 +705bonus pay for amazing work on #OSS 00010000074 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000075 +705bonus pay for amazing work on #OSS 00010000075 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000076 +705bonus pay for amazing work on #OSS 00010000076 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000077 +705bonus pay for amazing work on #OSS 00010000077 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000078 +705bonus pay for amazing work on #OSS 00010000078 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000079 +705bonus pay for amazing work on #OSS 00010000079 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000080 +705bonus pay for amazing work on #OSS 00010000080 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000081 +705bonus pay for amazing work on #OSS 00010000081 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000082 +705bonus pay for amazing work on #OSS 00010000082 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000083 +705bonus pay for amazing work on #OSS 00010000083 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000084 +705bonus pay for amazing work on #OSS 00010000084 +62223138010481967038518 0000100000#lWUA5eUgxQmCe#Zoey Robinson 1121042880000085 +705bonus pay for amazing work on #OSS 00010000085 +62223138010481967038518 0000100000#GOxlEZaHxO64c#Charlotte Martinez 1121042880000086 +705bonus pay for amazing work on #OSS 00010000086 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000087 +705bonus pay for amazing work on #OSS 00010000087 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000088 +705bonus pay for amazing work on #OSS 00010000088 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000089 +705bonus pay for amazing work on #OSS 00010000089 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000090 +705bonus pay for amazing work on #OSS 00010000090 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000091 +705bonus pay for amazing work on #OSS 00010000091 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000092 +705bonus pay for amazing work on #OSS 00010000092 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000093 +705bonus pay for amazing work on #OSS 00010000093 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000094 +705bonus pay for amazing work on #OSS 00010000094 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000095 +705bonus pay for amazing work on #OSS 00010000095 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000096 +705bonus pay for amazing work on #OSS 00010000096 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000097 +705bonus pay for amazing work on #OSS 00010000097 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000098 +705bonus pay for amazing work on #OSS 00010000098 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000099 +705bonus pay for amazing work on #OSS 00010000099 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000100 +705bonus pay for amazing work on #OSS 00010000100 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000101 +705bonus pay for amazing work on #OSS 00010000101 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000102 +705bonus pay for amazing work on #OSS 00010000102 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000103 +705bonus pay for amazing work on #OSS 00010000103 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000104 +705bonus pay for amazing work on #OSS 00010000104 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000105 +705bonus pay for amazing work on #OSS 00010000105 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000106 +705bonus pay for amazing work on #OSS 00010000106 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000107 +705bonus pay for amazing work on #OSS 00010000107 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000108 +705bonus pay for amazing work on #OSS 00010000108 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000109 +705bonus pay for amazing work on #OSS 00010000109 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000110 +705bonus pay for amazing work on #OSS 00010000110 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000111 +705bonus pay for amazing work on #OSS 00010000111 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000112 +705bonus pay for amazing work on #OSS 00010000112 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000113 +705bonus pay for amazing work on #OSS 00010000113 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000114 +705bonus pay for amazing work on #OSS 00010000114 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000115 +705bonus pay for amazing work on #OSS 00010000115 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000116 +705bonus pay for amazing work on #OSS 00010000116 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000117 +705bonus pay for amazing work on #OSS 00010000117 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000118 +705bonus pay for amazing work on #OSS 00010000118 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000119 +705bonus pay for amazing work on #OSS 00010000119 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000120 +705bonus pay for amazing work on #OSS 00010000120 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000121 +705bonus pay for amazing work on #OSS 00010000121 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000122 +705bonus pay for amazing work on #OSS 00010000122 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000123 +705bonus pay for amazing work on #OSS 00010000123 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000124 +705bonus pay for amazing work on #OSS 00010000124 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000125 +705bonus pay for amazing work on #OSS 00010000125 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000126 +705bonus pay for amazing work on #OSS 00010000126 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000127 +705bonus pay for amazing work on #OSS 00010000127 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000128 +705bonus pay for amazing work on #OSS 00010000128 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000129 +705bonus pay for amazing work on #OSS 00010000129 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000130 +705bonus pay for amazing work on #OSS 00010000130 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000131 +705bonus pay for amazing work on #OSS 00010000131 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000132 +705bonus pay for amazing work on #OSS 00010000132 +62223138010481967038518 0000100000#GOxlEZaHxO64c#David Martinez 1121042880000133 +705bonus pay for amazing work on #OSS 00010000133 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000134 +705bonus pay for amazing work on #OSS 00010000134 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000135 +705bonus pay for amazing work on #OSS 00010000135 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000136 +705bonus pay for amazing work on #OSS 00010000136 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000137 +705bonus pay for amazing work on #OSS 00010000137 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000138 +705bonus pay for amazing work on #OSS 00010000138 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000139 +705bonus pay for amazing work on #OSS 00010000139 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000140 +705bonus pay for amazing work on #OSS 00010000140 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000141 +705bonus pay for amazing work on #OSS 00010000141 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000142 +705bonus pay for amazing work on #OSS 00010000142 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000143 +705bonus pay for amazing work on #OSS 00010000143 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000144 +705bonus pay for amazing work on #OSS 00010000144 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000145 +705bonus pay for amazing work on #OSS 00010000145 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000146 +705bonus pay for amazing work on #OSS 00010000146 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000147 +705bonus pay for amazing work on #OSS 00010000147 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000148 +705bonus pay for amazing work on #OSS 00010000148 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000149 +705bonus pay for amazing work on #OSS 00010000149 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000150 +705bonus pay for amazing work on #OSS 00010000150 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000151 +705bonus pay for amazing work on #OSS 00010000151 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000152 +705bonus pay for amazing work on #OSS 00010000152 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000153 +705bonus pay for amazing work on #OSS 00010000153 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000154 +705bonus pay for amazing work on #OSS 00010000154 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000155 +705bonus pay for amazing work on #OSS 00010000155 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000156 +705bonus pay for amazing work on #OSS 00010000156 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000157 +705bonus pay for amazing work on #OSS 00010000157 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000158 +705bonus pay for amazing work on #OSS 00010000158 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000159 +705bonus pay for amazing work on #OSS 00010000159 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000160 +705bonus pay for amazing work on #OSS 00010000160 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000161 +705bonus pay for amazing work on #OSS 00010000161 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000162 +705bonus pay for amazing work on #OSS 00010000162 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000163 +705bonus pay for amazing work on #OSS 00010000163 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000164 +705bonus pay for amazing work on #OSS 00010000164 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000165 +705bonus pay for amazing work on #OSS 00010000165 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000166 +705bonus pay for amazing work on #OSS 00010000166 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000167 +705bonus pay for amazing work on #OSS 00010000167 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000168 +705bonus pay for amazing work on #OSS 00010000168 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000169 +705bonus pay for amazing work on #OSS 00010000169 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000170 +705bonus pay for amazing work on #OSS 00010000170 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000171 +705bonus pay for amazing work on #OSS 00010000171 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000172 +705bonus pay for amazing work on #OSS 00010000172 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000173 +705bonus pay for amazing work on #OSS 00010000173 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000174 +705bonus pay for amazing work on #OSS 00010000174 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000175 +705bonus pay for amazing work on #OSS 00010000175 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000176 +705bonus pay for amazing work on #OSS 00010000176 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000177 +705bonus pay for amazing work on #OSS 00010000177 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000178 +705bonus pay for amazing work on #OSS 00010000178 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000179 +705bonus pay for amazing work on #OSS 00010000179 +62223138010481967038518 0000100000#7RCLaiwWKDjQi#Mia Wilson 1121042880000180 +705bonus pay for amazing work on #OSS 00010000180 +62223138010481967038518 0000100000#2Me863oQpY2ul#Mia Robinson 1121042880000181 +705bonus pay for amazing work on #OSS 00010000181 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000182 +705bonus pay for amazing work on #OSS 00010000182 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000183 +705bonus pay for amazing work on #OSS 00010000183 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000184 +705bonus pay for amazing work on #OSS 00010000184 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000185 +705bonus pay for amazing work on #OSS 00010000185 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000186 +705bonus pay for amazing work on #OSS 00010000186 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000187 +705bonus pay for amazing work on #OSS 00010000187 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000188 +705bonus pay for amazing work on #OSS 00010000188 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000189 +705bonus pay for amazing work on #OSS 00010000189 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000190 +705bonus pay for amazing work on #OSS 00010000190 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000191 +705bonus pay for amazing work on #OSS 00010000191 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000192 +705bonus pay for amazing work on #OSS 00010000192 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000193 +705bonus pay for amazing work on #OSS 00010000193 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000194 +705bonus pay for amazing work on #OSS 00010000194 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000195 +705bonus pay for amazing work on #OSS 00010000195 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000196 +705bonus pay for amazing work on #OSS 00010000196 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000197 +705bonus pay for amazing work on #OSS 00010000197 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000198 +705bonus pay for amazing work on #OSS 00010000198 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000199 +705bonus pay for amazing work on #OSS 00010000199 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000200 +705bonus pay for amazing work on #OSS 00010000200 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000201 +705bonus pay for amazing work on #OSS 00010000201 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000202 +705bonus pay for amazing work on #OSS 00010000202 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000203 +705bonus pay for amazing work on #OSS 00010000203 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000204 +705bonus pay for amazing work on #OSS 00010000204 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000205 +705bonus pay for amazing work on #OSS 00010000205 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000206 +705bonus pay for amazing work on #OSS 00010000206 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000207 +705bonus pay for amazing work on #OSS 00010000207 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000208 +705bonus pay for amazing work on #OSS 00010000208 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000209 +705bonus pay for amazing work on #OSS 00010000209 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000210 +705bonus pay for amazing work on #OSS 00010000210 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000211 +705bonus pay for amazing work on #OSS 00010000211 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000212 +705bonus pay for amazing work on #OSS 00010000212 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000213 +705bonus pay for amazing work on #OSS 00010000213 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000214 +705bonus pay for amazing work on #OSS 00010000214 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000215 +705bonus pay for amazing work on #OSS 00010000215 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000216 +705bonus pay for amazing work on #OSS 00010000216 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000217 +705bonus pay for amazing work on #OSS 00010000217 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000218 +705bonus pay for amazing work on #OSS 00010000218 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000219 +705bonus pay for amazing work on #OSS 00010000219 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000220 +705bonus pay for amazing work on #OSS 00010000220 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000221 +705bonus pay for amazing work on #OSS 00010000221 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000222 +705bonus pay for amazing work on #OSS 00010000222 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000223 +705bonus pay for amazing work on #OSS 00010000223 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000224 +705bonus pay for amazing work on #OSS 00010000224 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000225 +705bonus pay for amazing work on #OSS 00010000225 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000226 +705bonus pay for amazing work on #OSS 00010000226 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000227 +705bonus pay for amazing work on #OSS 00010000227 +62223138010481967038518 0000100000#2Me863oQpY2ul#Zoey Robinson 1121042880000228 +705bonus pay for amazing work on #OSS 00010000228 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000229 +705bonus pay for amazing work on #OSS 00010000229 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000230 +705bonus pay for amazing work on #OSS 00010000230 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000231 +705bonus pay for amazing work on #OSS 00010000231 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000232 +705bonus pay for amazing work on #OSS 00010000232 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000233 +705bonus pay for amazing work on #OSS 00010000233 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000234 +705bonus pay for amazing work on #OSS 00010000234 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000235 +705bonus pay for amazing work on #OSS 00010000235 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000236 +705bonus pay for amazing work on #OSS 00010000236 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000237 +705bonus pay for amazing work on #OSS 00010000237 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000238 +705bonus pay for amazing work on #OSS 00010000238 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000239 +705bonus pay for amazing work on #OSS 00010000239 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000240 +705bonus pay for amazing work on #OSS 00010000240 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000241 +705bonus pay for amazing work on #OSS 00010000241 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000242 +705bonus pay for amazing work on #OSS 00010000242 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000243 +705bonus pay for amazing work on #OSS 00010000243 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000244 +705bonus pay for amazing work on #OSS 00010000244 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000245 +705bonus pay for amazing work on #OSS 00010000245 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000246 +705bonus pay for amazing work on #OSS 00010000246 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000247 +705bonus pay for amazing work on #OSS 00010000247 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000248 +705bonus pay for amazing work on #OSS 00010000248 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000249 +705bonus pay for amazing work on #OSS 00010000249 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000250 +705bonus pay for amazing work on #OSS 00010000250 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000251 +705bonus pay for amazing work on #OSS 00010000251 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000252 +705bonus pay for amazing work on #OSS 00010000252 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000253 +705bonus pay for amazing work on #OSS 00010000253 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000254 +705bonus pay for amazing work on #OSS 00010000254 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000255 +705bonus pay for amazing work on #OSS 00010000255 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000256 +705bonus pay for amazing work on #OSS 00010000256 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000257 +705bonus pay for amazing work on #OSS 00010000257 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000258 +705bonus pay for amazing work on #OSS 00010000258 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000259 +705bonus pay for amazing work on #OSS 00010000259 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000260 +705bonus pay for amazing work on #OSS 00010000260 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000261 +705bonus pay for amazing work on #OSS 00010000261 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000262 +705bonus pay for amazing work on #OSS 00010000262 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000263 +705bonus pay for amazing work on #OSS 00010000263 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000264 +705bonus pay for amazing work on #OSS 00010000264 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000265 +705bonus pay for amazing work on #OSS 00010000265 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000266 +705bonus pay for amazing work on #OSS 00010000266 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000267 +705bonus pay for amazing work on #OSS 00010000267 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000268 +705bonus pay for amazing work on #OSS 00010000268 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000269 +705bonus pay for amazing work on #OSS 00010000269 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000270 +705bonus pay for amazing work on #OSS 00010000270 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000271 +705bonus pay for amazing work on #OSS 00010000271 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000272 +705bonus pay for amazing work on #OSS 00010000272 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000273 +705bonus pay for amazing work on #OSS 00010000273 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000274 +705bonus pay for amazing work on #OSS 00010000274 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000275 +705bonus pay for amazing work on #OSS 00010000275 +62223138010481967038518 0000100000#zMFKJLdQL271e#Emma Johnson 1121042880000276 +705bonus pay for amazing work on #OSS 00010000276 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000277 +705bonus pay for amazing work on #OSS 00010000277 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000278 +705bonus pay for amazing work on #OSS 00010000278 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000279 +705bonus pay for amazing work on #OSS 00010000279 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000280 +705bonus pay for amazing work on #OSS 00010000280 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000281 +705bonus pay for amazing work on #OSS 00010000281 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000282 +705bonus pay for amazing work on #OSS 00010000282 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000283 +705bonus pay for amazing work on #OSS 00010000283 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000284 +705bonus pay for amazing work on #OSS 00010000284 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000285 +705bonus pay for amazing work on #OSS 00010000285 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000286 +705bonus pay for amazing work on #OSS 00010000286 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000287 +705bonus pay for amazing work on #OSS 00010000287 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000288 +705bonus pay for amazing work on #OSS 00010000288 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000289 +705bonus pay for amazing work on #OSS 00010000289 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000290 +705bonus pay for amazing work on #OSS 00010000290 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000291 +705bonus pay for amazing work on #OSS 00010000291 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000292 +705bonus pay for amazing work on #OSS 00010000292 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000293 +705bonus pay for amazing work on #OSS 00010000293 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000294 +705bonus pay for amazing work on #OSS 00010000294 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000295 +705bonus pay for amazing work on #OSS 00010000295 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000296 +705bonus pay for amazing work on #OSS 00010000296 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000297 +705bonus pay for amazing work on #OSS 00010000297 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000298 +705bonus pay for amazing work on #OSS 00010000298 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000299 +705bonus pay for amazing work on #OSS 00010000299 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000300 +705bonus pay for amazing work on #OSS 00010000300 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000301 +705bonus pay for amazing work on #OSS 00010000301 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000302 +705bonus pay for amazing work on #OSS 00010000302 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000303 +705bonus pay for amazing work on #OSS 00010000303 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000304 +705bonus pay for amazing work on #OSS 00010000304 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000305 +705bonus pay for amazing work on #OSS 00010000305 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000306 +705bonus pay for amazing work on #OSS 00010000306 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000307 +705bonus pay for amazing work on #OSS 00010000307 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000308 +705bonus pay for amazing work on #OSS 00010000308 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000309 +705bonus pay for amazing work on #OSS 00010000309 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000310 +705bonus pay for amazing work on #OSS 00010000310 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000311 +705bonus pay for amazing work on #OSS 00010000311 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000312 +705bonus pay for amazing work on #OSS 00010000312 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000313 +705bonus pay for amazing work on #OSS 00010000313 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000314 +705bonus pay for amazing work on #OSS 00010000314 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000315 +705bonus pay for amazing work on #OSS 00010000315 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000316 +705bonus pay for amazing work on #OSS 00010000316 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000317 +705bonus pay for amazing work on #OSS 00010000317 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000318 +705bonus pay for amazing work on #OSS 00010000318 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000319 +705bonus pay for amazing work on #OSS 00010000319 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000320 +705bonus pay for amazing work on #OSS 00010000320 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000321 +705bonus pay for amazing work on #OSS 00010000321 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000322 +705bonus pay for amazing work on #OSS 00010000322 +62223138010481967038518 0000100000#Xtl8scHijS8Dd#Zoey Robinson 1121042880000323 +705bonus pay for amazing work on #OSS 00010000323 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Zoey Williams 1121042880000324 +705bonus pay for amazing work on #OSS 00010000324 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000325 +705bonus pay for amazing work on #OSS 00010000325 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000326 +705bonus pay for amazing work on #OSS 00010000326 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000327 +705bonus pay for amazing work on #OSS 00010000327 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000328 +705bonus pay for amazing work on #OSS 00010000328 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000329 +705bonus pay for amazing work on #OSS 00010000329 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000330 +705bonus pay for amazing work on #OSS 00010000330 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000331 +705bonus pay for amazing work on #OSS 00010000331 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000332 +705bonus pay for amazing work on #OSS 00010000332 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000333 +705bonus pay for amazing work on #OSS 00010000333 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000334 +705bonus pay for amazing work on #OSS 00010000334 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000335 +705bonus pay for amazing work on #OSS 00010000335 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000336 +705bonus pay for amazing work on #OSS 00010000336 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000337 +705bonus pay for amazing work on #OSS 00010000337 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000338 +705bonus pay for amazing work on #OSS 00010000338 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000339 +705bonus pay for amazing work on #OSS 00010000339 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000340 +705bonus pay for amazing work on #OSS 00010000340 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000341 +705bonus pay for amazing work on #OSS 00010000341 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000342 +705bonus pay for amazing work on #OSS 00010000342 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000343 +705bonus pay for amazing work on #OSS 00010000343 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000344 +705bonus pay for amazing work on #OSS 00010000344 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000345 +705bonus pay for amazing work on #OSS 00010000345 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000346 +705bonus pay for amazing work on #OSS 00010000346 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000347 +705bonus pay for amazing work on #OSS 00010000347 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000348 +705bonus pay for amazing work on #OSS 00010000348 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000349 +705bonus pay for amazing work on #OSS 00010000349 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000350 +705bonus pay for amazing work on #OSS 00010000350 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000351 +705bonus pay for amazing work on #OSS 00010000351 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000352 +705bonus pay for amazing work on #OSS 00010000352 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000353 +705bonus pay for amazing work on #OSS 00010000353 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000354 +705bonus pay for amazing work on #OSS 00010000354 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000355 +705bonus pay for amazing work on #OSS 00010000355 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000356 +705bonus pay for amazing work on #OSS 00010000356 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000357 +705bonus pay for amazing work on #OSS 00010000357 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000358 +705bonus pay for amazing work on #OSS 00010000358 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000359 +705bonus pay for amazing work on #OSS 00010000359 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000360 +705bonus pay for amazing work on #OSS 00010000360 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000361 +705bonus pay for amazing work on #OSS 00010000361 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000362 +705bonus pay for amazing work on #OSS 00010000362 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000363 +705bonus pay for amazing work on #OSS 00010000363 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000364 +705bonus pay for amazing work on #OSS 00010000364 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000365 +705bonus pay for amazing work on #OSS 00010000365 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000366 +705bonus pay for amazing work on #OSS 00010000366 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000367 +705bonus pay for amazing work on #OSS 00010000367 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000368 +705bonus pay for amazing work on #OSS 00010000368 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000369 +705bonus pay for amazing work on #OSS 00010000369 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000370 +705bonus pay for amazing work on #OSS 00010000370 +62223138010481967038518 0000100000#ncLT3DyhZtsIm#Ethan Williams 1121042880000371 +705bonus pay for amazing work on #OSS 00010000371 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000372 +705bonus pay for amazing work on #OSS 00010000372 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000373 +705bonus pay for amazing work on #OSS 00010000373 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000374 +705bonus pay for amazing work on #OSS 00010000374 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000375 +705bonus pay for amazing work on #OSS 00010000375 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000376 +705bonus pay for amazing work on #OSS 00010000376 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000377 +705bonus pay for amazing work on #OSS 00010000377 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000378 +705bonus pay for amazing work on #OSS 00010000378 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000379 +705bonus pay for amazing work on #OSS 00010000379 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000380 +705bonus pay for amazing work on #OSS 00010000380 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000381 +705bonus pay for amazing work on #OSS 00010000381 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000382 +705bonus pay for amazing work on #OSS 00010000382 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000383 +705bonus pay for amazing work on #OSS 00010000383 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000384 +705bonus pay for amazing work on #OSS 00010000384 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000385 +705bonus pay for amazing work on #OSS 00010000385 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000386 +705bonus pay for amazing work on #OSS 00010000386 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000387 +705bonus pay for amazing work on #OSS 00010000387 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000388 +705bonus pay for amazing work on #OSS 00010000388 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000389 +705bonus pay for amazing work on #OSS 00010000389 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000390 +705bonus pay for amazing work on #OSS 00010000390 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000391 +705bonus pay for amazing work on #OSS 00010000391 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000392 +705bonus pay for amazing work on #OSS 00010000392 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000393 +705bonus pay for amazing work on #OSS 00010000393 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000394 +705bonus pay for amazing work on #OSS 00010000394 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000395 +705bonus pay for amazing work on #OSS 00010000395 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000396 +705bonus pay for amazing work on #OSS 00010000396 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000397 +705bonus pay for amazing work on #OSS 00010000397 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000398 +705bonus pay for amazing work on #OSS 00010000398 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000399 +705bonus pay for amazing work on #OSS 00010000399 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000400 +705bonus pay for amazing work on #OSS 00010000400 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000401 +705bonus pay for amazing work on #OSS 00010000401 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000402 +705bonus pay for amazing work on #OSS 00010000402 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000403 +705bonus pay for amazing work on #OSS 00010000403 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000404 +705bonus pay for amazing work on #OSS 00010000404 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000405 +705bonus pay for amazing work on #OSS 00010000405 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000406 +705bonus pay for amazing work on #OSS 00010000406 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000407 +705bonus pay for amazing work on #OSS 00010000407 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000408 +705bonus pay for amazing work on #OSS 00010000408 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000409 +705bonus pay for amazing work on #OSS 00010000409 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000410 +705bonus pay for amazing work on #OSS 00010000410 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000411 +705bonus pay for amazing work on #OSS 00010000411 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000412 +705bonus pay for amazing work on #OSS 00010000412 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000413 +705bonus pay for amazing work on #OSS 00010000413 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000414 +705bonus pay for amazing work on #OSS 00010000414 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000415 +705bonus pay for amazing work on #OSS 00010000415 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000416 +705bonus pay for amazing work on #OSS 00010000416 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000417 +705bonus pay for amazing work on #OSS 00010000417 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000418 +705bonus pay for amazing work on #OSS 00010000418 +62223138010481967038518 0000100000#GX7NcgJ905vCC#Mia Wilson 1121042880000419 +705bonus pay for amazing work on #OSS 00010000419 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000420 +705bonus pay for amazing work on #OSS 00010000420 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000421 +705bonus pay for amazing work on #OSS 00010000421 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000422 +705bonus pay for amazing work on #OSS 00010000422 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000423 +705bonus pay for amazing work on #OSS 00010000423 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000424 +705bonus pay for amazing work on #OSS 00010000424 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000425 +705bonus pay for amazing work on #OSS 00010000425 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000426 +705bonus pay for amazing work on #OSS 00010000426 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000427 +705bonus pay for amazing work on #OSS 00010000427 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000428 +705bonus pay for amazing work on #OSS 00010000428 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000429 +705bonus pay for amazing work on #OSS 00010000429 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000430 +705bonus pay for amazing work on #OSS 00010000430 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000431 +705bonus pay for amazing work on #OSS 00010000431 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000432 +705bonus pay for amazing work on #OSS 00010000432 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000433 +705bonus pay for amazing work on #OSS 00010000433 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000434 +705bonus pay for amazing work on #OSS 00010000434 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000435 +705bonus pay for amazing work on #OSS 00010000435 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000436 +705bonus pay for amazing work on #OSS 00010000436 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000437 +705bonus pay for amazing work on #OSS 00010000437 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000438 +705bonus pay for amazing work on #OSS 00010000438 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000439 +705bonus pay for amazing work on #OSS 00010000439 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000440 +705bonus pay for amazing work on #OSS 00010000440 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000441 +705bonus pay for amazing work on #OSS 00010000441 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000442 +705bonus pay for amazing work on #OSS 00010000442 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000443 +705bonus pay for amazing work on #OSS 00010000443 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000444 +705bonus pay for amazing work on #OSS 00010000444 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000445 +705bonus pay for amazing work on #OSS 00010000445 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000446 +705bonus pay for amazing work on #OSS 00010000446 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000447 +705bonus pay for amazing work on #OSS 00010000447 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000448 +705bonus pay for amazing work on #OSS 00010000448 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000449 +705bonus pay for amazing work on #OSS 00010000449 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000450 +705bonus pay for amazing work on #OSS 00010000450 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000451 +705bonus pay for amazing work on #OSS 00010000451 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000452 +705bonus pay for amazing work on #OSS 00010000452 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000453 +705bonus pay for amazing work on #OSS 00010000453 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000454 +705bonus pay for amazing work on #OSS 00010000454 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000455 +705bonus pay for amazing work on #OSS 00010000455 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000456 +705bonus pay for amazing work on #OSS 00010000456 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000457 +705bonus pay for amazing work on #OSS 00010000457 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000458 +705bonus pay for amazing work on #OSS 00010000458 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000459 +705bonus pay for amazing work on #OSS 00010000459 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000460 +705bonus pay for amazing work on #OSS 00010000460 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000461 +705bonus pay for amazing work on #OSS 00010000461 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000462 +705bonus pay for amazing work on #OSS 00010000462 +62223138010481967038518 0000100000#JhJSepp0trPSL#Zoey Robinson 1121042880000463 +705bonus pay for amazing work on #OSS 00010000463 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Zoey Thompson 1121042880000464 +705bonus pay for amazing work on #OSS 00010000464 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000465 +705bonus pay for amazing work on #OSS 00010000465 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000466 +705bonus pay for amazing work on #OSS 00010000466 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000467 +705bonus pay for amazing work on #OSS 00010000467 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000468 +705bonus pay for amazing work on #OSS 00010000468 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000469 +705bonus pay for amazing work on #OSS 00010000469 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000470 +705bonus pay for amazing work on #OSS 00010000470 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000471 +705bonus pay for amazing work on #OSS 00010000471 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000472 +705bonus pay for amazing work on #OSS 00010000472 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000473 +705bonus pay for amazing work on #OSS 00010000473 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000474 +705bonus pay for amazing work on #OSS 00010000474 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000475 +705bonus pay for amazing work on #OSS 00010000475 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000476 +705bonus pay for amazing work on #OSS 00010000476 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000477 +705bonus pay for amazing work on #OSS 00010000477 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000478 +705bonus pay for amazing work on #OSS 00010000478 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000479 +705bonus pay for amazing work on #OSS 00010000479 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000480 +705bonus pay for amazing work on #OSS 00010000480 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000481 +705bonus pay for amazing work on #OSS 00010000481 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000482 +705bonus pay for amazing work on #OSS 00010000482 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000483 +705bonus pay for amazing work on #OSS 00010000483 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000484 +705bonus pay for amazing work on #OSS 00010000484 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000485 +705bonus pay for amazing work on #OSS 00010000485 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000486 +705bonus pay for amazing work on #OSS 00010000486 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000487 +705bonus pay for amazing work on #OSS 00010000487 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000488 +705bonus pay for amazing work on #OSS 00010000488 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000489 +705bonus pay for amazing work on #OSS 00010000489 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000490 +705bonus pay for amazing work on #OSS 00010000490 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000491 +705bonus pay for amazing work on #OSS 00010000491 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000492 +705bonus pay for amazing work on #OSS 00010000492 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000493 +705bonus pay for amazing work on #OSS 00010000493 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000494 +705bonus pay for amazing work on #OSS 00010000494 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000495 +705bonus pay for amazing work on #OSS 00010000495 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000496 +705bonus pay for amazing work on #OSS 00010000496 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000497 +705bonus pay for amazing work on #OSS 00010000497 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000498 +705bonus pay for amazing work on #OSS 00010000498 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000499 +705bonus pay for amazing work on #OSS 00010000499 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000500 +705bonus pay for amazing work on #OSS 00010000500 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000501 +705bonus pay for amazing work on #OSS 00010000501 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000502 +705bonus pay for amazing work on #OSS 00010000502 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000503 +705bonus pay for amazing work on #OSS 00010000503 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000504 +705bonus pay for amazing work on #OSS 00010000504 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000505 +705bonus pay for amazing work on #OSS 00010000505 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000506 +705bonus pay for amazing work on #OSS 00010000506 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000507 +705bonus pay for amazing work on #OSS 00010000507 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000508 +705bonus pay for amazing work on #OSS 00010000508 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000509 +705bonus pay for amazing work on #OSS 00010000509 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000510 +705bonus pay for amazing work on #OSS 00010000510 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000511 +705bonus pay for amazing work on #OSS 00010000511 +62223138010481967038518 0000100000#dibaxDXu8AkHK#Joshua Thompson 1121042880000512 +705bonus pay for amazing work on #OSS 00010000512 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Joshua Thomas 1121042880000513 +705bonus pay for amazing work on #OSS 00010000513 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000514 +705bonus pay for amazing work on #OSS 00010000514 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000515 +705bonus pay for amazing work on #OSS 00010000515 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000516 +705bonus pay for amazing work on #OSS 00010000516 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000517 +705bonus pay for amazing work on #OSS 00010000517 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000518 +705bonus pay for amazing work on #OSS 00010000518 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000519 +705bonus pay for amazing work on #OSS 00010000519 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000520 +705bonus pay for amazing work on #OSS 00010000520 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000521 +705bonus pay for amazing work on #OSS 00010000521 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000522 +705bonus pay for amazing work on #OSS 00010000522 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000523 +705bonus pay for amazing work on #OSS 00010000523 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000524 +705bonus pay for amazing work on #OSS 00010000524 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000525 +705bonus pay for amazing work on #OSS 00010000525 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000526 +705bonus pay for amazing work on #OSS 00010000526 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000527 +705bonus pay for amazing work on #OSS 00010000527 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000528 +705bonus pay for amazing work on #OSS 00010000528 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000529 +705bonus pay for amazing work on #OSS 00010000529 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000530 +705bonus pay for amazing work on #OSS 00010000530 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000531 +705bonus pay for amazing work on #OSS 00010000531 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000532 +705bonus pay for amazing work on #OSS 00010000532 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000533 +705bonus pay for amazing work on #OSS 00010000533 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000534 +705bonus pay for amazing work on #OSS 00010000534 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000535 +705bonus pay for amazing work on #OSS 00010000535 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000536 +705bonus pay for amazing work on #OSS 00010000536 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000537 +705bonus pay for amazing work on #OSS 00010000537 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000538 +705bonus pay for amazing work on #OSS 00010000538 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000539 +705bonus pay for amazing work on #OSS 00010000539 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000540 +705bonus pay for amazing work on #OSS 00010000540 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000541 +705bonus pay for amazing work on #OSS 00010000541 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000542 +705bonus pay for amazing work on #OSS 00010000542 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000543 +705bonus pay for amazing work on #OSS 00010000543 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000544 +705bonus pay for amazing work on #OSS 00010000544 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000545 +705bonus pay for amazing work on #OSS 00010000545 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000546 +705bonus pay for amazing work on #OSS 00010000546 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000547 +705bonus pay for amazing work on #OSS 00010000547 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000548 +705bonus pay for amazing work on #OSS 00010000548 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000549 +705bonus pay for amazing work on #OSS 00010000549 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000550 +705bonus pay for amazing work on #OSS 00010000550 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000551 +705bonus pay for amazing work on #OSS 00010000551 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000552 +705bonus pay for amazing work on #OSS 00010000552 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000553 +705bonus pay for amazing work on #OSS 00010000553 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000554 +705bonus pay for amazing work on #OSS 00010000554 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000555 +705bonus pay for amazing work on #OSS 00010000555 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000556 +705bonus pay for amazing work on #OSS 00010000556 +62223138010481967038518 0000100000#AeqfY3v9oYj84#Ella Thomas 1121042880000557 +705bonus pay for amazing work on #OSS 00010000557 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Ella Thompson 1121042880000558 +705bonus pay for amazing work on #OSS 00010000558 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000559 +705bonus pay for amazing work on #OSS 00010000559 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000560 +705bonus pay for amazing work on #OSS 00010000560 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000561 +705bonus pay for amazing work on #OSS 00010000561 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000562 +705bonus pay for amazing work on #OSS 00010000562 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000563 +705bonus pay for amazing work on #OSS 00010000563 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000564 +705bonus pay for amazing work on #OSS 00010000564 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000565 +705bonus pay for amazing work on #OSS 00010000565 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000566 +705bonus pay for amazing work on #OSS 00010000566 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000567 +705bonus pay for amazing work on #OSS 00010000567 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000568 +705bonus pay for amazing work on #OSS 00010000568 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000569 +705bonus pay for amazing work on #OSS 00010000569 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000570 +705bonus pay for amazing work on #OSS 00010000570 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000571 +705bonus pay for amazing work on #OSS 00010000571 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000572 +705bonus pay for amazing work on #OSS 00010000572 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000573 +705bonus pay for amazing work on #OSS 00010000573 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000574 +705bonus pay for amazing work on #OSS 00010000574 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000575 +705bonus pay for amazing work on #OSS 00010000575 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000576 +705bonus pay for amazing work on #OSS 00010000576 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000577 +705bonus pay for amazing work on #OSS 00010000577 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000578 +705bonus pay for amazing work on #OSS 00010000578 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000579 +705bonus pay for amazing work on #OSS 00010000579 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000580 +705bonus pay for amazing work on #OSS 00010000580 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000581 +705bonus pay for amazing work on #OSS 00010000581 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000582 +705bonus pay for amazing work on #OSS 00010000582 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000583 +705bonus pay for amazing work on #OSS 00010000583 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000584 +705bonus pay for amazing work on #OSS 00010000584 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000585 +705bonus pay for amazing work on #OSS 00010000585 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000586 +705bonus pay for amazing work on #OSS 00010000586 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000587 +705bonus pay for amazing work on #OSS 00010000587 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000588 +705bonus pay for amazing work on #OSS 00010000588 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000589 +705bonus pay for amazing work on #OSS 00010000589 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000590 +705bonus pay for amazing work on #OSS 00010000590 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000591 +705bonus pay for amazing work on #OSS 00010000591 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000592 +705bonus pay for amazing work on #OSS 00010000592 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000593 +705bonus pay for amazing work on #OSS 00010000593 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000594 +705bonus pay for amazing work on #OSS 00010000594 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000595 +705bonus pay for amazing work on #OSS 00010000595 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000596 +705bonus pay for amazing work on #OSS 00010000596 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000597 +705bonus pay for amazing work on #OSS 00010000597 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000598 +705bonus pay for amazing work on #OSS 00010000598 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000599 +705bonus pay for amazing work on #OSS 00010000599 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000600 +705bonus pay for amazing work on #OSS 00010000600 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000601 +705bonus pay for amazing work on #OSS 00010000601 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000602 +705bonus pay for amazing work on #OSS 00010000602 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000603 +705bonus pay for amazing work on #OSS 00010000603 +62223138010481967038518 0000100000#sMsMFiAD3y8R8#Joshua Thompson 1121042880000604 +705bonus pay for amazing work on #OSS 00010000604 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Joshua Smith 1121042880000605 +705bonus pay for amazing work on #OSS 00010000605 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000606 +705bonus pay for amazing work on #OSS 00010000606 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000607 +705bonus pay for amazing work on #OSS 00010000607 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000608 +705bonus pay for amazing work on #OSS 00010000608 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000609 +705bonus pay for amazing work on #OSS 00010000609 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000610 +705bonus pay for amazing work on #OSS 00010000610 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000611 +705bonus pay for amazing work on #OSS 00010000611 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000612 +705bonus pay for amazing work on #OSS 00010000612 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000613 +705bonus pay for amazing work on #OSS 00010000613 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000614 +705bonus pay for amazing work on #OSS 00010000614 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000615 +705bonus pay for amazing work on #OSS 00010000615 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000616 +705bonus pay for amazing work on #OSS 00010000616 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000617 +705bonus pay for amazing work on #OSS 00010000617 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000618 +705bonus pay for amazing work on #OSS 00010000618 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000619 +705bonus pay for amazing work on #OSS 00010000619 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000620 +705bonus pay for amazing work on #OSS 00010000620 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000621 +705bonus pay for amazing work on #OSS 00010000621 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000622 +705bonus pay for amazing work on #OSS 00010000622 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000623 +705bonus pay for amazing work on #OSS 00010000623 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000624 +705bonus pay for amazing work on #OSS 00010000624 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000625 +705bonus pay for amazing work on #OSS 00010000625 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000626 +705bonus pay for amazing work on #OSS 00010000626 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000627 +705bonus pay for amazing work on #OSS 00010000627 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000628 +705bonus pay for amazing work on #OSS 00010000628 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000629 +705bonus pay for amazing work on #OSS 00010000629 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000630 +705bonus pay for amazing work on #OSS 00010000630 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000631 +705bonus pay for amazing work on #OSS 00010000631 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000632 +705bonus pay for amazing work on #OSS 00010000632 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000633 +705bonus pay for amazing work on #OSS 00010000633 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000634 +705bonus pay for amazing work on #OSS 00010000634 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000635 +705bonus pay for amazing work on #OSS 00010000635 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000636 +705bonus pay for amazing work on #OSS 00010000636 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000637 +705bonus pay for amazing work on #OSS 00010000637 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000638 +705bonus pay for amazing work on #OSS 00010000638 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000639 +705bonus pay for amazing work on #OSS 00010000639 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000640 +705bonus pay for amazing work on #OSS 00010000640 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000641 +705bonus pay for amazing work on #OSS 00010000641 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000642 +705bonus pay for amazing work on #OSS 00010000642 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000643 +705bonus pay for amazing work on #OSS 00010000643 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000644 +705bonus pay for amazing work on #OSS 00010000644 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000645 +705bonus pay for amazing work on #OSS 00010000645 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000646 +705bonus pay for amazing work on #OSS 00010000646 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000647 +705bonus pay for amazing work on #OSS 00010000647 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000648 +705bonus pay for amazing work on #OSS 00010000648 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000649 +705bonus pay for amazing work on #OSS 00010000649 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000650 +705bonus pay for amazing work on #OSS 00010000650 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000651 +705bonus pay for amazing work on #OSS 00010000651 +62223138010481967038518 0000100000#MNpzIT6VjEXXo#Jacob Smith 1121042880000652 +705bonus pay for amazing work on #OSS 00010000652 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Joseph Robinson 1121042880000653 +705bonus pay for amazing work on #OSS 00010000653 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000654 +705bonus pay for amazing work on #OSS 00010000654 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000655 +705bonus pay for amazing work on #OSS 00010000655 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000656 +705bonus pay for amazing work on #OSS 00010000656 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000657 +705bonus pay for amazing work on #OSS 00010000657 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000658 +705bonus pay for amazing work on #OSS 00010000658 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000659 +705bonus pay for amazing work on #OSS 00010000659 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000660 +705bonus pay for amazing work on #OSS 00010000660 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000661 +705bonus pay for amazing work on #OSS 00010000661 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000662 +705bonus pay for amazing work on #OSS 00010000662 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000663 +705bonus pay for amazing work on #OSS 00010000663 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000664 +705bonus pay for amazing work on #OSS 00010000664 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000665 +705bonus pay for amazing work on #OSS 00010000665 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000666 +705bonus pay for amazing work on #OSS 00010000666 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000667 +705bonus pay for amazing work on #OSS 00010000667 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000668 +705bonus pay for amazing work on #OSS 00010000668 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000669 +705bonus pay for amazing work on #OSS 00010000669 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000670 +705bonus pay for amazing work on #OSS 00010000670 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000671 +705bonus pay for amazing work on #OSS 00010000671 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000672 +705bonus pay for amazing work on #OSS 00010000672 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000673 +705bonus pay for amazing work on #OSS 00010000673 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000674 +705bonus pay for amazing work on #OSS 00010000674 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000675 +705bonus pay for amazing work on #OSS 00010000675 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000676 +705bonus pay for amazing work on #OSS 00010000676 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000677 +705bonus pay for amazing work on #OSS 00010000677 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000678 +705bonus pay for amazing work on #OSS 00010000678 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000679 +705bonus pay for amazing work on #OSS 00010000679 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000680 +705bonus pay for amazing work on #OSS 00010000680 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000681 +705bonus pay for amazing work on #OSS 00010000681 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000682 +705bonus pay for amazing work on #OSS 00010000682 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000683 +705bonus pay for amazing work on #OSS 00010000683 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000684 +705bonus pay for amazing work on #OSS 00010000684 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000685 +705bonus pay for amazing work on #OSS 00010000685 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000686 +705bonus pay for amazing work on #OSS 00010000686 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000687 +705bonus pay for amazing work on #OSS 00010000687 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000688 +705bonus pay for amazing work on #OSS 00010000688 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000689 +705bonus pay for amazing work on #OSS 00010000689 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000690 +705bonus pay for amazing work on #OSS 00010000690 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000691 +705bonus pay for amazing work on #OSS 00010000691 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000692 +705bonus pay for amazing work on #OSS 00010000692 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000693 +705bonus pay for amazing work on #OSS 00010000693 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000694 +705bonus pay for amazing work on #OSS 00010000694 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000695 +705bonus pay for amazing work on #OSS 00010000695 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000696 +705bonus pay for amazing work on #OSS 00010000696 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000697 +705bonus pay for amazing work on #OSS 00010000697 +62223138010481967038518 0000100000#rtfeGh2Meqpzb#Zoey Robinson 1121042880000698 +705bonus pay for amazing work on #OSS 00010000698 +62223138010481967038518 0000100000#M6oY0AzJgimFq#Zoey Brown 1121042880000699 +705bonus pay for amazing work on #OSS 00010000699 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000700 +705bonus pay for amazing work on #OSS 00010000700 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000701 +705bonus pay for amazing work on #OSS 00010000701 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000702 +705bonus pay for amazing work on #OSS 00010000702 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000703 +705bonus pay for amazing work on #OSS 00010000703 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000704 +705bonus pay for amazing work on #OSS 00010000704 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000705 +705bonus pay for amazing work on #OSS 00010000705 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000706 +705bonus pay for amazing work on #OSS 00010000706 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000707 +705bonus pay for amazing work on #OSS 00010000707 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000708 +705bonus pay for amazing work on #OSS 00010000708 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000709 +705bonus pay for amazing work on #OSS 00010000709 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000710 +705bonus pay for amazing work on #OSS 00010000710 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000711 +705bonus pay for amazing work on #OSS 00010000711 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000712 +705bonus pay for amazing work on #OSS 00010000712 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000713 +705bonus pay for amazing work on #OSS 00010000713 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000714 +705bonus pay for amazing work on #OSS 00010000714 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000715 +705bonus pay for amazing work on #OSS 00010000715 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000716 +705bonus pay for amazing work on #OSS 00010000716 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000717 +705bonus pay for amazing work on #OSS 00010000717 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000718 +705bonus pay for amazing work on #OSS 00010000718 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000719 +705bonus pay for amazing work on #OSS 00010000719 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000720 +705bonus pay for amazing work on #OSS 00010000720 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000721 +705bonus pay for amazing work on #OSS 00010000721 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000722 +705bonus pay for amazing work on #OSS 00010000722 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000723 +705bonus pay for amazing work on #OSS 00010000723 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000724 +705bonus pay for amazing work on #OSS 00010000724 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000725 +705bonus pay for amazing work on #OSS 00010000725 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000726 +705bonus pay for amazing work on #OSS 00010000726 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000727 +705bonus pay for amazing work on #OSS 00010000727 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000728 +705bonus pay for amazing work on #OSS 00010000728 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000729 +705bonus pay for amazing work on #OSS 00010000729 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000730 +705bonus pay for amazing work on #OSS 00010000730 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000731 +705bonus pay for amazing work on #OSS 00010000731 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000732 +705bonus pay for amazing work on #OSS 00010000732 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000733 +705bonus pay for amazing work on #OSS 00010000733 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000734 +705bonus pay for amazing work on #OSS 00010000734 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000735 +705bonus pay for amazing work on #OSS 00010000735 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000736 +705bonus pay for amazing work on #OSS 00010000736 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000737 +705bonus pay for amazing work on #OSS 00010000737 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000738 +705bonus pay for amazing work on #OSS 00010000738 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000739 +705bonus pay for amazing work on #OSS 00010000739 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000740 +705bonus pay for amazing work on #OSS 00010000740 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000741 +705bonus pay for amazing work on #OSS 00010000741 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000742 +705bonus pay for amazing work on #OSS 00010000742 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000743 +705bonus pay for amazing work on #OSS 00010000743 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000744 +705bonus pay for amazing work on #OSS 00010000744 +62223138010481967038518 0000100000#M6oY0AzJgimFq#William Brown 1121042880000745 +705bonus pay for amazing work on #OSS 00010000745 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#William Martinez 1121042880000746 +705bonus pay for amazing work on #OSS 00010000746 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000747 +705bonus pay for amazing work on #OSS 00010000747 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000748 +705bonus pay for amazing work on #OSS 00010000748 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000749 +705bonus pay for amazing work on #OSS 00010000749 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000750 +705bonus pay for amazing work on #OSS 00010000750 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000751 +705bonus pay for amazing work on #OSS 00010000751 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000752 +705bonus pay for amazing work on #OSS 00010000752 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000753 +705bonus pay for amazing work on #OSS 00010000753 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000754 +705bonus pay for amazing work on #OSS 00010000754 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000755 +705bonus pay for amazing work on #OSS 00010000755 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000756 +705bonus pay for amazing work on #OSS 00010000756 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000757 +705bonus pay for amazing work on #OSS 00010000757 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000758 +705bonus pay for amazing work on #OSS 00010000758 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000759 +705bonus pay for amazing work on #OSS 00010000759 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000760 +705bonus pay for amazing work on #OSS 00010000760 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000761 +705bonus pay for amazing work on #OSS 00010000761 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000762 +705bonus pay for amazing work on #OSS 00010000762 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000763 +705bonus pay for amazing work on #OSS 00010000763 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000764 +705bonus pay for amazing work on #OSS 00010000764 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000765 +705bonus pay for amazing work on #OSS 00010000765 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000766 +705bonus pay for amazing work on #OSS 00010000766 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000767 +705bonus pay for amazing work on #OSS 00010000767 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000768 +705bonus pay for amazing work on #OSS 00010000768 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000769 +705bonus pay for amazing work on #OSS 00010000769 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000770 +705bonus pay for amazing work on #OSS 00010000770 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000771 +705bonus pay for amazing work on #OSS 00010000771 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000772 +705bonus pay for amazing work on #OSS 00010000772 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000773 +705bonus pay for amazing work on #OSS 00010000773 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000774 +705bonus pay for amazing work on #OSS 00010000774 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000775 +705bonus pay for amazing work on #OSS 00010000775 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000776 +705bonus pay for amazing work on #OSS 00010000776 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000777 +705bonus pay for amazing work on #OSS 00010000777 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000778 +705bonus pay for amazing work on #OSS 00010000778 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000779 +705bonus pay for amazing work on #OSS 00010000779 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000780 +705bonus pay for amazing work on #OSS 00010000780 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000781 +705bonus pay for amazing work on #OSS 00010000781 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000782 +705bonus pay for amazing work on #OSS 00010000782 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000783 +705bonus pay for amazing work on #OSS 00010000783 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000784 +705bonus pay for amazing work on #OSS 00010000784 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000785 +705bonus pay for amazing work on #OSS 00010000785 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000786 +705bonus pay for amazing work on #OSS 00010000786 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000787 +705bonus pay for amazing work on #OSS 00010000787 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000788 +705bonus pay for amazing work on #OSS 00010000788 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000789 +705bonus pay for amazing work on #OSS 00010000789 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000790 +705bonus pay for amazing work on #OSS 00010000790 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000791 +705bonus pay for amazing work on #OSS 00010000791 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000792 +705bonus pay for amazing work on #OSS 00010000792 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000793 +705bonus pay for amazing work on #OSS 00010000793 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000794 +705bonus pay for amazing work on #OSS 00010000794 +62223138010481967038518 0000100000#MIOzQcrU8ZrBN#David Martinez 1121042880000795 +705bonus pay for amazing work on #OSS 00010000795 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000796 +705bonus pay for amazing work on #OSS 00010000796 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000797 +705bonus pay for amazing work on #OSS 00010000797 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000798 +705bonus pay for amazing work on #OSS 00010000798 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000799 +705bonus pay for amazing work on #OSS 00010000799 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000800 +705bonus pay for amazing work on #OSS 00010000800 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000801 +705bonus pay for amazing work on #OSS 00010000801 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000802 +705bonus pay for amazing work on #OSS 00010000802 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000803 +705bonus pay for amazing work on #OSS 00010000803 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000804 +705bonus pay for amazing work on #OSS 00010000804 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000805 +705bonus pay for amazing work on #OSS 00010000805 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000806 +705bonus pay for amazing work on #OSS 00010000806 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000807 +705bonus pay for amazing work on #OSS 00010000807 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000808 +705bonus pay for amazing work on #OSS 00010000808 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000809 +705bonus pay for amazing work on #OSS 00010000809 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000810 +705bonus pay for amazing work on #OSS 00010000810 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000811 +705bonus pay for amazing work on #OSS 00010000811 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000812 +705bonus pay for amazing work on #OSS 00010000812 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000813 +705bonus pay for amazing work on #OSS 00010000813 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000814 +705bonus pay for amazing work on #OSS 00010000814 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000815 +705bonus pay for amazing work on #OSS 00010000815 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000816 +705bonus pay for amazing work on #OSS 00010000816 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000817 +705bonus pay for amazing work on #OSS 00010000817 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000818 +705bonus pay for amazing work on #OSS 00010000818 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000819 +705bonus pay for amazing work on #OSS 00010000819 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000820 +705bonus pay for amazing work on #OSS 00010000820 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000821 +705bonus pay for amazing work on #OSS 00010000821 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000822 +705bonus pay for amazing work on #OSS 00010000822 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000823 +705bonus pay for amazing work on #OSS 00010000823 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000824 +705bonus pay for amazing work on #OSS 00010000824 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000825 +705bonus pay for amazing work on #OSS 00010000825 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000826 +705bonus pay for amazing work on #OSS 00010000826 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000827 +705bonus pay for amazing work on #OSS 00010000827 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000828 +705bonus pay for amazing work on #OSS 00010000828 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000829 +705bonus pay for amazing work on #OSS 00010000829 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000830 +705bonus pay for amazing work on #OSS 00010000830 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000831 +705bonus pay for amazing work on #OSS 00010000831 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000832 +705bonus pay for amazing work on #OSS 00010000832 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000833 +705bonus pay for amazing work on #OSS 00010000833 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000834 +705bonus pay for amazing work on #OSS 00010000834 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000835 +705bonus pay for amazing work on #OSS 00010000835 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000836 +705bonus pay for amazing work on #OSS 00010000836 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000837 +705bonus pay for amazing work on #OSS 00010000837 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000838 +705bonus pay for amazing work on #OSS 00010000838 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000839 +705bonus pay for amazing work on #OSS 00010000839 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000840 +705bonus pay for amazing work on #OSS 00010000840 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000841 +705bonus pay for amazing work on #OSS 00010000841 +62223138010481967038518 0000100000#I4huaz9y2qdn9#Sofia Garcia 1121042880000842 +705bonus pay for amazing work on #OSS 00010000842 +62223138010481967038518 0000100000#mNX82cp57avlG#Natalie Thompson 1121042880000843 +705bonus pay for amazing work on #OSS 00010000843 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000844 +705bonus pay for amazing work on #OSS 00010000844 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000845 +705bonus pay for amazing work on #OSS 00010000845 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000846 +705bonus pay for amazing work on #OSS 00010000846 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000847 +705bonus pay for amazing work on #OSS 00010000847 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000848 +705bonus pay for amazing work on #OSS 00010000848 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000849 +705bonus pay for amazing work on #OSS 00010000849 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000850 +705bonus pay for amazing work on #OSS 00010000850 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000851 +705bonus pay for amazing work on #OSS 00010000851 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000852 +705bonus pay for amazing work on #OSS 00010000852 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000853 +705bonus pay for amazing work on #OSS 00010000853 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000854 +705bonus pay for amazing work on #OSS 00010000854 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000855 +705bonus pay for amazing work on #OSS 00010000855 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000856 +705bonus pay for amazing work on #OSS 00010000856 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000857 +705bonus pay for amazing work on #OSS 00010000857 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000858 +705bonus pay for amazing work on #OSS 00010000858 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000859 +705bonus pay for amazing work on #OSS 00010000859 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000860 +705bonus pay for amazing work on #OSS 00010000860 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000861 +705bonus pay for amazing work on #OSS 00010000861 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000862 +705bonus pay for amazing work on #OSS 00010000862 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000863 +705bonus pay for amazing work on #OSS 00010000863 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000864 +705bonus pay for amazing work on #OSS 00010000864 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000865 +705bonus pay for amazing work on #OSS 00010000865 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000866 +705bonus pay for amazing work on #OSS 00010000866 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000867 +705bonus pay for amazing work on #OSS 00010000867 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000868 +705bonus pay for amazing work on #OSS 00010000868 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000869 +705bonus pay for amazing work on #OSS 00010000869 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000870 +705bonus pay for amazing work on #OSS 00010000870 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000871 +705bonus pay for amazing work on #OSS 00010000871 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000872 +705bonus pay for amazing work on #OSS 00010000872 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000873 +705bonus pay for amazing work on #OSS 00010000873 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000874 +705bonus pay for amazing work on #OSS 00010000874 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000875 +705bonus pay for amazing work on #OSS 00010000875 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000876 +705bonus pay for amazing work on #OSS 00010000876 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000877 +705bonus pay for amazing work on #OSS 00010000877 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000878 +705bonus pay for amazing work on #OSS 00010000878 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000879 +705bonus pay for amazing work on #OSS 00010000879 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000880 +705bonus pay for amazing work on #OSS 00010000880 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000881 +705bonus pay for amazing work on #OSS 00010000881 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000882 +705bonus pay for amazing work on #OSS 00010000882 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000883 +705bonus pay for amazing work on #OSS 00010000883 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000884 +705bonus pay for amazing work on #OSS 00010000884 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000885 +705bonus pay for amazing work on #OSS 00010000885 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000886 +705bonus pay for amazing work on #OSS 00010000886 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000887 +705bonus pay for amazing work on #OSS 00010000887 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000888 +705bonus pay for amazing work on #OSS 00010000888 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000889 +705bonus pay for amazing work on #OSS 00010000889 +62223138010481967038518 0000100000#mNX82cp57avlG#Joshua Thompson 1121042880000890 +705bonus pay for amazing work on #OSS 00010000890 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000891 +705bonus pay for amazing work on #OSS 00010000891 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000892 +705bonus pay for amazing work on #OSS 00010000892 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000893 +705bonus pay for amazing work on #OSS 00010000893 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000894 +705bonus pay for amazing work on #OSS 00010000894 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000895 +705bonus pay for amazing work on #OSS 00010000895 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000896 +705bonus pay for amazing work on #OSS 00010000896 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000897 +705bonus pay for amazing work on #OSS 00010000897 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000898 +705bonus pay for amazing work on #OSS 00010000898 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000899 +705bonus pay for amazing work on #OSS 00010000899 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000900 +705bonus pay for amazing work on #OSS 00010000900 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000901 +705bonus pay for amazing work on #OSS 00010000901 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000902 +705bonus pay for amazing work on #OSS 00010000902 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000903 +705bonus pay for amazing work on #OSS 00010000903 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000904 +705bonus pay for amazing work on #OSS 00010000904 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000905 +705bonus pay for amazing work on #OSS 00010000905 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000906 +705bonus pay for amazing work on #OSS 00010000906 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000907 +705bonus pay for amazing work on #OSS 00010000907 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000908 +705bonus pay for amazing work on #OSS 00010000908 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000909 +705bonus pay for amazing work on #OSS 00010000909 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000910 +705bonus pay for amazing work on #OSS 00010000910 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000911 +705bonus pay for amazing work on #OSS 00010000911 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000912 +705bonus pay for amazing work on #OSS 00010000912 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000913 +705bonus pay for amazing work on #OSS 00010000913 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000914 +705bonus pay for amazing work on #OSS 00010000914 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000915 +705bonus pay for amazing work on #OSS 00010000915 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000916 +705bonus pay for amazing work on #OSS 00010000916 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000917 +705bonus pay for amazing work on #OSS 00010000917 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000918 +705bonus pay for amazing work on #OSS 00010000918 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000919 +705bonus pay for amazing work on #OSS 00010000919 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000920 +705bonus pay for amazing work on #OSS 00010000920 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000921 +705bonus pay for amazing work on #OSS 00010000921 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000922 +705bonus pay for amazing work on #OSS 00010000922 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000923 +705bonus pay for amazing work on #OSS 00010000923 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000924 +705bonus pay for amazing work on #OSS 00010000924 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000925 +705bonus pay for amazing work on #OSS 00010000925 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000926 +705bonus pay for amazing work on #OSS 00010000926 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000927 +705bonus pay for amazing work on #OSS 00010000927 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000928 +705bonus pay for amazing work on #OSS 00010000928 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000929 +705bonus pay for amazing work on #OSS 00010000929 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000930 +705bonus pay for amazing work on #OSS 00010000930 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000931 +705bonus pay for amazing work on #OSS 00010000931 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000932 +705bonus pay for amazing work on #OSS 00010000932 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000933 +705bonus pay for amazing work on #OSS 00010000933 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000934 +705bonus pay for amazing work on #OSS 00010000934 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000935 +705bonus pay for amazing work on #OSS 00010000935 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000936 +705bonus pay for amazing work on #OSS 00010000936 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000937 +705bonus pay for amazing work on #OSS 00010000937 +62223138010481967038518 0000100000#FIqV7bx0XyTQs#Elijah Jackson 1121042880000938 +705bonus pay for amazing work on #OSS 00010000938 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000939 +705bonus pay for amazing work on #OSS 00010000939 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000940 +705bonus pay for amazing work on #OSS 00010000940 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000941 +705bonus pay for amazing work on #OSS 00010000941 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000942 +705bonus pay for amazing work on #OSS 00010000942 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000943 +705bonus pay for amazing work on #OSS 00010000943 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000944 +705bonus pay for amazing work on #OSS 00010000944 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000945 +705bonus pay for amazing work on #OSS 00010000945 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000946 +705bonus pay for amazing work on #OSS 00010000946 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000947 +705bonus pay for amazing work on #OSS 00010000947 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000948 +705bonus pay for amazing work on #OSS 00010000948 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000949 +705bonus pay for amazing work on #OSS 00010000949 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000950 +705bonus pay for amazing work on #OSS 00010000950 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000951 +705bonus pay for amazing work on #OSS 00010000951 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000952 +705bonus pay for amazing work on #OSS 00010000952 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000953 +705bonus pay for amazing work on #OSS 00010000953 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000954 +705bonus pay for amazing work on #OSS 00010000954 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000955 +705bonus pay for amazing work on #OSS 00010000955 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000956 +705bonus pay for amazing work on #OSS 00010000956 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000957 +705bonus pay for amazing work on #OSS 00010000957 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000958 +705bonus pay for amazing work on #OSS 00010000958 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000959 +705bonus pay for amazing work on #OSS 00010000959 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000960 +705bonus pay for amazing work on #OSS 00010000960 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000961 +705bonus pay for amazing work on #OSS 00010000961 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000962 +705bonus pay for amazing work on #OSS 00010000962 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000963 +705bonus pay for amazing work on #OSS 00010000963 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000964 +705bonus pay for amazing work on #OSS 00010000964 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000965 +705bonus pay for amazing work on #OSS 00010000965 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000966 +705bonus pay for amazing work on #OSS 00010000966 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000967 +705bonus pay for amazing work on #OSS 00010000967 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000968 +705bonus pay for amazing work on #OSS 00010000968 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000969 +705bonus pay for amazing work on #OSS 00010000969 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000970 +705bonus pay for amazing work on #OSS 00010000970 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000971 +705bonus pay for amazing work on #OSS 00010000971 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000972 +705bonus pay for amazing work on #OSS 00010000972 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000973 +705bonus pay for amazing work on #OSS 00010000973 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000974 +705bonus pay for amazing work on #OSS 00010000974 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000975 +705bonus pay for amazing work on #OSS 00010000975 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000976 +705bonus pay for amazing work on #OSS 00010000976 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000977 +705bonus pay for amazing work on #OSS 00010000977 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000978 +705bonus pay for amazing work on #OSS 00010000978 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000979 +705bonus pay for amazing work on #OSS 00010000979 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000980 +705bonus pay for amazing work on #OSS 00010000980 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000981 +705bonus pay for amazing work on #OSS 00010000981 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000982 +705bonus pay for amazing work on #OSS 00010000982 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000983 +705bonus pay for amazing work on #OSS 00010000983 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000984 +705bonus pay for amazing work on #OSS 00010000984 +62223138010481967038518 0000100000#SAqaC8dCnXqUW#Jacob Smith 1121042880000985 +705bonus pay for amazing work on #OSS 00010000985 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Jacob Robinson 1121042880000986 +705bonus pay for amazing work on #OSS 00010000986 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000987 +705bonus pay for amazing work on #OSS 00010000987 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000988 +705bonus pay for amazing work on #OSS 00010000988 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000989 +705bonus pay for amazing work on #OSS 00010000989 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000990 +705bonus pay for amazing work on #OSS 00010000990 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000991 +705bonus pay for amazing work on #OSS 00010000991 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000992 +705bonus pay for amazing work on #OSS 00010000992 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000993 +705bonus pay for amazing work on #OSS 00010000993 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000994 +705bonus pay for amazing work on #OSS 00010000994 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000995 +705bonus pay for amazing work on #OSS 00010000995 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000996 +705bonus pay for amazing work on #OSS 00010000996 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000997 +705bonus pay for amazing work on #OSS 00010000997 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000998 +705bonus pay for amazing work on #OSS 00010000998 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880000999 +705bonus pay for amazing work on #OSS 00010000999 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001000 +705bonus pay for amazing work on #OSS 00010001000 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001001 +705bonus pay for amazing work on #OSS 00010001001 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001002 +705bonus pay for amazing work on #OSS 00010001002 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001003 +705bonus pay for amazing work on #OSS 00010001003 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001004 +705bonus pay for amazing work on #OSS 00010001004 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001005 +705bonus pay for amazing work on #OSS 00010001005 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001006 +705bonus pay for amazing work on #OSS 00010001006 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001007 +705bonus pay for amazing work on #OSS 00010001007 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001008 +705bonus pay for amazing work on #OSS 00010001008 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001009 +705bonus pay for amazing work on #OSS 00010001009 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001010 +705bonus pay for amazing work on #OSS 00010001010 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001011 +705bonus pay for amazing work on #OSS 00010001011 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001012 +705bonus pay for amazing work on #OSS 00010001012 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001013 +705bonus pay for amazing work on #OSS 00010001013 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001014 +705bonus pay for amazing work on #OSS 00010001014 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001015 +705bonus pay for amazing work on #OSS 00010001015 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001016 +705bonus pay for amazing work on #OSS 00010001016 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001017 +705bonus pay for amazing work on #OSS 00010001017 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001018 +705bonus pay for amazing work on #OSS 00010001018 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001019 +705bonus pay for amazing work on #OSS 00010001019 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001020 +705bonus pay for amazing work on #OSS 00010001020 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001021 +705bonus pay for amazing work on #OSS 00010001021 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001022 +705bonus pay for amazing work on #OSS 00010001022 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001023 +705bonus pay for amazing work on #OSS 00010001023 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001024 +705bonus pay for amazing work on #OSS 00010001024 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001025 +705bonus pay for amazing work on #OSS 00010001025 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001026 +705bonus pay for amazing work on #OSS 00010001026 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001027 +705bonus pay for amazing work on #OSS 00010001027 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001028 +705bonus pay for amazing work on #OSS 00010001028 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001029 +705bonus pay for amazing work on #OSS 00010001029 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001030 +705bonus pay for amazing work on #OSS 00010001030 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001031 +705bonus pay for amazing work on #OSS 00010001031 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001032 +705bonus pay for amazing work on #OSS 00010001032 +62223138010481967038518 0000100000#lQnpnGFqAPDUD#Zoey Robinson 1121042880001033 +705bonus pay for amazing work on #OSS 00010001033 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001034 +705bonus pay for amazing work on #OSS 00010001034 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001035 +705bonus pay for amazing work on #OSS 00010001035 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001036 +705bonus pay for amazing work on #OSS 00010001036 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001037 +705bonus pay for amazing work on #OSS 00010001037 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001038 +705bonus pay for amazing work on #OSS 00010001038 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001039 +705bonus pay for amazing work on #OSS 00010001039 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001040 +705bonus pay for amazing work on #OSS 00010001040 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001041 +705bonus pay for amazing work on #OSS 00010001041 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001042 +705bonus pay for amazing work on #OSS 00010001042 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001043 +705bonus pay for amazing work on #OSS 00010001043 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001044 +705bonus pay for amazing work on #OSS 00010001044 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001045 +705bonus pay for amazing work on #OSS 00010001045 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001046 +705bonus pay for amazing work on #OSS 00010001046 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001047 +705bonus pay for amazing work on #OSS 00010001047 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001048 +705bonus pay for amazing work on #OSS 00010001048 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001049 +705bonus pay for amazing work on #OSS 00010001049 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001050 +705bonus pay for amazing work on #OSS 00010001050 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001051 +705bonus pay for amazing work on #OSS 00010001051 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001052 +705bonus pay for amazing work on #OSS 00010001052 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001053 +705bonus pay for amazing work on #OSS 00010001053 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001054 +705bonus pay for amazing work on #OSS 00010001054 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001055 +705bonus pay for amazing work on #OSS 00010001055 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001056 +705bonus pay for amazing work on #OSS 00010001056 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001057 +705bonus pay for amazing work on #OSS 00010001057 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001058 +705bonus pay for amazing work on #OSS 00010001058 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001059 +705bonus pay for amazing work on #OSS 00010001059 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001060 +705bonus pay for amazing work on #OSS 00010001060 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001061 +705bonus pay for amazing work on #OSS 00010001061 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001062 +705bonus pay for amazing work on #OSS 00010001062 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001063 +705bonus pay for amazing work on #OSS 00010001063 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001064 +705bonus pay for amazing work on #OSS 00010001064 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001065 +705bonus pay for amazing work on #OSS 00010001065 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001066 +705bonus pay for amazing work on #OSS 00010001066 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001067 +705bonus pay for amazing work on #OSS 00010001067 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001068 +705bonus pay for amazing work on #OSS 00010001068 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001069 +705bonus pay for amazing work on #OSS 00010001069 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001070 +705bonus pay for amazing work on #OSS 00010001070 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001071 +705bonus pay for amazing work on #OSS 00010001071 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001072 +705bonus pay for amazing work on #OSS 00010001072 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001073 +705bonus pay for amazing work on #OSS 00010001073 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001074 +705bonus pay for amazing work on #OSS 00010001074 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001075 +705bonus pay for amazing work on #OSS 00010001075 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001076 +705bonus pay for amazing work on #OSS 00010001076 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001077 +705bonus pay for amazing work on #OSS 00010001077 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001078 +705bonus pay for amazing work on #OSS 00010001078 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001079 +705bonus pay for amazing work on #OSS 00010001079 +62223138010481967038518 0000100000#9h32axwUf3RCZ#Emma Johnson 1121042880001080 +705bonus pay for amazing work on #OSS 00010001080 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#Ava Brown 1121042880001081 +705bonus pay for amazing work on #OSS 00010001081 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001082 +705bonus pay for amazing work on #OSS 00010001082 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001083 +705bonus pay for amazing work on #OSS 00010001083 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001084 +705bonus pay for amazing work on #OSS 00010001084 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001085 +705bonus pay for amazing work on #OSS 00010001085 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001086 +705bonus pay for amazing work on #OSS 00010001086 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001087 +705bonus pay for amazing work on #OSS 00010001087 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001088 +705bonus pay for amazing work on #OSS 00010001088 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001089 +705bonus pay for amazing work on #OSS 00010001089 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001090 +705bonus pay for amazing work on #OSS 00010001090 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001091 +705bonus pay for amazing work on #OSS 00010001091 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001092 +705bonus pay for amazing work on #OSS 00010001092 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001093 +705bonus pay for amazing work on #OSS 00010001093 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001094 +705bonus pay for amazing work on #OSS 00010001094 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001095 +705bonus pay for amazing work on #OSS 00010001095 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001096 +705bonus pay for amazing work on #OSS 00010001096 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001097 +705bonus pay for amazing work on #OSS 00010001097 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001098 +705bonus pay for amazing work on #OSS 00010001098 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001099 +705bonus pay for amazing work on #OSS 00010001099 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001100 +705bonus pay for amazing work on #OSS 00010001100 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001101 +705bonus pay for amazing work on #OSS 00010001101 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001102 +705bonus pay for amazing work on #OSS 00010001102 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001103 +705bonus pay for amazing work on #OSS 00010001103 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001104 +705bonus pay for amazing work on #OSS 00010001104 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001105 +705bonus pay for amazing work on #OSS 00010001105 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001106 +705bonus pay for amazing work on #OSS 00010001106 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001107 +705bonus pay for amazing work on #OSS 00010001107 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001108 +705bonus pay for amazing work on #OSS 00010001108 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001109 +705bonus pay for amazing work on #OSS 00010001109 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001110 +705bonus pay for amazing work on #OSS 00010001110 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001111 +705bonus pay for amazing work on #OSS 00010001111 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001112 +705bonus pay for amazing work on #OSS 00010001112 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001113 +705bonus pay for amazing work on #OSS 00010001113 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001114 +705bonus pay for amazing work on #OSS 00010001114 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001115 +705bonus pay for amazing work on #OSS 00010001115 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001116 +705bonus pay for amazing work on #OSS 00010001116 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001117 +705bonus pay for amazing work on #OSS 00010001117 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001118 +705bonus pay for amazing work on #OSS 00010001118 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001119 +705bonus pay for amazing work on #OSS 00010001119 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001120 +705bonus pay for amazing work on #OSS 00010001120 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001121 +705bonus pay for amazing work on #OSS 00010001121 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001122 +705bonus pay for amazing work on #OSS 00010001122 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001123 +705bonus pay for amazing work on #OSS 00010001123 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001124 +705bonus pay for amazing work on #OSS 00010001124 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001125 +705bonus pay for amazing work on #OSS 00010001125 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001126 +705bonus pay for amazing work on #OSS 00010001126 +62223138010481967038518 0000100000#3ldvwYFeFjwNI#William Brown 1121042880001127 +705bonus pay for amazing work on #OSS 00010001127 +62223138010481967038518 0000100000#rbQebs4WIqZFc#William Thomas 1121042880001128 +705bonus pay for amazing work on #OSS 00010001128 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001129 +705bonus pay for amazing work on #OSS 00010001129 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001130 +705bonus pay for amazing work on #OSS 00010001130 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001131 +705bonus pay for amazing work on #OSS 00010001131 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001132 +705bonus pay for amazing work on #OSS 00010001132 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001133 +705bonus pay for amazing work on #OSS 00010001133 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001134 +705bonus pay for amazing work on #OSS 00010001134 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001135 +705bonus pay for amazing work on #OSS 00010001135 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001136 +705bonus pay for amazing work on #OSS 00010001136 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001137 +705bonus pay for amazing work on #OSS 00010001137 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001138 +705bonus pay for amazing work on #OSS 00010001138 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001139 +705bonus pay for amazing work on #OSS 00010001139 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001140 +705bonus pay for amazing work on #OSS 00010001140 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001141 +705bonus pay for amazing work on #OSS 00010001141 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001142 +705bonus pay for amazing work on #OSS 00010001142 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001143 +705bonus pay for amazing work on #OSS 00010001143 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001144 +705bonus pay for amazing work on #OSS 00010001144 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001145 +705bonus pay for amazing work on #OSS 00010001145 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001146 +705bonus pay for amazing work on #OSS 00010001146 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001147 +705bonus pay for amazing work on #OSS 00010001147 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001148 +705bonus pay for amazing work on #OSS 00010001148 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001149 +705bonus pay for amazing work on #OSS 00010001149 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001150 +705bonus pay for amazing work on #OSS 00010001150 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001151 +705bonus pay for amazing work on #OSS 00010001151 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001152 +705bonus pay for amazing work on #OSS 00010001152 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001153 +705bonus pay for amazing work on #OSS 00010001153 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001154 +705bonus pay for amazing work on #OSS 00010001154 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001155 +705bonus pay for amazing work on #OSS 00010001155 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001156 +705bonus pay for amazing work on #OSS 00010001156 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001157 +705bonus pay for amazing work on #OSS 00010001157 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001158 +705bonus pay for amazing work on #OSS 00010001158 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001159 +705bonus pay for amazing work on #OSS 00010001159 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001160 +705bonus pay for amazing work on #OSS 00010001160 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001161 +705bonus pay for amazing work on #OSS 00010001161 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001162 +705bonus pay for amazing work on #OSS 00010001162 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001163 +705bonus pay for amazing work on #OSS 00010001163 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001164 +705bonus pay for amazing work on #OSS 00010001164 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001165 +705bonus pay for amazing work on #OSS 00010001165 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001166 +705bonus pay for amazing work on #OSS 00010001166 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001167 +705bonus pay for amazing work on #OSS 00010001167 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001168 +705bonus pay for amazing work on #OSS 00010001168 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001169 +705bonus pay for amazing work on #OSS 00010001169 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001170 +705bonus pay for amazing work on #OSS 00010001170 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001171 +705bonus pay for amazing work on #OSS 00010001171 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001172 +705bonus pay for amazing work on #OSS 00010001172 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001173 +705bonus pay for amazing work on #OSS 00010001173 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001174 +705bonus pay for amazing work on #OSS 00010001174 +62223138010481967038518 0000100000#rbQebs4WIqZFc#Ella Thomas 1121042880001175 +705bonus pay for amazing work on #OSS 00010001175 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001176 +705bonus pay for amazing work on #OSS 00010001176 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001177 +705bonus pay for amazing work on #OSS 00010001177 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001178 +705bonus pay for amazing work on #OSS 00010001178 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001179 +705bonus pay for amazing work on #OSS 00010001179 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001180 +705bonus pay for amazing work on #OSS 00010001180 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001181 +705bonus pay for amazing work on #OSS 00010001181 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001182 +705bonus pay for amazing work on #OSS 00010001182 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001183 +705bonus pay for amazing work on #OSS 00010001183 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001184 +705bonus pay for amazing work on #OSS 00010001184 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001185 +705bonus pay for amazing work on #OSS 00010001185 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001186 +705bonus pay for amazing work on #OSS 00010001186 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001187 +705bonus pay for amazing work on #OSS 00010001187 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001188 +705bonus pay for amazing work on #OSS 00010001188 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001189 +705bonus pay for amazing work on #OSS 00010001189 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001190 +705bonus pay for amazing work on #OSS 00010001190 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001191 +705bonus pay for amazing work on #OSS 00010001191 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001192 +705bonus pay for amazing work on #OSS 00010001192 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001193 +705bonus pay for amazing work on #OSS 00010001193 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001194 +705bonus pay for amazing work on #OSS 00010001194 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001195 +705bonus pay for amazing work on #OSS 00010001195 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001196 +705bonus pay for amazing work on #OSS 00010001196 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001197 +705bonus pay for amazing work on #OSS 00010001197 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001198 +705bonus pay for amazing work on #OSS 00010001198 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001199 +705bonus pay for amazing work on #OSS 00010001199 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001200 +705bonus pay for amazing work on #OSS 00010001200 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001201 +705bonus pay for amazing work on #OSS 00010001201 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001202 +705bonus pay for amazing work on #OSS 00010001202 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001203 +705bonus pay for amazing work on #OSS 00010001203 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001204 +705bonus pay for amazing work on #OSS 00010001204 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001205 +705bonus pay for amazing work on #OSS 00010001205 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001206 +705bonus pay for amazing work on #OSS 00010001206 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001207 +705bonus pay for amazing work on #OSS 00010001207 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001208 +705bonus pay for amazing work on #OSS 00010001208 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001209 +705bonus pay for amazing work on #OSS 00010001209 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001210 +705bonus pay for amazing work on #OSS 00010001210 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001211 +705bonus pay for amazing work on #OSS 00010001211 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001212 +705bonus pay for amazing work on #OSS 00010001212 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001213 +705bonus pay for amazing work on #OSS 00010001213 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001214 +705bonus pay for amazing work on #OSS 00010001214 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001215 +705bonus pay for amazing work on #OSS 00010001215 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001216 +705bonus pay for amazing work on #OSS 00010001216 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001217 +705bonus pay for amazing work on #OSS 00010001217 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001218 +705bonus pay for amazing work on #OSS 00010001218 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001219 +705bonus pay for amazing work on #OSS 00010001219 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001220 +705bonus pay for amazing work on #OSS 00010001220 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001221 +705bonus pay for amazing work on #OSS 00010001221 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001222 +705bonus pay for amazing work on #OSS 00010001222 +62223138010481967038518 0000100000#8xo7U8d55wAdj#Mia Wilson 1121042880001223 +705bonus pay for amazing work on #OSS 00010001223 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001224 +705bonus pay for amazing work on #OSS 00010001224 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001225 +705bonus pay for amazing work on #OSS 00010001225 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001226 +705bonus pay for amazing work on #OSS 00010001226 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001227 +705bonus pay for amazing work on #OSS 00010001227 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001228 +705bonus pay for amazing work on #OSS 00010001228 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001229 +705bonus pay for amazing work on #OSS 00010001229 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001230 +705bonus pay for amazing work on #OSS 00010001230 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001231 +705bonus pay for amazing work on #OSS 00010001231 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001232 +705bonus pay for amazing work on #OSS 00010001232 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001233 +705bonus pay for amazing work on #OSS 00010001233 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001234 +705bonus pay for amazing work on #OSS 00010001234 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001235 +705bonus pay for amazing work on #OSS 00010001235 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001236 +705bonus pay for amazing work on #OSS 00010001236 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001237 +705bonus pay for amazing work on #OSS 00010001237 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001238 +705bonus pay for amazing work on #OSS 00010001238 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001239 +705bonus pay for amazing work on #OSS 00010001239 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001240 +705bonus pay for amazing work on #OSS 00010001240 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001241 +705bonus pay for amazing work on #OSS 00010001241 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001242 +705bonus pay for amazing work on #OSS 00010001242 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001243 +705bonus pay for amazing work on #OSS 00010001243 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001244 +705bonus pay for amazing work on #OSS 00010001244 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001245 +705bonus pay for amazing work on #OSS 00010001245 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001246 +705bonus pay for amazing work on #OSS 00010001246 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001247 +705bonus pay for amazing work on #OSS 00010001247 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001248 +705bonus pay for amazing work on #OSS 00010001248 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001249 +705bonus pay for amazing work on #OSS 00010001249 +62223138010481967038518 0000100000#wuDASrtkr9R1U#William Brown 1121042880001250 +705bonus pay for amazing work on #OSS 00010001250 +82000025008922512500000000000000000125000000121042882 121042880000004 +9000004001001000100005690050000000000000000000500000000 diff --git a/cmd/writeACH/main.go b/cmd/writeACH/main.go new file mode 100644 index 000000000..ea945e7f2 --- /dev/null +++ b/cmd/writeACH/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "flag" + "fmt" + "github.com/go-randomdata" + "github.com/moov-io/ach" + "log" + "os" + "runtime/pprof" + "time" +) + +// main creates an ACH File with 4 batches of SEC Code PPD. +// Each batch contains an EntryAddendaCount of 2500. +func main() { + + var fPath = flag.String("fPath", "", "File Path") + var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + + flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + path := *fPath + + f, err := os.Create(path + time.Now().UTC().Format("200601021504") + ".ach") + if err != nil { + fmt.Printf("%T: %s", err, err) + } + + // To create a file + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" + fh.ImmediateOrigin = "121042882" + fh.FileCreationDate = time.Now() + fh.ImmediateDestinationName = "Citadel" + fh.ImmediateOriginName = "Wells Fargo" + file := ach.NewFile() + file.SetHeader(fh) + + // Create 4 Batches of SEC Code PPD + for i := 0; i < 4; i++ { + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 200 + bh.CompanyName = "Wells Fargo" + bh.CompanyIdentification = "121042882" + bh.StandardEntryClassCode = "PPD" + bh.CompanyEntryDescription = "Trans. Description" + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" + + batch, _ := ach.NewBatch(bh) + + // Create Entry + entrySeq := 0 + for i := 0; i < 1250; i++ { + entrySeq = entrySeq + 1 + + entry_entrySeq := ach.NewEntryDetail() + entry_entrySeq.TransactionCode = 22 + entry_entrySeq.SetRDFI("231380104") + entry_entrySeq.DFIAccountNumber = "81967038518" + entry_entrySeq.Amount = 100000 + entry_entrySeq.IndividualName = randomdata.FullName(randomdata.RandomGender) + entry_entrySeq.SetTraceNumber(bh.ODFIIdentification, entrySeq) + entry_entrySeq.IdentificationNumber = "#" + randomdata.RandStringRunes(13) + "#" + entry_entrySeq.Category = ach.CategoryForward + + // Add addenda record for an entry + addenda_entrySeq := ach.NewAddenda05() + addenda_entrySeq.PaymentRelatedInformation = "bonus pay for amazing work on #OSS" + entry_entrySeq.AddAddenda(addenda_entrySeq) + + // Add entries + batch.AddEntry(entry_entrySeq) + + } + + // Create the batch. + if err := batch.Create(); err != nil { + fmt.Printf("%T: %s", err, err) + } + + // Add batch to the file + file.AddBatch(batch) + } + + // Create the file + if err := file.Create(); err != nil { + fmt.Printf("%T: %s", err, err) + } + + // Write to a file + w := ach.NewWriter(f) + if err := w.Write(file); err != nil { + fmt.Printf("%T: %s", err, err) + } + w.Flush() + f.Close() +} diff --git a/cmd/writeACH/main_test.go b/cmd/writeACH/main_test.go new file mode 100644 index 000000000..0bdfb8dd1 --- /dev/null +++ b/cmd/writeACH/main_test.go @@ -0,0 +1,23 @@ +package main + +import ( + "testing" +) + +// TestFileCreate tests creating an ACH File +func TestFileWrite(t *testing.T) { + FileWrite(t) +} + +//BenchmarkTestFileCreate benchmarks creating an ACH File +func BenchmarkTestFileWrite(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + FileWrite(b) + } +} + +// FileCreate creates an ACH File +func FileWrite(t testing.TB) { + main() +} diff --git a/cmd/writeACH/old b/cmd/writeACH/old new file mode 100644 index 000000000..95eb3485d --- /dev/null +++ b/cmd/writeACH/old @@ -0,0 +1,30 @@ +C:\Users\Owner\AppData\Local\Temp\go-build176990002\b001\writeACH.test.exe flag redefined: fPath +--- FAIL: TestFileCreate (0.00s) +panic: C:\Users\Owner\AppData\Local\Temp\go-build176990002\b001\writeACH.test.exe flag redefined: fPath [recovered] + panic: C:\Users\Owner\AppData\Local\Temp\go-build176990002\b001\writeACH.test.exe flag redefined: fPath + +goroutine 50 [running]: +testing.tRunner.func1(0xc0420fe2d0) + C:/Go/src/testing/testing.go:742 +0x2a4 +panic(0x556400, 0xc042226270) + C:/Go/src/runtime/panic.go:505 +0x237 +flag.(*FlagSet).Var(0xc042092000, 0x5b1a40, 0xc042226230, 0x591fa0, 0x5, 0x59292e, 0x9) + C:/Go/src/flag/flag.go:810 +0x547 +flag.(*FlagSet).StringVar(0xc042092000, 0xc042226230, 0x591fa0, 0x5, 0x0, 0x0, 0x59292e, 0x9) + C:/Go/src/flag/flag.go:713 +0x92 +flag.(*FlagSet).String(0xc042092000, 0x591fa0, 0x5, 0x0, 0x0, 0x59292e, 0x9, 0x0) + C:/Go/src/flag/flag.go:726 +0x92 +flag.String(0x591fa0, 0x5, 0x0, 0x0, 0x59292e, 0x9, 0x0) + C:/Go/src/flag/flag.go:733 +0x70 +github.com/bkmoovio/ach/cmd/writeACH.main() + C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/writeACH/main.go:18 +0x6f +github.com/bkmoovio/ach/cmd/writeACH.FileCreate(0x5b3200, 0xc0420fe2d0) + C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/writeACH/main_test.go:23 +0x27 +github.com/bkmoovio/ach/cmd/writeACH.TestFileCreate(0xc0420fe2d0) + C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/writeACH/main_test.go:10 +0x3e +testing.tRunner(0xc0420fe2d0, 0x59ef18) + C:/Go/src/testing/testing.go:777 +0xd7 +created by testing.(*T).Run + C:/Go/src/testing/testing.go:824 +0x2e7 +exit status 2 +FAIL github.com/bkmoovio/ach/cmd/writeACH 0.579s diff --git a/cmd/writeACH/writeACH.pprof b/cmd/writeACH/writeACH.pprof new file mode 100644 index 0000000000000000000000000000000000000000..e843ab1004b1cbb3d5851bff88582de40d40acc9 GIT binary patch literal 1306 zcmV+#1?Bo5iwFP!00004|CCc*XdGo1{&usQoc)`eY<4#%f6Zi)w8`*wh7NrFARpgF zO-);1xRC#a&lfNKqu|L zE`UyaLMY*?F!UXbIvbjXEBbDy#f=o82NRX#j ze-b5fxS#WLSfY}gS0pIVkyi0yZ&RO*^!Y+Ir<6gWr>==q^7u(YB)!aC@>r&_+!<3z zqN-3y&`n1pRRX``)=AuVX(^MDfTp_(39*)6{GQc>A7YnmlH8cZ;Mi*Q(qqCi5BnxpRIrus>pqc1Z6t^$J-o{ z@M2m8kdTPveno-`-TGA^-FS>6-8eu40_mfdeisPhS&kqM(x5$F1zbZf($7oCdqjdWNkwgX8sQ?umr*V;l zLLWYJjwjKFC;98_!wH&@WkrH9`s#wni10Aah;SvX6d8@vxp&X=^8L6*1?b0Bv`U^- z3NS$j-@m|t3_cmV6C?7dGB!SuGyY|C1*WsOzyc#}d3855I;_RIk#kMg3z+4()xa2Z z%y3@!O}DB|s=iyB^Xk6ErnEW6s>Z;~w7$X91K-k}CQbL}VJV&p2tGV2Bs%B`4 zGV9#xHWt=>mpgcl@&B1={h@&Qf&Sobm-%|l6WzD-&?CFYgkQ}w#@Z3Jh&bzej-j+A z&DwqtZZREZjJD_07ZwC1dEIiV`flG2*}9pHIyd00 z=l!}H+762x`&-dB&hL@2|QnfsH7xP2r2UFUj=?831bTwI1XSdQA{ow{ft#>%!;9_AO_pVGwFSbI-F?)L4F`C85U QGXMbp|E+J5u8au)0F@+@9RL6T literal 0 HcmV?d00001 From a8e7f7be9fcf5fd1f4be86ffea2f6dd6b7e23296 Mon Sep 17 00:00:00 2001 From: Matias Insaurralde Date: Fri, 11 May 2018 04:10:19 -0400 Subject: [PATCH 0082/1694] Follow lint and gosimple suggestions --- batch.go | 10 ++-------- batchCCD.go | 5 +---- batchCOR.go | 5 +---- batchPPD.go | 5 +---- batchTEL.go | 11 ++--------- batchWeb.go | 11 ++--------- file.go | 6 +----- 7 files changed, 10 insertions(+), 43 deletions(-) diff --git a/batch.go b/batch.go index 34d17870c..c89323daa 100644 --- a/batch.go +++ b/batch.go @@ -97,10 +97,7 @@ func (batch *batch) verify() error { if err := batch.isAddendaSequence(); err != nil { return err } - if err := batch.isCategory(); err != nil { - return err - } - return nil + return batch.isCategory() } // Build creates valid batch by building sequence numbers and batch batch control. An error is returned if @@ -209,10 +206,7 @@ func (batch *batch) isFieldInclusion() error { } } } - if err := batch.control.Validate(); err != nil { - return err - } - return nil + return batch.control.Validate() } // isBatchEntryCount validate Entry count is accurate diff --git a/batchCCD.go b/batchCCD.go index 6c4486469..d3c00a7e8 100644 --- a/batchCCD.go +++ b/batchCCD.go @@ -50,8 +50,5 @@ func (batch *BatchCCD) Create() error { return err } - if err := batch.Validate(); err != nil { - return err - } - return nil + return batch.Validate() } diff --git a/batchCOR.go b/batchCOR.go index 1646abf5d..94ff94743 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -58,10 +58,7 @@ func (batch *BatchCOR) Create() error { return err } - if err := batch.Validate(); err != nil { - return err - } - return nil + return batch.Validate() } // isAddenda98 verifies that a Addenda98 exists for each EntryDetail and is Validated diff --git a/batchPPD.go b/batchPPD.go index 539d4ad0c..f87904003 100644 --- a/batchPPD.go +++ b/batchPPD.go @@ -47,8 +47,5 @@ func (batch *BatchPPD) Create() error { // Additional steps specific to batch type // ... - if err := batch.Validate(); err != nil { - return err - } - return nil + return batch.Validate() } diff --git a/batchTEL.go b/batchTEL.go index 50f92791c..c4bc73e67 100644 --- a/batchTEL.go +++ b/batchTEL.go @@ -42,11 +42,7 @@ func (batch *BatchTEL) Validate() error { } } - if err := batch.isPaymentTypeCode(); err != nil { - return err - } - - return nil + return batch.isPaymentTypeCode() } // Create builds the batch sequence numbers and batch control. Additional creation @@ -56,8 +52,5 @@ func (batch *BatchTEL) Create() error { return err } - if err := batch.Validate(); err != nil { - return err - } - return nil + return batch.Validate() } diff --git a/batchWeb.go b/batchWeb.go index 03cd232c1..c88c946c9 100644 --- a/batchWeb.go +++ b/batchWeb.go @@ -44,11 +44,7 @@ func (batch *BatchWEB) Validate() error { return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } - if err := batch.isPaymentTypeCode(); err != nil { - return err - } - - return nil + return batch.isPaymentTypeCode() } // Create builds the batch sequence numbers and batch control. Additional creation @@ -58,8 +54,5 @@ func (batch *BatchWEB) Create() error { return err } - if err := batch.Validate(); err != nil { - return err - } - return nil + return batch.Validate() } diff --git a/file.go b/file.go index 75fdfea41..e248372e8 100644 --- a/file.go +++ b/file.go @@ -158,11 +158,7 @@ func (f *File) Validate() error { return err } - if err := f.isEntryHash(); err != nil { - return err - } - - return nil + return f.isEntryHash() } // isEntryAddenda is prepared by hashing the RDFI’s 8-digit Routing Number in each entry. From e11583728185447f86e823754f091925132474cd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 11 May 2018 09:37:30 -0400 Subject: [PATCH 0083/1694] #165 golint # 165 golint --- addenda05.go | 4 +--- cmd/writeACH/main.go | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/addenda05.go b/addenda05.go index 9ff3191d8..a16a76083 100644 --- a/addenda05.go +++ b/addenda05.go @@ -8,7 +8,6 @@ import ( // Addenda05 is a Addendumer addenda which provides business transaction information for Addenda Type // Code 05 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. // Future development to allow for use case specific 05 addenda records. - type Addenda05 struct { // RecordType defines the type of record in the block. entryAddenda05 Pos 7 recordType string @@ -32,7 +31,6 @@ type Addenda05 struct { } // NewAddenda05 returns a new Addenda05 with default values for none exported fields - func NewAddenda05() *Addenda05 { addenda05 := new(Addenda05) addenda05.recordType = "7" @@ -65,7 +63,7 @@ func (addenda05 *Addenda05) String() string { addenda05.EntryDetailSequenceNumberField()) } -// SetPaymentRealtedInformation +// SetPaymentRelatedInformation sets payment information func (addenda05 *Addenda05) SetPaymentRelatedInformation(s string) *Addenda05 { addenda05.PaymentRelatedInformation = s diff --git a/cmd/writeACH/main.go b/cmd/writeACH/main.go index ea945e7f2..d25496854 100644 --- a/cmd/writeACH/main.go +++ b/cmd/writeACH/main.go @@ -63,23 +63,23 @@ func main() { for i := 0; i < 1250; i++ { entrySeq = entrySeq + 1 - entry_entrySeq := ach.NewEntryDetail() - entry_entrySeq.TransactionCode = 22 - entry_entrySeq.SetRDFI("231380104") - entry_entrySeq.DFIAccountNumber = "81967038518" - entry_entrySeq.Amount = 100000 - entry_entrySeq.IndividualName = randomdata.FullName(randomdata.RandomGender) - entry_entrySeq.SetTraceNumber(bh.ODFIIdentification, entrySeq) - entry_entrySeq.IdentificationNumber = "#" + randomdata.RandStringRunes(13) + "#" - entry_entrySeq.Category = ach.CategoryForward + entryEntrySeq := ach.NewEntryDetail() + entryEntrySeq.TransactionCode = 22 + entryEntrySeq.SetRDFI("231380104") + entryEntrySeq.DFIAccountNumber = "81967038518" + entryEntrySeq.Amount = 100000 + entryEntrySeq.IndividualName = randomdata.FullName(randomdata.RandomGender) + entryEntrySeq.SetTraceNumber(bh.ODFIIdentification, entrySeq) + entryEntrySeq.IdentificationNumber = "#" + randomdata.RandStringRunes(13) + "#" + entryEntrySeq.Category = ach.CategoryForward // Add addenda record for an entry - addenda_entrySeq := ach.NewAddenda05() - addenda_entrySeq.PaymentRelatedInformation = "bonus pay for amazing work on #OSS" - entry_entrySeq.AddAddenda(addenda_entrySeq) + addendaEntrySeq := ach.NewAddenda05() + addendaEntrySeq.PaymentRelatedInformation = "bonus pay for amazing work on #OSS" + entryEntrySeq.AddAddenda(addendaEntrySeq) // Add entries - batch.AddEntry(entry_entrySeq) + batch.AddEntry(entryEntrySeq) } From e9c94aaeb26c69532eebd3e545176cb775fcb301 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 11 May 2018 15:04:35 -0600 Subject: [PATCH 0084/1694] Adding Linters to build process --- .travis.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c1557d8e0..c7d052c75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,5 +4,25 @@ go: - tip before_install: - go get github.com/mattn/goveralls + - go get -u honnef.co/go/tools/cmd/megacheck + - go get -u github.com/client9/misspell/cmd/misspell + - go get -u golang.org/x/lint/golint + - go get github.com/fzipp/gocyclo + +before_script: + - GO_FILES=$(find . -iname '*.go' -type f | grep -v /example/) # All the .go files, excluding example/ vendor/ + + # script always run to completion (set +e). All of these code checks are must haves +# in a modern Go project. script: - - $HOME/gopath/bin/goveralls -service=travis-ci -ignore=main,main.go,/example/,./example/ + - test -z $(gofmt -s -l $GO_FILES) # Fail if a .go file hasn't been formatted with gofmt + - go test -v -race ./... # Run all the tests with the race detector enabled + - go vet ./... # go vet is the official Go static analyzer + - megacheck ./... # "go vet on steroids" + linter + - misspell -error -locale US . # check for spelling errors + - gocyclo -over 19 $GO_FILES # forbid code with huge functions + - golint -set_exit_status $(go list ./...) # one last linter + # output code coverage + - go test -v -covermode=count -coverprofile=coverage.out $GO_FILES + - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN + \ No newline at end of file From 297f8838fc9a6e30f0548e3dea1936b515ccbba6 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 11 May 2018 15:33:31 -0600 Subject: [PATCH 0085/1694] Gofmt -w files --- batchHeader_internal_test.go | 2 +- batch_test.go | 4 +--- fileHeader_internal_test.go | 2 +- reader_test.go | 4 +--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index 33b2317d5..1926d1e8c 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -305,4 +305,4 @@ func TestBHFieldInclusionODFIIdentification(t *testing.T) { } } } -} \ No newline at end of file +} diff --git a/batch_test.go b/batch_test.go index 79afbc875..2795ab79f 100644 --- a/batch_test.go +++ b/batch_test.go @@ -38,7 +38,6 @@ func mockEntryDetailInvalidTraceNumberODFI() *EntryDetail { return entry } - // Batch with no entries func mockBatchNoEntry() *batch { mockBatch := &batch{} @@ -351,7 +350,6 @@ func TestBatchFieldInclusion(t *testing.T) { } } - func TestBatchInvalidTraceNumberODFI(t *testing.T) { mockBatch := mockBatchInvalidTraceNumberODFI() if err := mockBatch.build(); err != nil { @@ -384,4 +382,4 @@ func TestBatchControl(t *testing.T) { t.Errorf("%T: %s", err, err) } } -} \ No newline at end of file +} diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index b15f6dbf5..cb649cfb5 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -338,4 +338,4 @@ func TestFHFieldInclusionCreationDate(t *testing.T) { } } } -} \ No newline at end of file +} diff --git a/reader_test.go b/reader_test.go index 127742fbe..7eda2d8c2 100644 --- a/reader_test.go +++ b/reader_test.go @@ -319,7 +319,6 @@ func TestFileAddenda99(t *testing.T) { } } - // TestFileAddendaOutsideBatch validation error populates through the reader func TestFileAddendaOutsideBatch(t *testing.T) { addenda := mockAddenda05() @@ -469,7 +468,6 @@ func TestFileAddendaOutsideEntry(t *testing.T) { } } - func TestFileFHImmediateOrigin(t *testing.T) { fh := mockFileHeader() fh.ImmediateDestination = "" @@ -486,4 +484,4 @@ func TestFileFHImmediateOrigin(t *testing.T) { } else { t.Errorf("%T: %s", err, err) } -} \ No newline at end of file +} From a6ed0f421f3e7a36c68b37e2324ec699fff5a503 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 11 May 2018 15:40:51 -0600 Subject: [PATCH 0086/1694] Utilize var $Go_FILES for all tests --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c7d052c75..d6b69cef3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ before_install: - go get -u github.com/client9/misspell/cmd/misspell - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo - + before_script: - GO_FILES=$(find . -iname '*.go' -type f | grep -v /example/) # All the .go files, excluding example/ vendor/ @@ -16,12 +16,12 @@ before_script: # in a modern Go project. script: - test -z $(gofmt -s -l $GO_FILES) # Fail if a .go file hasn't been formatted with gofmt - - go test -v -race ./... # Run all the tests with the race detector enabled - - go vet ./... # go vet is the official Go static analyzer - - megacheck ./... # "go vet on steroids" + linter + # - go test -v -race $GO_FILES # Run all the tests with the race detector enabled + - go vet $GO_FILES # go vet is the official Go static analyzer + - megacheck $GO_FILES # "go vet on steroids" + linter - misspell -error -locale US . # check for spelling errors - gocyclo -over 19 $GO_FILES # forbid code with huge functions - - golint -set_exit_status $(go list ./...) # one last linter + - golint -set_exit_status $(go list $GO_FILES) # one last linter # output code coverage - go test -v -covermode=count -coverprofile=coverage.out $GO_FILES - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN From d0c75c8ba8e1609637a0f800c3544181082fa82d Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 11 May 2018 15:41:05 -0600 Subject: [PATCH 0087/1694] Fix spelling --- example/simple-file-creation/main.go | 7 ++++--- record_test.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/example/simple-file-creation/main.go b/example/simple-file-creation/main.go index 9a7441937..c1163f788 100644 --- a/example/simple-file-creation/main.go +++ b/example/simple-file-creation/main.go @@ -4,8 +4,9 @@ import ( "fmt" "os" - "github.com/moov-io/ach" "time" + + "github.com/moov-io/ach" ) func main() { @@ -76,8 +77,8 @@ func main() { batch2, _ := ach.NewBatch(bh2) - // Add an entry and define if it is a single or reccuring payment - // The following is a reccuring payment for $7.99 + // Add an entry and define if it is a single or recurring payment + // The following is a recurring payment for $7.99 entry2 := ach.NewEntryDetail() entry2.TransactionCode = 22 diff --git a/record_test.go b/record_test.go index 08806a090..2d4234933 100644 --- a/record_test.go +++ b/record_test.go @@ -111,7 +111,7 @@ func TestBuildFile(t *testing.T) { batch, _ = NewBatch(mockBatchWEBHeader()) - // Add an entry and define if it is a single or reccuring payment + // Add an entry and define if it is a single or recurring payment // The following is a reoccuring payment for $7.99 entry = mockWEBEntryDetail() From 4dcfffd9954ecb319b855c14f8244ba2ca8ecb8f Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 11 May 2018 16:12:32 -0600 Subject: [PATCH 0088/1694] Handle the directory not individual files --- .travis.yml | 2 +- coverage.out | 688 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 689 insertions(+), 1 deletion(-) create mode 100644 coverage.out diff --git a/.travis.yml b/.travis.yml index d6b69cef3..7f575e492 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,6 @@ script: - gocyclo -over 19 $GO_FILES # forbid code with huge functions - golint -set_exit_status $(go list $GO_FILES) # one last linter # output code coverage - - go test -v -covermode=count -coverprofile=coverage.out $GO_FILES + - go test -v -covermode=count -coverprofile=coverage.out . - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN \ No newline at end of file diff --git a/coverage.out b/coverage.out new file mode 100644 index 000000000..55316af20 --- /dev/null +++ b/coverage.out @@ -0,0 +1,688 @@ +mode: count +github.com/wadearnold/ach/batchCOR.go:19.45,24.2 4 9 +github.com/wadearnold/ach/batchCOR.go:27.41,29.39 1 12 +github.com/wadearnold/ach/batchCOR.go:34.2,34.44 1 12 +github.com/wadearnold/ach/batchCOR.go:39.2,39.50 1 9 +github.com/wadearnold/ach/batchCOR.go:46.2,46.103 1 8 +github.com/wadearnold/ach/batchCOR.go:51.2,51.12 1 7 +github.com/wadearnold/ach/batchCOR.go:29.39,31.3 1 0 +github.com/wadearnold/ach/batchCOR.go:34.44,36.3 1 3 +github.com/wadearnold/ach/batchCOR.go:39.50,42.3 2 1 +github.com/wadearnold/ach/batchCOR.go:46.103,49.3 2 1 +github.com/wadearnold/ach/batchCOR.go:55.39,57.38 1 11 +github.com/wadearnold/ach/batchCOR.go:61.2,61.25 1 10 +github.com/wadearnold/ach/batchCOR.go:57.38,59.3 1 1 +github.com/wadearnold/ach/batchCOR.go:65.44,66.38 1 12 +github.com/wadearnold/ach/batchCOR.go:85.2,85.12 1 9 +github.com/wadearnold/ach/batchCOR.go:66.38,68.31 1 12 +github.com/wadearnold/ach/batchCOR.go:72.3,73.10 2 11 +github.com/wadearnold/ach/batchCOR.go:78.3,78.46 1 10 +github.com/wadearnold/ach/batchCOR.go:68.31,70.4 1 1 +github.com/wadearnold/ach/batchCOR.go:73.10,76.4 2 1 +github.com/wadearnold/ach/batchCOR.go:78.46,80.38 1 1 +github.com/wadearnold/ach/batchCOR.go:80.38,82.5 1 1 +github.com/wadearnold/ach/batchWeb.go:19.45,24.2 4 9 +github.com/wadearnold/ach/batchWeb.go:27.41,29.39 1 13 +github.com/wadearnold/ach/batchWeb.go:34.2,34.48 1 12 +github.com/wadearnold/ach/batchWeb.go:37.2,37.47 1 11 +github.com/wadearnold/ach/batchWeb.go:42.2,42.50 1 10 +github.com/wadearnold/ach/batchWeb.go:47.2,47.34 1 9 +github.com/wadearnold/ach/batchWeb.go:29.39,31.3 1 1 +github.com/wadearnold/ach/batchWeb.go:34.48,36.3 1 1 +github.com/wadearnold/ach/batchWeb.go:37.47,39.3 1 1 +github.com/wadearnold/ach/batchWeb.go:42.50,45.3 2 1 +github.com/wadearnold/ach/batchWeb.go:51.39,53.38 1 8 +github.com/wadearnold/ach/batchWeb.go:57.2,57.25 1 7 +github.com/wadearnold/ach/batchWeb.go:53.38,55.3 1 1 +github.com/wadearnold/ach/fileHeader.go:96.33,106.2 2 45 +github.com/wadearnold/ach/fileHeader.go:109.44,137.2 13 11 +github.com/wadearnold/ach/fileHeader.go:140.39,157.2 1 5 +github.com/wadearnold/ach/fileHeader.go:161.40,163.44 1 45 +github.com/wadearnold/ach/fileHeader.go:166.2,166.26 1 36 +github.com/wadearnold/ach/fileHeader.go:170.2,170.66 1 35 +github.com/wadearnold/ach/fileHeader.go:173.2,173.33 1 33 +github.com/wadearnold/ach/fileHeader.go:177.2,177.28 1 32 +github.com/wadearnold/ach/fileHeader.go:180.2,180.31 1 31 +github.com/wadearnold/ach/fileHeader.go:183.2,183.26 1 30 +github.com/wadearnold/ach/fileHeader.go:186.2,186.71 1 29 +github.com/wadearnold/ach/fileHeader.go:189.2,189.39 1 28 +github.com/wadearnold/ach/fileHeader.go:192.2,192.44 1 27 +github.com/wadearnold/ach/fileHeader.go:195.2,195.66 1 26 +github.com/wadearnold/ach/fileHeader.go:198.2,198.60 1 25 +github.com/wadearnold/ach/fileHeader.go:208.2,208.12 1 24 +github.com/wadearnold/ach/fileHeader.go:163.44,165.3 1 9 +github.com/wadearnold/ach/fileHeader.go:166.26,169.3 2 1 +github.com/wadearnold/ach/fileHeader.go:170.66,172.3 1 2 +github.com/wadearnold/ach/fileHeader.go:173.33,176.3 2 1 +github.com/wadearnold/ach/fileHeader.go:177.28,179.3 1 1 +github.com/wadearnold/ach/fileHeader.go:180.31,182.3 1 1 +github.com/wadearnold/ach/fileHeader.go:183.26,185.3 1 1 +github.com/wadearnold/ach/fileHeader.go:186.71,188.3 1 1 +github.com/wadearnold/ach/fileHeader.go:189.39,191.3 1 1 +github.com/wadearnold/ach/fileHeader.go:192.44,194.3 1 1 +github.com/wadearnold/ach/fileHeader.go:195.66,197.3 1 1 +github.com/wadearnold/ach/fileHeader.go:198.60,200.3 1 1 +github.com/wadearnold/ach/fileHeader.go:213.46,214.25 1 45 +github.com/wadearnold/ach/fileHeader.go:217.2,217.35 1 43 +github.com/wadearnold/ach/fileHeader.go:220.2,220.30 1 42 +github.com/wadearnold/ach/fileHeader.go:223.2,223.34 1 41 +github.com/wadearnold/ach/fileHeader.go:226.2,226.29 1 40 +github.com/wadearnold/ach/fileHeader.go:229.2,229.25 1 39 +github.com/wadearnold/ach/fileHeader.go:232.2,232.29 1 38 +github.com/wadearnold/ach/fileHeader.go:235.2,235.25 1 37 +github.com/wadearnold/ach/fileHeader.go:238.2,238.12 1 36 +github.com/wadearnold/ach/fileHeader.go:214.25,216.3 1 2 +github.com/wadearnold/ach/fileHeader.go:217.35,219.3 1 1 +github.com/wadearnold/ach/fileHeader.go:220.30,222.3 1 1 +github.com/wadearnold/ach/fileHeader.go:223.34,225.3 1 1 +github.com/wadearnold/ach/fileHeader.go:226.29,228.3 1 1 +github.com/wadearnold/ach/fileHeader.go:229.25,231.3 1 1 +github.com/wadearnold/ach/fileHeader.go:232.29,234.3 1 1 +github.com/wadearnold/ach/fileHeader.go:235.25,237.3 1 1 +github.com/wadearnold/ach/fileHeader.go:242.58,244.2 1 7 +github.com/wadearnold/ach/fileHeader.go:247.53,249.2 1 7 +github.com/wadearnold/ach/fileHeader.go:252.54,254.2 1 6 +github.com/wadearnold/ach/fileHeader.go:257.54,259.2 1 6 +github.com/wadearnold/ach/fileHeader.go:262.62,264.2 1 6 +github.com/wadearnold/ach/fileHeader.go:267.57,269.2 1 6 +github.com/wadearnold/ach/fileHeader.go:272.51,274.2 1 6 +github.com/wadearnold/ach/reader.go:22.37,23.20 1 2 +github.com/wadearnold/ach/reader.go:26.2,26.79 1 1 +github.com/wadearnold/ach/reader.go:23.20,25.3 1 1 +github.com/wadearnold/ach/reader.go:46.41,52.2 1 24 +github.com/wadearnold/ach/reader.go:56.49,58.2 1 24 +github.com/wadearnold/ach/reader.go:61.37,65.2 1 38 +github.com/wadearnold/ach/reader.go:70.39,73.23 2 27 +github.com/wadearnold/ach/reader.go:94.2,94.37 1 8 +github.com/wadearnold/ach/reader.go:99.2,99.39 1 5 +github.com/wadearnold/ach/reader.go:105.2,105.20 1 4 +github.com/wadearnold/ach/reader.go:73.23,78.10 4 73 +github.com/wadearnold/ach/reader.go:79.84,80.57 1 2 +github.com/wadearnold/ach/reader.go:83.35,86.31 3 3 +github.com/wadearnold/ach/reader.go:87.11,89.40 2 68 +github.com/wadearnold/ach/reader.go:80.57,82.5 1 1 +github.com/wadearnold/ach/reader.go:89.40,91.5 1 15 +github.com/wadearnold/ach/reader.go:94.37,98.3 2 3 +github.com/wadearnold/ach/reader.go:99.39,103.3 2 1 +github.com/wadearnold/ach/reader.go:108.60,111.26 2 2 +github.com/wadearnold/ach/reader.go:121.2,121.12 1 1 +github.com/wadearnold/ach/reader.go:111.26,113.39 2 658 +github.com/wadearnold/ach/reader.go:113.39,115.40 2 7 +github.com/wadearnold/ach/reader.go:118.4,118.15 1 6 +github.com/wadearnold/ach/reader.go:115.40,117.5 1 1 +github.com/wadearnold/ach/reader.go:124.36,125.20 1 75 +github.com/wadearnold/ach/reader.go:164.2,164.12 1 59 +github.com/wadearnold/ach/reader.go:126.21,127.45 1 9 +github.com/wadearnold/ach/reader.go:130.22,131.46 1 17 +github.com/wadearnold/ach/reader.go:134.22,135.46 1 16 +github.com/wadearnold/ach/reader.go:138.23,139.42 1 7 +github.com/wadearnold/ach/reader.go:142.23,143.47 1 9 +github.com/wadearnold/ach/reader.go:146.3,146.51 1 7 +github.com/wadearnold/ach/reader.go:150.3,151.23 2 6 +github.com/wadearnold/ach/reader.go:152.22,153.25 1 16 +github.com/wadearnold/ach/reader.go:157.3,157.46 1 6 +github.com/wadearnold/ach/reader.go:160.10,162.83 2 1 +github.com/wadearnold/ach/reader.go:127.45,129.4 1 2 +github.com/wadearnold/ach/reader.go:131.46,133.4 1 3 +github.com/wadearnold/ach/reader.go:135.46,137.4 1 1 +github.com/wadearnold/ach/reader.go:139.42,141.4 1 4 +github.com/wadearnold/ach/reader.go:143.47,145.4 1 2 +github.com/wadearnold/ach/reader.go:146.51,149.4 2 1 +github.com/wadearnold/ach/reader.go:153.25,155.9 1 10 +github.com/wadearnold/ach/reader.go:157.46,159.4 1 2 +github.com/wadearnold/ach/reader.go:168.42,170.37 2 11 +github.com/wadearnold/ach/reader.go:174.2,176.49 2 11 +github.com/wadearnold/ach/reader.go:179.2,179.12 1 9 +github.com/wadearnold/ach/reader.go:170.37,173.3 1 1 +github.com/wadearnold/ach/reader.go:176.49,178.3 1 2 +github.com/wadearnold/ach/reader.go:183.43,185.27 2 19 +github.com/wadearnold/ach/reader.go:191.2,193.38 3 18 +github.com/wadearnold/ach/reader.go:198.2,199.16 2 16 +github.com/wadearnold/ach/reader.go:203.2,204.12 2 16 +github.com/wadearnold/ach/reader.go:185.27,188.3 1 1 +github.com/wadearnold/ach/reader.go:193.38,195.3 1 2 +github.com/wadearnold/ach/reader.go:199.16,201.3 1 0 +github.com/wadearnold/ach/reader.go:208.43,210.27 2 18 +github.com/wadearnold/ach/reader.go:213.2,215.38 3 17 +github.com/wadearnold/ach/reader.go:218.2,219.12 2 17 +github.com/wadearnold/ach/reader.go:210.27,212.3 1 1 +github.com/wadearnold/ach/reader.go:215.38,217.3 1 0 +github.com/wadearnold/ach/reader.go:223.39,226.27 2 7 +github.com/wadearnold/ach/reader.go:230.2,230.43 1 6 +github.com/wadearnold/ach/reader.go:233.2,236.39 3 5 +github.com/wadearnold/ach/reader.go:265.2,265.12 1 3 +github.com/wadearnold/ach/reader.go:226.27,229.3 2 1 +github.com/wadearnold/ach/reader.go:230.43,232.3 1 1 +github.com/wadearnold/ach/reader.go:236.39,237.22 1 4 +github.com/wadearnold/ach/reader.go:238.13,241.47 3 2 +github.com/wadearnold/ach/reader.go:244.4,244.65 1 1 +github.com/wadearnold/ach/reader.go:245.13,248.47 3 1 +github.com/wadearnold/ach/reader.go:251.4,251.65 1 1 +github.com/wadearnold/ach/reader.go:252.13,255.47 3 1 +github.com/wadearnold/ach/reader.go:258.4,258.65 1 1 +github.com/wadearnold/ach/reader.go:241.47,243.5 1 1 +github.com/wadearnold/ach/reader.go:248.47,250.5 1 0 +github.com/wadearnold/ach/reader.go:255.47,257.5 1 0 +github.com/wadearnold/ach/reader.go:260.8,263.3 2 1 +github.com/wadearnold/ach/reader.go:269.44,271.27 2 11 +github.com/wadearnold/ach/reader.go:275.2,276.63 2 10 +github.com/wadearnold/ach/reader.go:279.2,279.12 1 9 +github.com/wadearnold/ach/reader.go:271.27,274.3 1 1 +github.com/wadearnold/ach/reader.go:276.63,278.3 1 1 +github.com/wadearnold/ach/reader.go:283.43,285.39 2 8 +github.com/wadearnold/ach/reader.go:289.2,290.50 2 7 +github.com/wadearnold/ach/reader.go:293.2,293.12 1 6 +github.com/wadearnold/ach/reader.go:285.39,288.3 1 1 +github.com/wadearnold/ach/reader.go:290.50,292.3 1 1 +github.com/wadearnold/ach/batchCCD.go:15.45,20.2 4 7 +github.com/wadearnold/ach/batchCCD.go:23.41,25.39 1 13 +github.com/wadearnold/ach/batchCCD.go:30.2,30.48 1 10 +github.com/wadearnold/ach/batchCCD.go:33.2,33.47 1 8 +github.com/wadearnold/ach/batchCCD.go:38.2,38.50 1 7 +github.com/wadearnold/ach/batchCCD.go:43.2,43.12 1 6 +github.com/wadearnold/ach/batchCCD.go:25.39,27.3 1 3 +github.com/wadearnold/ach/batchCCD.go:30.48,32.3 1 2 +github.com/wadearnold/ach/batchCCD.go:33.47,35.3 1 1 +github.com/wadearnold/ach/batchCCD.go:38.50,41.3 2 1 +github.com/wadearnold/ach/batchCCD.go:47.39,49.38 1 8 +github.com/wadearnold/ach/batchCCD.go:53.2,53.25 1 7 +github.com/wadearnold/ach/batchCCD.go:49.38,51.3 1 1 +github.com/wadearnold/ach/batchHeader.go:108.36,115.2 2 228 +github.com/wadearnold/ach/batchHeader.go:118.45,153.2 13 18 +github.com/wadearnold/ach/batchHeader.go:156.40,172.2 1 14 +github.com/wadearnold/ach/batchHeader.go:176.41,177.44 1 212 +github.com/wadearnold/ach/batchHeader.go:180.2,180.26 1 198 +github.com/wadearnold/ach/batchHeader.go:184.2,184.63 1 197 +github.com/wadearnold/ach/batchHeader.go:187.2,187.64 1 194 +github.com/wadearnold/ach/batchHeader.go:190.2,190.75 1 192 +github.com/wadearnold/ach/batchHeader.go:193.2,193.58 1 191 +github.com/wadearnold/ach/batchHeader.go:196.2,196.71 1 190 +github.com/wadearnold/ach/batchHeader.go:199.2,199.68 1 189 +github.com/wadearnold/ach/batchHeader.go:202.2,202.70 1 188 +github.com/wadearnold/ach/batchHeader.go:205.2,205.12 1 187 +github.com/wadearnold/ach/batchHeader.go:177.44,179.3 1 14 +github.com/wadearnold/ach/batchHeader.go:180.26,183.3 2 1 +github.com/wadearnold/ach/batchHeader.go:184.63,186.3 1 3 +github.com/wadearnold/ach/batchHeader.go:187.64,189.3 1 2 +github.com/wadearnold/ach/batchHeader.go:190.75,192.3 1 1 +github.com/wadearnold/ach/batchHeader.go:193.58,195.3 1 1 +github.com/wadearnold/ach/batchHeader.go:196.71,198.3 1 1 +github.com/wadearnold/ach/batchHeader.go:199.68,201.3 1 1 +github.com/wadearnold/ach/batchHeader.go:202.70,204.3 1 1 +github.com/wadearnold/ach/batchHeader.go:210.47,211.25 1 212 +github.com/wadearnold/ach/batchHeader.go:214.2,214.30 1 211 +github.com/wadearnold/ach/batchHeader.go:217.2,217.26 1 204 +github.com/wadearnold/ach/batchHeader.go:220.2,220.36 1 203 +github.com/wadearnold/ach/batchHeader.go:223.2,223.37 1 202 +github.com/wadearnold/ach/batchHeader.go:226.2,226.38 1 201 +github.com/wadearnold/ach/batchHeader.go:229.2,229.33 1 200 +github.com/wadearnold/ach/batchHeader.go:232.2,232.12 1 198 +github.com/wadearnold/ach/batchHeader.go:211.25,213.3 1 1 +github.com/wadearnold/ach/batchHeader.go:214.30,216.3 1 7 +github.com/wadearnold/ach/batchHeader.go:217.26,219.3 1 1 +github.com/wadearnold/ach/batchHeader.go:220.36,222.3 1 1 +github.com/wadearnold/ach/batchHeader.go:223.37,225.3 1 1 +github.com/wadearnold/ach/batchHeader.go:226.38,228.3 1 1 +github.com/wadearnold/ach/batchHeader.go:229.33,231.3 1 2 +github.com/wadearnold/ach/batchHeader.go:236.50,238.2 1 15 +github.com/wadearnold/ach/batchHeader.go:241.63,243.2 1 15 +github.com/wadearnold/ach/batchHeader.go:246.60,248.2 1 15 +github.com/wadearnold/ach/batchHeader.go:251.62,253.2 1 15 +github.com/wadearnold/ach/batchHeader.go:256.61,258.2 1 14 +github.com/wadearnold/ach/batchHeader.go:261.57,263.2 1 15 +github.com/wadearnold/ach/batchHeader.go:266.57,268.2 1 188 +github.com/wadearnold/ach/batchHeader.go:271.50,273.2 1 15 +github.com/wadearnold/ach/batchHeader.go:275.53,277.2 1 14 +github.com/wadearnold/ach/batchPPD.go:13.45,18.2 4 41 +github.com/wadearnold/ach/batchPPD.go:21.41,23.39 1 30 +github.com/wadearnold/ach/batchPPD.go:29.2,29.48 1 23 +github.com/wadearnold/ach/batchPPD.go:32.2,32.47 1 23 +github.com/wadearnold/ach/batchPPD.go:38.2,38.12 1 20 +github.com/wadearnold/ach/batchPPD.go:23.39,25.3 1 7 +github.com/wadearnold/ach/batchPPD.go:29.48,31.3 1 0 +github.com/wadearnold/ach/batchPPD.go:32.47,34.3 1 3 +github.com/wadearnold/ach/batchPPD.go:42.39,44.38 1 21 +github.com/wadearnold/ach/batchPPD.go:50.2,50.25 1 20 +github.com/wadearnold/ach/batchPPD.go:44.38,46.3 1 1 +github.com/wadearnold/ach/batcher.go:35.37,37.2 1 1 +github.com/wadearnold/ach/addenda05.go:36.32,41.2 4 45 +github.com/wadearnold/ach/addenda05.go:44.50,56.2 5 4 +github.com/wadearnold/ach/addenda05.go:59.45,66.2 1 7 +github.com/wadearnold/ach/addenda05.go:69.79,73.2 2 0 +github.com/wadearnold/ach/addenda05.go:77.46,78.51 1 51 +github.com/wadearnold/ach/addenda05.go:81.2,81.33 1 47 +github.com/wadearnold/ach/addenda05.go:85.2,85.65 1 46 +github.com/wadearnold/ach/addenda05.go:88.2,88.86 1 41 +github.com/wadearnold/ach/addenda05.go:92.2,92.12 1 40 +github.com/wadearnold/ach/addenda05.go:78.51,80.3 1 4 +github.com/wadearnold/ach/addenda05.go:81.33,84.3 2 1 +github.com/wadearnold/ach/addenda05.go:85.65,87.3 1 5 +github.com/wadearnold/ach/addenda05.go:88.86,90.3 1 1 +github.com/wadearnold/ach/addenda05.go:97.52,98.32 1 51 +github.com/wadearnold/ach/addenda05.go:101.2,101.30 1 50 +github.com/wadearnold/ach/addenda05.go:104.2,104.35 1 49 +github.com/wadearnold/ach/addenda05.go:107.2,107.46 1 48 +github.com/wadearnold/ach/addenda05.go:110.2,110.12 1 47 +github.com/wadearnold/ach/addenda05.go:98.32,100.3 1 1 +github.com/wadearnold/ach/addenda05.go:101.30,103.3 1 1 +github.com/wadearnold/ach/addenda05.go:104.35,106.3 1 1 +github.com/wadearnold/ach/addenda05.go:107.46,109.3 1 1 +github.com/wadearnold/ach/addenda05.go:114.69,116.2 1 8 +github.com/wadearnold/ach/addenda05.go:119.58,121.2 1 9 +github.com/wadearnold/ach/addenda05.go:124.69,126.2 1 44 +github.com/wadearnold/ach/addenda05.go:129.47,131.2 1 28 +github.com/wadearnold/ach/addenda98.go:43.13,46.2 1 1 +github.com/wadearnold/ach/addenda98.go:55.32,61.2 2 20 +github.com/wadearnold/ach/addenda98.go:64.50,79.2 7 3 +github.com/wadearnold/ach/addenda98.go:82.45,94.2 1 2 +github.com/wadearnold/ach/addenda98.go:97.46,98.33 1 26 +github.com/wadearnold/ach/addenda98.go:103.2,103.32 1 25 +github.com/wadearnold/ach/addenda98.go:108.2,109.9 2 22 +github.com/wadearnold/ach/addenda98.go:114.2,114.35 1 21 +github.com/wadearnold/ach/addenda98.go:118.2,118.12 1 20 +github.com/wadearnold/ach/addenda98.go:98.33,101.3 2 1 +github.com/wadearnold/ach/addenda98.go:103.32,105.3 1 3 +github.com/wadearnold/ach/addenda98.go:109.9,111.3 1 1 +github.com/wadearnold/ach/addenda98.go:114.35,116.3 1 1 +github.com/wadearnold/ach/addenda98.go:122.47,124.2 1 2 +github.com/wadearnold/ach/addenda98.go:127.57,129.2 1 3 +github.com/wadearnold/ach/addenda98.go:132.55,134.2 1 3 +github.com/wadearnold/ach/addenda98.go:137.57,139.2 1 3 +github.com/wadearnold/ach/addenda98.go:142.55,144.2 1 3 +github.com/wadearnold/ach/addenda98.go:146.50,163.29 3 1 +github.com/wadearnold/ach/addenda98.go:166.2,166.13 1 1 +github.com/wadearnold/ach/addenda98.go:163.29,165.3 1 11 +github.com/wadearnold/ach/batch.go:22.49,23.35 1 24 +github.com/wadearnold/ach/batch.go:36.2,37.71 2 1 +github.com/wadearnold/ach/batch.go:24.13,25.30 1 17 +github.com/wadearnold/ach/batch.go:26.13,27.30 1 3 +github.com/wadearnold/ach/batch.go:28.13,29.30 1 1 +github.com/wadearnold/ach/batch.go:30.13,31.30 1 1 +github.com/wadearnold/ach/batch.go:32.13,33.30 1 1 +github.com/wadearnold/ach/batch.go:34.10,34.10 0 1 +github.com/wadearnold/ach/batch.go:41.36,45.49 2 93 +github.com/wadearnold/ach/batch.go:53.2,53.69 1 87 +github.com/wadearnold/ach/batch.go:58.2,58.79 1 86 +github.com/wadearnold/ach/batch.go:63.2,63.73 1 85 +github.com/wadearnold/ach/batch.go:68.2,68.59 1 83 +github.com/wadearnold/ach/batch.go:73.2,73.50 1 82 +github.com/wadearnold/ach/batch.go:77.2,77.52 1 79 +github.com/wadearnold/ach/batch.go:81.2,81.46 1 76 +github.com/wadearnold/ach/batch.go:85.2,85.44 1 74 +github.com/wadearnold/ach/batch.go:89.2,89.48 1 73 +github.com/wadearnold/ach/batch.go:93.2,93.50 1 72 +github.com/wadearnold/ach/batch.go:97.2,97.50 1 71 +github.com/wadearnold/ach/batch.go:100.2,100.27 1 68 +github.com/wadearnold/ach/batch.go:45.49,47.37 1 6 +github.com/wadearnold/ach/batch.go:50.3,50.90 1 0 +github.com/wadearnold/ach/batch.go:47.37,49.4 1 6 +github.com/wadearnold/ach/batch.go:53.69,56.3 2 1 +github.com/wadearnold/ach/batch.go:58.79,61.3 2 1 +github.com/wadearnold/ach/batch.go:63.73,66.3 2 2 +github.com/wadearnold/ach/batch.go:68.59,71.3 2 1 +github.com/wadearnold/ach/batch.go:73.50,75.3 1 3 +github.com/wadearnold/ach/batch.go:77.52,79.3 1 3 +github.com/wadearnold/ach/batch.go:81.46,83.3 1 2 +github.com/wadearnold/ach/batch.go:85.44,87.3 1 1 +github.com/wadearnold/ach/batch.go:89.48,91.3 1 1 +github.com/wadearnold/ach/batch.go:93.50,95.3 1 1 +github.com/wadearnold/ach/batch.go:97.50,99.3 1 3 +github.com/wadearnold/ach/batch.go:105.35,107.48 1 83 +github.com/wadearnold/ach/batch.go:110.2,110.29 1 78 +github.com/wadearnold/ach/batch.go:114.2,116.38 3 77 +github.com/wadearnold/ach/batch.go:145.2,155.12 10 77 +github.com/wadearnold/ach/batch.go:107.48,109.3 1 5 +github.com/wadearnold/ach/batch.go:110.29,112.3 1 1 +github.com/wadearnold/ach/batch.go:116.38,119.17 3 94 +github.com/wadearnold/ach/batch.go:123.3,124.17 2 94 +github.com/wadearnold/ach/batch.go:129.3,129.48 1 94 +github.com/wadearnold/ach/batch.go:132.3,134.33 3 94 +github.com/wadearnold/ach/batch.go:119.17,121.4 1 0 +github.com/wadearnold/ach/batch.go:124.17,126.4 1 0 +github.com/wadearnold/ach/batch.go:129.48,131.4 1 1 +github.com/wadearnold/ach/batch.go:134.33,136.62 1 39 +github.com/wadearnold/ach/batch.go:140.4,140.16 1 39 +github.com/wadearnold/ach/batch.go:136.62,139.5 2 28 +github.com/wadearnold/ach/batch.go:159.57,161.2 1 98 +github.com/wadearnold/ach/batch.go:164.46,166.2 1 27 +github.com/wadearnold/ach/batch.go:169.60,171.2 1 73 +github.com/wadearnold/ach/batch.go:174.48,176.2 1 176 +github.com/wadearnold/ach/batch.go:179.49,181.2 1 140 +github.com/wadearnold/ach/batch.go:184.50,187.2 2 93 +github.com/wadearnold/ach/batch.go:190.39,192.2 1 22 +github.com/wadearnold/ach/batch.go:195.46,196.48 1 93 +github.com/wadearnold/ach/batch.go:199.2,199.38 1 89 +github.com/wadearnold/ach/batch.go:209.2,209.33 1 82 +github.com/wadearnold/ach/batch.go:196.48,198.3 1 4 +github.com/wadearnold/ach/batch.go:199.38,200.42 1 108 +github.com/wadearnold/ach/batch.go:203.3,203.42 1 106 +github.com/wadearnold/ach/batch.go:200.42,202.4 1 2 +github.com/wadearnold/ach/batch.go:203.42,204.45 1 53 +github.com/wadearnold/ach/batch.go:204.45,206.5 1 5 +github.com/wadearnold/ach/batch.go:215.47,217.38 2 82 +github.com/wadearnold/ach/batch.go:220.2,220.51 1 82 +github.com/wadearnold/ach/batch.go:224.2,224.12 1 79 +github.com/wadearnold/ach/batch.go:217.38,219.3 1 101 +github.com/wadearnold/ach/batch.go:220.51,223.3 2 3 +github.com/wadearnold/ach/batch.go:230.43,232.56 2 76 +github.com/wadearnold/ach/batch.go:237.2,237.58 1 75 +github.com/wadearnold/ach/batch.go:241.2,241.12 1 74 +github.com/wadearnold/ach/batch.go:232.56,235.3 2 1 +github.com/wadearnold/ach/batch.go:237.58,240.3 2 1 +github.com/wadearnold/ach/batch.go:244.69,245.38 1 153 +github.com/wadearnold/ach/batch.go:253.2,253.22 1 153 +github.com/wadearnold/ach/batch.go:245.38,246.158 1 176 +github.com/wadearnold/ach/batch.go:249.3,249.189 1 176 +github.com/wadearnold/ach/batch.go:246.158,248.4 1 117 +github.com/wadearnold/ach/batch.go:249.189,251.4 1 59 +github.com/wadearnold/ach/batch.go:258.49,260.38 2 79 +github.com/wadearnold/ach/batch.go:267.2,267.12 1 76 +github.com/wadearnold/ach/batch.go:260.38,261.35 1 88 +github.com/wadearnold/ach/batch.go:265.3,265.30 1 85 +github.com/wadearnold/ach/batch.go:261.35,264.4 2 3 +github.com/wadearnold/ach/batch.go:271.41,273.49 2 74 +github.com/wadearnold/ach/batch.go:277.2,277.12 1 73 +github.com/wadearnold/ach/batch.go:273.49,276.3 2 1 +github.com/wadearnold/ach/batch.go:282.49,284.38 2 151 +github.com/wadearnold/ach/batch.go:290.2,290.37 1 151 +github.com/wadearnold/ach/batch.go:284.38,289.3 2 172 +github.com/wadearnold/ach/batch.go:294.45,295.44 1 73 +github.com/wadearnold/ach/batch.go:303.2,303.12 1 72 +github.com/wadearnold/ach/batch.go:295.44,296.39 1 73 +github.com/wadearnold/ach/batch.go:296.39,297.66 1 77 +github.com/wadearnold/ach/batch.go:297.66,300.5 2 1 +github.com/wadearnold/ach/batch.go:308.47,309.38 1 72 +github.com/wadearnold/ach/batch.go:316.2,316.12 1 71 +github.com/wadearnold/ach/batch.go:309.38,310.77 1 76 +github.com/wadearnold/ach/batch.go:310.77,313.4 2 1 +github.com/wadearnold/ach/batch.go:320.47,321.38 1 71 +github.com/wadearnold/ach/batch.go:347.2,347.12 1 68 +github.com/wadearnold/ach/batch.go:321.38,322.30 1 75 +github.com/wadearnold/ach/batch.go:322.30,324.41 1 44 +github.com/wadearnold/ach/batch.go:327.4,329.43 2 43 +github.com/wadearnold/ach/batch.go:324.41,326.5 1 1 +github.com/wadearnold/ach/batch.go:329.43,331.42 1 47 +github.com/wadearnold/ach/batch.go:331.42,333.36 1 35 +github.com/wadearnold/ach/batch.go:337.6,339.79 2 34 +github.com/wadearnold/ach/batch.go:333.36,336.7 2 1 +github.com/wadearnold/ach/batch.go:339.79,342.7 2 1 +github.com/wadearnold/ach/batch.go:353.53,354.38 1 55 +github.com/wadearnold/ach/batch.go:361.2,361.12 1 50 +github.com/wadearnold/ach/batch.go:354.38,355.34 1 58 +github.com/wadearnold/ach/batch.go:355.34,358.4 2 5 +github.com/wadearnold/ach/batch.go:365.55,366.38 1 42 +github.com/wadearnold/ach/batch.go:374.2,374.12 1 37 +github.com/wadearnold/ach/batch.go:366.38,367.42 1 45 +github.com/wadearnold/ach/batch.go:367.42,368.38 1 24 +github.com/wadearnold/ach/batch.go:368.38,371.5 2 5 +github.com/wadearnold/ach/batch.go:378.40,380.28 2 68 +github.com/wadearnold/ach/batch.go:390.2,390.12 1 67 +github.com/wadearnold/ach/batch.go:380.28,381.43 1 2 +github.com/wadearnold/ach/batch.go:381.43,382.48 1 4 +github.com/wadearnold/ach/batch.go:385.4,385.45 1 4 +github.com/wadearnold/ach/batch.go:382.48,383.13 1 0 +github.com/wadearnold/ach/batch.go:385.45,387.5 1 1 +github.com/wadearnold/ach/batch.go:396.47,397.38 1 15 +github.com/wadearnold/ach/batch.go:404.2,404.12 1 15 +github.com/wadearnold/ach/batch.go:397.38,398.141 1 18 +github.com/wadearnold/ach/batch.go:398.141,402.4 2 0 +github.com/wadearnold/ach/converters.go:16.54,19.2 2 456 +github.com/wadearnold/ach/converters.go:21.60,24.2 2 301 +github.com/wadearnold/ach/converters.go:27.59,29.2 1 22 +github.com/wadearnold/ach/converters.go:32.58,35.2 2 32 +github.com/wadearnold/ach/converters.go:38.59,40.2 1 6 +github.com/wadearnold/ach/converters.go:43.58,46.2 2 11 +github.com/wadearnold/ach/converters.go:49.60,51.14 2 188 +github.com/wadearnold/ach/converters.go:54.2,55.10 2 184 +github.com/wadearnold/ach/converters.go:51.14,53.3 1 4 +github.com/wadearnold/ach/converters.go:59.59,62.14 3 774 +github.com/wadearnold/ach/converters.go:65.2,66.10 2 773 +github.com/wadearnold/ach/converters.go:62.14,64.3 1 1 +github.com/wadearnold/ach/converters.go:70.64,72.14 2 574 +github.com/wadearnold/ach/converters.go:75.2,76.10 2 513 +github.com/wadearnold/ach/converters.go:72.14,74.3 1 61 +github.com/wadearnold/ach/entryDetail.go:94.36,100.2 2 105 +github.com/wadearnold/ach/entryDetail.go:103.45,129.2 11 17 +github.com/wadearnold/ach/entryDetail.go:132.40,145.2 1 11 +github.com/wadearnold/ach/entryDetail.go:149.41,150.44 1 143 +github.com/wadearnold/ach/entryDetail.go:153.2,153.26 1 135 +github.com/wadearnold/ach/entryDetail.go:157.2,157.65 1 134 +github.com/wadearnold/ach/entryDetail.go:160.2,160.63 1 133 +github.com/wadearnold/ach/entryDetail.go:163.2,163.67 1 132 +github.com/wadearnold/ach/entryDetail.go:166.2,166.61 1 131 +github.com/wadearnold/ach/entryDetail.go:169.2,169.64 1 130 +github.com/wadearnold/ach/entryDetail.go:173.2,176.16 3 129 +github.com/wadearnold/ach/entryDetail.go:180.2,180.32 1 129 +github.com/wadearnold/ach/entryDetail.go:184.2,184.12 1 128 +github.com/wadearnold/ach/entryDetail.go:150.44,152.3 1 8 +github.com/wadearnold/ach/entryDetail.go:153.26,156.3 2 1 +github.com/wadearnold/ach/entryDetail.go:157.65,159.3 1 1 +github.com/wadearnold/ach/entryDetail.go:160.63,162.3 1 1 +github.com/wadearnold/ach/entryDetail.go:163.67,165.3 1 1 +github.com/wadearnold/ach/entryDetail.go:166.61,168.3 1 1 +github.com/wadearnold/ach/entryDetail.go:169.64,171.3 1 1 +github.com/wadearnold/ach/entryDetail.go:176.16,178.3 1 0 +github.com/wadearnold/ach/entryDetail.go:180.32,183.3 2 1 +github.com/wadearnold/ach/entryDetail.go:189.47,190.25 1 143 +github.com/wadearnold/ach/entryDetail.go:193.2,193.29 1 142 +github.com/wadearnold/ach/entryDetail.go:196.2,196.33 1 141 +github.com/wadearnold/ach/entryDetail.go:199.2,199.31 1 140 +github.com/wadearnold/ach/entryDetail.go:202.2,202.29 1 139 +github.com/wadearnold/ach/entryDetail.go:205.2,205.25 1 136 +github.com/wadearnold/ach/entryDetail.go:208.2,208.12 1 135 +github.com/wadearnold/ach/entryDetail.go:190.25,192.3 1 1 +github.com/wadearnold/ach/entryDetail.go:193.29,195.3 1 1 +github.com/wadearnold/ach/entryDetail.go:196.33,198.3 1 1 +github.com/wadearnold/ach/entryDetail.go:199.31,201.3 1 1 +github.com/wadearnold/ach/entryDetail.go:202.29,204.3 1 3 +github.com/wadearnold/ach/entryDetail.go:205.25,207.3 1 1 +github.com/wadearnold/ach/entryDetail.go:212.68,215.24 2 49 +github.com/wadearnold/ach/entryDetail.go:216.18,220.21 4 8 +github.com/wadearnold/ach/entryDetail.go:221.18,225.21 4 9 +github.com/wadearnold/ach/entryDetail.go:227.10,230.21 3 32 +github.com/wadearnold/ach/entryDetail.go:235.58,240.2 4 105 +github.com/wadearnold/ach/entryDetail.go:243.75,246.2 2 106 +github.com/wadearnold/ach/entryDetail.go:249.57,251.2 1 142 +github.com/wadearnold/ach/entryDetail.go:254.55,256.2 1 12 +github.com/wadearnold/ach/entryDetail.go:259.45,261.2 1 12 +github.com/wadearnold/ach/entryDetail.go:264.59,266.2 1 11 +github.com/wadearnold/ach/entryDetail.go:269.53,271.2 1 11 +github.com/wadearnold/ach/entryDetail.go:274.55,276.2 1 0 +github.com/wadearnold/ach/entryDetail.go:279.54,281.2 1 15 +github.com/wadearnold/ach/entryDetail.go:284.56,286.2 1 11 +github.com/wadearnold/ach/entryDetail.go:289.50,293.2 2 18 +github.com/wadearnold/ach/entryDetail.go:296.49,298.14 2 30 +github.com/wadearnold/ach/entryDetail.go:298.14,300.3 1 0 +github.com/wadearnold/ach/entryDetail.go:300.8,302.3 1 30 +github.com/wadearnold/ach/entryDetail.go:306.50,308.2 1 249 +github.com/wadearnold/ach/entryDetail.go:311.47,314.17 2 9 +github.com/wadearnold/ach/entryDetail.go:320.2,320.11 1 0 +github.com/wadearnold/ach/entryDetail.go:315.21,316.13 1 2 +github.com/wadearnold/ach/entryDetail.go:317.21,318.13 1 7 +github.com/wadearnold/ach/file.go:50.36,52.2 1 1 +github.com/wadearnold/ach/file.go:69.22,74.2 1 13 +github.com/wadearnold/ach/file.go:77.31,79.44 1 14 +github.com/wadearnold/ach/file.go:83.2,83.25 1 13 +github.com/wadearnold/ach/file.go:87.2,93.34 7 12 +github.com/wadearnold/ach/file.go:109.2,112.36 3 12 +github.com/wadearnold/ach/file.go:117.2,123.12 6 12 +github.com/wadearnold/ach/file.go:79.44,81.3 1 1 +github.com/wadearnold/ach/file.go:83.25,85.3 1 1 +github.com/wadearnold/ach/file.go:93.34,107.3 8 15 +github.com/wadearnold/ach/file.go:112.36,114.3 1 11 +github.com/wadearnold/ach/file.go:114.8,116.3 1 1 +github.com/wadearnold/ach/file.go:127.50,128.22 1 21 +github.com/wadearnold/ach/file.go:132.2,132.40 1 21 +github.com/wadearnold/ach/file.go:135.2,136.18 2 21 +github.com/wadearnold/ach/file.go:129.17,130.77 1 1 +github.com/wadearnold/ach/file.go:132.40,134.3 1 1 +github.com/wadearnold/ach/file.go:140.46,143.2 2 13 +github.com/wadearnold/ach/file.go:146.33,148.44 1 11 +github.com/wadearnold/ach/file.go:153.2,153.48 1 10 +github.com/wadearnold/ach/file.go:157.2,157.41 1 9 +github.com/wadearnold/ach/file.go:161.2,161.24 1 7 +github.com/wadearnold/ach/file.go:148.44,151.3 2 1 +github.com/wadearnold/ach/file.go:153.48,155.3 1 1 +github.com/wadearnold/ach/file.go:157.41,159.3 1 2 +github.com/wadearnold/ach/file.go:166.44,169.34 2 10 +github.com/wadearnold/ach/file.go:172.2,172.42 1 10 +github.com/wadearnold/ach/file.go:176.2,176.12 1 9 +github.com/wadearnold/ach/file.go:169.34,171.3 1 14 +github.com/wadearnold/ach/file.go:172.42,175.3 2 1 +github.com/wadearnold/ach/file.go:181.37,184.34 3 9 +github.com/wadearnold/ach/file.go:188.2,188.58 1 9 +github.com/wadearnold/ach/file.go:192.2,192.60 1 8 +github.com/wadearnold/ach/file.go:196.2,196.12 1 7 +github.com/wadearnold/ach/file.go:184.34,187.3 2 13 +github.com/wadearnold/ach/file.go:188.58,191.3 2 1 +github.com/wadearnold/ach/file.go:192.60,195.3 2 1 +github.com/wadearnold/ach/file.go:200.36,202.45 2 7 +github.com/wadearnold/ach/file.go:206.2,206.12 1 6 +github.com/wadearnold/ach/file.go:202.45,205.3 2 1 +github.com/wadearnold/ach/file.go:211.44,213.34 2 7 +github.com/wadearnold/ach/file.go:216.2,216.33 1 7 +github.com/wadearnold/ach/file.go:213.34,215.3 1 11 +github.com/wadearnold/ach/validators.go:34.37,36.2 1 2 +github.com/wadearnold/ach/validators.go:53.52,54.14 1 294 +github.com/wadearnold/ach/validators.go:66.2,66.36 1 4 +github.com/wadearnold/ach/validators.go:63.7,64.13 1 290 +github.com/wadearnold/ach/validators.go:70.50,71.14 1 194 +github.com/wadearnold/ach/validators.go:77.2,77.31 1 2 +github.com/wadearnold/ach/validators.go:74.86,75.13 1 192 +github.com/wadearnold/ach/validators.go:83.51,84.14 1 46 +github.com/wadearnold/ach/validators.go:100.2,100.39 1 5 +github.com/wadearnold/ach/validators.go:97.8,98.13 1 41 +github.com/wadearnold/ach/validators.go:121.55,122.14 1 134 +github.com/wadearnold/ach/validators.go:229.2,229.39 1 1 +github.com/wadearnold/ach/validators.go:226.6,227.13 1 133 +github.com/wadearnold/ach/validators.go:233.60,234.14 1 192 +github.com/wadearnold/ach/validators.go:244.2,244.38 1 1 +github.com/wadearnold/ach/validators.go:241.5,242.13 1 191 +github.com/wadearnold/ach/validators.go:248.57,249.43 1 35 +github.com/wadearnold/ach/validators.go:252.2,252.12 1 33 +github.com/wadearnold/ach/validators.go:249.43,251.3 1 2 +github.com/wadearnold/ach/validators.go:256.52,257.38 1 1595 +github.com/wadearnold/ach/validators.go:261.2,261.12 1 1580 +github.com/wadearnold/ach/validators.go:257.38,260.3 1 15 +github.com/wadearnold/ach/validators.go:271.67,273.25 2 129 +github.com/wadearnold/ach/validators.go:276.2,293.31 17 129 +github.com/wadearnold/ach/validators.go:273.25,275.3 1 1032 +github.com/wadearnold/ach/validators.go:297.42,299.2 1 129 +github.com/wadearnold/ach/writer.go:24.37,28.2 1 2 +github.com/wadearnold/ach/writer.go:31.42,32.40 1 2 +github.com/wadearnold/ach/writer.go:36.2,38.72 2 2 +github.com/wadearnold/ach/writer.go:41.2,43.37 2 2 +github.com/wadearnold/ach/writer.go:65.2,65.73 1 2 +github.com/wadearnold/ach/writer.go:68.2,71.64 2 2 +github.com/wadearnold/ach/writer.go:77.2,77.12 1 2 +github.com/wadearnold/ach/writer.go:32.40,34.3 1 0 +github.com/wadearnold/ach/writer.go:38.72,40.3 1 0 +github.com/wadearnold/ach/writer.go:43.37,44.79 1 3 +github.com/wadearnold/ach/writer.go:47.3,48.44 2 3 +github.com/wadearnold/ach/writer.go:60.3,60.80 1 3 +github.com/wadearnold/ach/writer.go:63.3,63.14 1 3 +github.com/wadearnold/ach/writer.go:44.79,46.4 1 0 +github.com/wadearnold/ach/writer.go:48.44,49.68 1 2 +github.com/wadearnold/ach/writer.go:52.4,53.43 2 2 +github.com/wadearnold/ach/writer.go:49.68,51.5 1 0 +github.com/wadearnold/ach/writer.go:53.43,54.71 1 2 +github.com/wadearnold/ach/writer.go:57.5,57.16 1 2 +github.com/wadearnold/ach/writer.go:54.71,56.6 1 0 +github.com/wadearnold/ach/writer.go:60.80,62.4 1 0 +github.com/wadearnold/ach/writer.go:65.73,67.3 1 0 +github.com/wadearnold/ach/writer.go:71.64,72.76 1 6 +github.com/wadearnold/ach/writer.go:72.76,74.4 1 0 +github.com/wadearnold/ach/writer.go:82.26,84.2 1 1 +github.com/wadearnold/ach/writer.go:87.32,90.2 2 0 +github.com/wadearnold/ach/writer.go:93.48,94.29 1 2 +github.com/wadearnold/ach/writer.go:102.2,102.20 1 2 +github.com/wadearnold/ach/writer.go:94.29,98.17 2 2 +github.com/wadearnold/ach/writer.go:98.17,100.4 1 0 +github.com/wadearnold/ach/addenda99.go:24.13,27.2 1 1 +github.com/wadearnold/ach/addenda99.go:65.32,71.2 2 17 +github.com/wadearnold/ach/addenda99.go:74.50,91.2 8 3 +github.com/wadearnold/ach/addenda99.go:94.45,105.2 1 2 +github.com/wadearnold/ach/addenda99.go:108.46,110.33 1 5 +github.com/wadearnold/ach/addenda99.go:116.2,117.9 2 5 +github.com/wadearnold/ach/addenda99.go:121.2,121.12 1 4 +github.com/wadearnold/ach/addenda99.go:110.33,113.3 2 0 +github.com/wadearnold/ach/addenda99.go:117.9,120.3 1 1 +github.com/wadearnold/ach/addenda99.go:125.47,127.2 1 4 +github.com/wadearnold/ach/addenda99.go:130.57,132.2 1 3 +github.com/wadearnold/ach/addenda99.go:135.55,137.36 1 4 +github.com/wadearnold/ach/addenda99.go:141.2,141.58 1 1 +github.com/wadearnold/ach/addenda99.go:137.36,139.3 1 3 +github.com/wadearnold/ach/addenda99.go:145.55,147.2 1 3 +github.com/wadearnold/ach/addenda99.go:150.62,152.2 1 3 +github.com/wadearnold/ach/addenda99.go:155.55,157.2 1 3 +github.com/wadearnold/ach/addenda99.go:159.50,202.29 3 2 +github.com/wadearnold/ach/addenda99.go:205.2,205.13 1 2 +github.com/wadearnold/ach/addenda99.go:202.29,204.3 1 70 +github.com/wadearnold/ach/batchControl.go:72.46,96.2 11 10 +github.com/wadearnold/ach/batchControl.go:99.38,106.2 1 162 +github.com/wadearnold/ach/batchControl.go:109.41,123.2 1 8 +github.com/wadearnold/ach/batchControl.go:127.42,128.44 1 101 +github.com/wadearnold/ach/batchControl.go:131.2,131.26 1 98 +github.com/wadearnold/ach/batchControl.go:135.2,135.63 1 97 +github.com/wadearnold/ach/batchControl.go:139.2,139.68 1 96 +github.com/wadearnold/ach/batchControl.go:143.2,143.72 1 94 +github.com/wadearnold/ach/batchControl.go:147.2,147.12 1 93 +github.com/wadearnold/ach/batchControl.go:128.44,130.3 1 3 +github.com/wadearnold/ach/batchControl.go:131.26,134.3 2 1 +github.com/wadearnold/ach/batchControl.go:135.63,137.3 1 1 +github.com/wadearnold/ach/batchControl.go:139.68,141.3 1 2 +github.com/wadearnold/ach/batchControl.go:143.72,145.3 1 1 +github.com/wadearnold/ach/batchControl.go:152.48,153.25 1 101 +github.com/wadearnold/ach/batchControl.go:156.2,156.30 1 100 +github.com/wadearnold/ach/batchControl.go:159.2,159.42 1 99 +github.com/wadearnold/ach/batchControl.go:162.2,162.12 1 98 +github.com/wadearnold/ach/batchControl.go:153.25,155.3 1 1 +github.com/wadearnold/ach/batchControl.go:156.30,158.3 1 1 +github.com/wadearnold/ach/batchControl.go:159.42,161.3 1 1 +github.com/wadearnold/ach/batchControl.go:166.57,168.2 1 9 +github.com/wadearnold/ach/batchControl.go:171.49,173.2 1 84 +github.com/wadearnold/ach/batchControl.go:176.67,178.2 1 9 +github.com/wadearnold/ach/batchControl.go:181.68,183.2 1 9 +github.com/wadearnold/ach/batchControl.go:186.61,188.2 1 9 +github.com/wadearnold/ach/batchControl.go:191.65,193.2 1 9 +github.com/wadearnold/ach/batchControl.go:196.58,198.2 1 10 +github.com/wadearnold/ach/batchControl.go:201.51,203.2 1 9 +github.com/wadearnold/ach/batchTEL.go:13.45,18.2 4 6 +github.com/wadearnold/ach/batchTEL.go:21.41,23.39 1 11 +github.com/wadearnold/ach/batchTEL.go:28.2,28.48 1 10 +github.com/wadearnold/ach/batchTEL.go:33.2,33.50 1 8 +github.com/wadearnold/ach/batchTEL.go:38.2,38.38 1 7 +github.com/wadearnold/ach/batchTEL.go:45.2,45.34 1 6 +github.com/wadearnold/ach/batchTEL.go:23.39,25.3 1 1 +github.com/wadearnold/ach/batchTEL.go:28.48,30.3 1 2 +github.com/wadearnold/ach/batchTEL.go:33.50,36.3 2 1 +github.com/wadearnold/ach/batchTEL.go:38.38,39.35 1 7 +github.com/wadearnold/ach/batchTEL.go:39.35,42.4 2 1 +github.com/wadearnold/ach/batchTEL.go:49.39,51.38 1 8 +github.com/wadearnold/ach/batchTEL.go:55.2,55.25 1 7 +github.com/wadearnold/ach/batchTEL.go:51.38,53.3 1 1 +github.com/wadearnold/ach/fileControl.go:42.45,60.2 8 7 +github.com/wadearnold/ach/fileControl.go:63.35,68.2 1 35 +github.com/wadearnold/ach/fileControl.go:71.40,82.2 1 4 +github.com/wadearnold/ach/fileControl.go:86.41,87.44 1 14 +github.com/wadearnold/ach/fileControl.go:90.2,90.26 1 8 +github.com/wadearnold/ach/fileControl.go:94.2,94.12 1 7 +github.com/wadearnold/ach/fileControl.go:87.44,89.3 1 6 +github.com/wadearnold/ach/fileControl.go:90.26,93.3 2 1 +github.com/wadearnold/ach/fileControl.go:99.47,100.25 1 14 +github.com/wadearnold/ach/fileControl.go:103.2,103.24 1 13 +github.com/wadearnold/ach/fileControl.go:106.2,106.24 1 11 +github.com/wadearnold/ach/fileControl.go:109.2,109.31 1 10 +github.com/wadearnold/ach/fileControl.go:112.2,112.23 1 9 +github.com/wadearnold/ach/fileControl.go:115.2,115.12 1 8 +github.com/wadearnold/ach/fileControl.go:100.25,102.3 1 1 +github.com/wadearnold/ach/fileControl.go:103.24,105.3 1 2 +github.com/wadearnold/ach/fileControl.go:106.24,108.3 1 1 +github.com/wadearnold/ach/fileControl.go:109.31,111.3 1 1 +github.com/wadearnold/ach/fileControl.go:112.23,114.3 1 1 +github.com/wadearnold/ach/fileControl.go:119.49,121.2 1 7 +github.com/wadearnold/ach/fileControl.go:124.49,126.2 1 6 +github.com/wadearnold/ach/fileControl.go:129.56,131.2 1 8 +github.com/wadearnold/ach/fileControl.go:134.48,136.2 1 14 +github.com/wadearnold/ach/fileControl.go:139.72,141.2 1 6 +github.com/wadearnold/ach/fileControl.go:144.73,146.2 1 6 From 70daabe65a1be612ed63b6de6c0248c1e8c15a35 Mon Sep 17 00:00:00 2001 From: fossabot Date: Mon, 14 May 2018 10:32:59 -0700 Subject: [PATCH 0089/1694] Add license scan report and status Signed-off-by: fossabot --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 36375ab72..d0c8c0b4c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ moov-io/ach [![Coverage Status](https://coveralls.io/repos/github/moov-io/ach/badge.svg?branch=master)](https://coveralls.io/github/moov-io/ach?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/moov-io/ach)](https://goreportcard.com/report/github.com/moov-io/ach) [![Apache 2 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/moov-io/ach/master/LICENSE) +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoov-io%2Fach.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoov-io%2Fach?ref=badge_shield) Package 'moov-io/ach' implements a file reader and writer for parsing [ACH](https://en.wikipedia.org/wiki/Automated_Clearing_House @@ -367,3 +368,6 @@ Pull request require a batchMTE_test.go file that covers the logic of the type. ## License Apache License 2.0 See [LICENSE](LICENSE) for details. + + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoov-io%2Fach.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoov-io%2Fach?ref=badge_large) \ No newline at end of file From 606109e512d4431cfe4faf46fecc7bc9a0b21618 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 14 May 2018 12:07:39 -0600 Subject: [PATCH 0090/1694] Create Contributors --- CONTRIBUTING.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..5eedb3200 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,121 @@ +# Contributing + +Wow, we really appreciate that you even looked at this section! We are trying to make the worlds best atomic building blocks for financial services that accelerate innovation in banking and we need your help! + +You only have a fresh set of eyes once! The easiest way to contribute is to give feedback on the documentation that you are reading right now. This can be as simple as sending a message to our Google Group with your feedback or updating the markdown in this documentation and issuing a pull request. + +Stability is the hallmark of any good software. If you find an edge case that isn't handled please open an GitHub issue with the example data so that we can make our software more robust for everyone. We also welcome pull requests if you want to get your hands dirty. + +Have a use case that we don't handle; or handle well! Start the discussion on our Google Group or open a GitHub Issue. We want to make the project meet the needs of the community and keeps you using our code. + +Please review our [Code of Conduct](CODE_OF_CONDUCT.md) to ensure you agree with the values of this project. + +We use GitHub to manage reviews of pull requests. + +* If you have a trivial fix or improvement, go ahead and create a pull + request, addressing (with `@...`) one or more of the maintainers + (see [AUTHORS.md](AUTHORS.md)) in the description of the pull request. + +* If you plan to do something more involved, first propose your ideas + in a Github issue. This will avoid unnecessary work and surely give + you and us a good deal of inspiration. + +* Relevant coding style guidelines are the [Go Code Review + Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) + and the _Formatting and style_ section of Peter Bourgon's [Go: Best + Practices for Production + Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). + +## Pull Requests + +A good quality PR will have the following characteristics: + +* It will be a complete piece of work that adds value in some way. +* It will have a title that reflects the work within, and a summary that helps to understand the context of the change. +* There will be well written commit messages, with well crafted commits that tell the story of the development of this work. +* Ideally it will be small and easy to understand. Single commit PRs are usually easy to submit, review, and merge. +* The code contained within will meet the best practises set by the team wherever possible. +* A PR does not end at submission though. A code change is not made until it is merged and used in production. + +A good PR should be able to flow through a peer review system easily and quickly. + +Our Build pipeline utilizes [Travis-CI](https://travis-ci.org/moov-io/ach) to enforce many tools that you should add to your editor before issuing a pull request. Learn more about these tools on our [Go Report card](https://goreportcard.com/report/github.com/moov-io/ach) + + +## Additional SEC (Standard Entry Class) code batch types. + +SEC type's in the Batch Header record define the payment type of the following Entry Details and Addenda. The format of the records in the batch is the same between all payment types but NACHA defines different rules for the values that are held in each record field. To add support for an additional SEC type you will need to implement NACHA rules for that type. The vast majority of rules are implemented in ach.batch and then composed into Batch(SEC) for reuse. All Batch(SEC) types must be a ach.Batcher. + +1. Create a milestone for the new SEC type that you want supported. +2. Add issues to that milestone to meet the NACHA rules for the batch type. +3. Create a new struct of the batch type. In the following example we will use MTE(Machine Transfer Entry) as our example. +4. The following code would be place in a new file batchMTE.go next to the existing batch types. +5. The code is stub code and the MTE type is not implemented. For concrete examples review the existing batch types in the source. + +Create a new struct and compose ach.batch + +```go +type BatchMTE struct { + batch +} +``` +Add the ability for the new type to be created. + +```go +func NewBatchMTE(bh *BatchHeader) *BatchMTE { + batch := new(BatchMTE) + batch.setControl(NewBatchControl) + batch.SetHeader(bh) + return batch +} +``` + +To support the Batcher interface you must add the following functions that are not implemented in ach.batch. +* Validate() error +* Create() error + +Validate is designed to enforce the NACHA rules for the MTE payment type. Validate is run after a batch of this type is read from a file. If you are creating a batch from code call validate afterwards. + +```go +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchMTE) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + // ... batch.isAddendaCount(1) + // Add type specific validation. + // ... + return nil +} +``` +Create takes the Batch Header and Entry details and creates the proper sequence number and batch control. If additional logic specific to the SEC type is required it building a batch file it should be added here. + +```go +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchMTE) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + if err := batch.Validate(); err != nil { + return err + } + return nil +} +``` + +Finally add the batch type to the NewBatch factory in batch.go. + +```go +//... +case "MTE": + return NewBatchMTE(bh), nil +//... +``` + +Pull request require a batchMTE_test.go file that covers the logic of the type. From 7e3fb32016c4d66cf58b069134fd7d8523815062 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 14 May 2018 12:12:19 -0600 Subject: [PATCH 0091/1694] Update README --- README.md | 102 +++++------------------------------------------------- 1 file changed, 8 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index d0c8c0b4c..d1a31b621 100644 --- a/README.md +++ b/README.md @@ -250,102 +250,16 @@ Which will generate a well formed ACH flat file. ``` # Mailing lists -Users trade notes on the Google group moov-users (send mail to moov-users@googlegroups.com). You must join the [moov-users](https://groups.google.com/forum/#!forum/moov-users)forumn in order to post. +## Getting help -# Contributing + channel | info + ------- | ------- + Google Group [moov-users](https://groups.google.com/forum/#!forum/moov-users)| The Moov users Google group is for contributors other people contributing to the Moov project. You can join them without a google account by sending an email to [moov-users+subscribe@googlegroups.com](mailto:moov-users+subscribe@googlegroups.com). After receiving the join-request message, you can simply reply to that to confirm the subscription. +Twitter [@moov_io](https://twitter.com/moov_io) | You can follow Moov.IO's Twitter feed to get updates on our project(s). You can also tweet us questions or just share blogs or stories. +[GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. +[moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. -We use GitHub to manage reviews of pull requests. -* If you have a trivial fix or improvement, go ahead and create a pull - request, addressing (with `@...`) one or more of the maintainers - (see [AUTHORS.md](AUTHORS.md)) in the description of the pull request. - -* If you plan to do something more involved, first propose your ideas - in a Github issue. This will avoid unnecessary work and surely give - you and us a good deal of inspiration. - -* Relevant coding style guidelines are the [Go Code Review - Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) - and the _Formatting and style_ section of Peter Bourgon's [Go: Best - Practices for Production - Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). - -# Additional SEC (Standard Entry Class) code batch types. -SEC type's in the Batch Header record define the payment type of the following Entry Details and Addenda. The format of the records in the batch is the same between all payment types but NACHA defines different rules for the values that are held in each record field. To add support for an additional SEC type you will need to implement NACHA rules for that type. The vast majority of rules are implemented in ach.batch and then composed into Batch(SEC) for reuse. All Batch(SEC) types must be a ach.Batcher. - -1. Create a milestone for the new SEC type that you want supported. -2. Add issues to that milestone to meet the NACHA rules for the batch type. -3. Create a new struct of the batch type. In the following example we will use MTE(Machine Transfer Entry) as our example. -4. The following code would be place in a new file batchMTE.go next to the existing batch types. -5. The code is stub code and the MTE type is not implemented. For concrete examples review the existing batch types in the source. - -Create a new struct and compose ach.batch - -```go -type BatchMTE struct { - batch -} -``` -Add the ability for the new type to be created. - -```go -func NewBatchMTE(bh *BatchHeader) *BatchMTE { - batch := new(BatchMTE) - batch.setControl(NewBatchControl) - batch.SetHeader(bh) - return batch -} -``` - -To support the Batcher interface you must add the following functions that are not implemented in ach.batch. -* Validate() error -* Create() error - -Validate is designed to enforce the NACHA rules for the MTE payment type. Validate is run after a batch of this type is read from a file. If you are creating a batch from code call validate afterwards. - -```go -// Validate checks valid NACHA batch rules. Assumes properly parsed records. -func (batch *BatchMTE) Validate() error { - // basic verification of the batch before we validate specific rules. - if err := batch.verify(); err != nil { - return err - } - // Add configuration based validation for this type. - // ... batch.isAddendaCount(1) - // Add type specific validation. - // ... - return nil -} -``` -Create takes the Batch Header and Entry details and creates the proper sequence number and batch control. If additional logic specific to the SEC type is required it building a batch file it should be added here. - -```go -// Create takes Batch Header and Entries and builds a valid batch -func (batch *BatchMTE) Create() error { - // generates sequence numbers and batch control - if err := batch.build(); err != nil { - return err - } - // Additional steps specific to batch type - // ... - - if err := batch.Validate(); err != nil { - return err - } - return nil -} -``` - -Finally add the batch type to the NewBatch factory in batch.go. - -```go -//... -case "MTE": - return NewBatchMTE(bh), nil -//... -``` - -Pull request require a batchMTE_test.go file that covers the logic of the type. ## References * [Wikipeda: Automated Clearing House](http://en.wikipedia.org/wiki/Automated_Clearing_House) @@ -370,4 +284,4 @@ Pull request require a batchMTE_test.go file that covers the logic of the type. Apache License 2.0 See [LICENSE](LICENSE) for details. -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoov-io%2Fach.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoov-io%2Fach?ref=badge_large) \ No newline at end of file +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoov-io%2Fach.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoov-io%2Fach?ref=badge_large) From 7f9aa00b2597eea8b31493b699dcc2ad928711ff Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 14 May 2018 12:18:35 -0600 Subject: [PATCH 0092/1694] Adding Contributing --- README.md | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index d1a31b621..2b22f126e 100644 --- a/README.md +++ b/README.md @@ -248,9 +248,7 @@ Which will generate a well formed ACH flat file. 82200000020010200101000000000000000000000799123456789 234567890000002 9000002000001000000040020400202000000017500000000000799 ``` -# Mailing lists - -## Getting help +# Getting help channel | info ------- | ------- @@ -259,26 +257,9 @@ Twitter [@moov_io](https://twitter.com/moov_io) | You can follow Moov.IO's Twitt [GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. [moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. +# Contributing - -## References -* [Wikipeda: Automated Clearing House](http://en.wikipedia.org/wiki/Automated_Clearing_House) -* [Nacha ACH Network: How it Works](https://www.nacha.org/ach-network) -* [Federal ACH Directory](https://www.frbservices.org/EPaymentsDirectory/search.html) - -## Format Specification -* [NACHA ACH File Formatting](https://www.nacha.org/system/files/resources/AAP201%20-%20ACH%20File%20Formatting.pdf) -* [PNC ACH File Specification](http://content.pncmc.com/live/pnc/corporate/treasury-management/ach-conversion/ACH-File-Specifications.pdf) -* [Thomson Reuters ACH FIle Structure](http://cs.thomsonreuters.com/ua/acct_pr/acs/cs_us_en/pr/dd/ach_file_structure_and_content.htm) -* [Gusto: How ACH Works: A developer perspective](http://engineering.gusto.com/how-ach-works-a-developer-perspective-part-4/) - -![ACH File Layout](https://github.com/moov-io/ach/blob/master/documentation/ach_file_structure_shg.gif) - -## Inspiration -* [ACH:Builder - Tools for Building ACH](http://search.cpan.org/~tkeefer/ACH-Builder-0.03/lib/ACH/Builder.pm) -* [mosscode / ach](https://github.com/mosscode/ach) -* [Helper for building ACH files in Ruby](https://github.com/jm81/ach) -* [Glenselle / nACH2](https://github.com/glenselle/nACH2) +Yes please! Please review our [Contributing guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) to get started! ## License Apache License 2.0 See [LICENSE](LICENSE) for details. From d9f8956398c348eaa233ddf4529b7b1888d86298 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 14 May 2018 12:19:28 -0600 Subject: [PATCH 0093/1694] References, format specification, inspiration --- CONTRIBUTING.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5eedb3200..c1e368f1d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,3 +119,22 @@ case "MTE": ``` Pull request require a batchMTE_test.go file that covers the logic of the type. + +## References +* [Wikipeda: Automated Clearing House](http://en.wikipedia.org/wiki/Automated_Clearing_House) +* [Nacha ACH Network: How it Works](https://www.nacha.org/ach-network) +* [Federal ACH Directory](https://www.frbservices.org/EPaymentsDirectory/search.html) + +## Format Specification +* [NACHA ACH File Formatting](https://www.nacha.org/system/files/resources/AAP201%20-%20ACH%20File%20Formatting.pdf) +* [PNC ACH File Specification](http://content.pncmc.com/live/pnc/corporate/treasury-management/ach-conversion/ACH-File-Specifications.pdf) +* [Thomson Reuters ACH FIle Structure](http://cs.thomsonreuters.com/ua/acct_pr/acs/cs_us_en/pr/dd/ach_file_structure_and_content.htm) +* [Gusto: How ACH Works: A developer perspective](http://engineering.gusto.com/how-ach-works-a-developer-perspective-part-4/) + +![ACH File Layout](https://github.com/moov-io/ach/blob/master/documentation/ach_file_structure_shg.gif) + +## Inspiration +* [ACH:Builder - Tools for Building ACH](http://search.cpan.org/~tkeefer/ACH-Builder-0.03/lib/ACH/Builder.pm) +* [mosscode / ach](https://github.com/mosscode/ach) +* [Helper for building ACH files in Ruby](https://github.com/jm81/ach) +* [Glenselle / nACH2](https://github.com/glenselle/nACH2) From b7590f5297c957c3281b81e8113c393dcf9e4de6 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 14 May 2018 12:24:54 -0600 Subject: [PATCH 0094/1694] Fix misspelling of practices --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1e368f1d..ccd4d07bb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,7 +34,7 @@ A good quality PR will have the following characteristics: * It will have a title that reflects the work within, and a summary that helps to understand the context of the change. * There will be well written commit messages, with well crafted commits that tell the story of the development of this work. * Ideally it will be small and easy to understand. Single commit PRs are usually easy to submit, review, and merge. -* The code contained within will meet the best practises set by the team wherever possible. +* The code contained within will meet the best practices set by the team wherever possible. * A PR does not end at submission though. A code change is not made until it is merged and used in production. A good PR should be able to flow through a peer review system easily and quickly. From 2f981088c2a052269328dd9077620720fddce264 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 14 May 2018 14:41:08 -0600 Subject: [PATCH 0095/1694] GoLint comments added to functions --- addenda05.go | 6 +----- entryDetail.go | 7 +++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/addenda05.go b/addenda05.go index 9ff3191d8..bbc9b69db 100644 --- a/addenda05.go +++ b/addenda05.go @@ -7,8 +7,6 @@ import ( // Addenda05 is a Addendumer addenda which provides business transaction information for Addenda Type // Code 05 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. -// Future development to allow for use case specific 05 addenda records. - type Addenda05 struct { // RecordType defines the type of record in the block. entryAddenda05 Pos 7 recordType string @@ -32,7 +30,6 @@ type Addenda05 struct { } // NewAddenda05 returns a new Addenda05 with default values for none exported fields - func NewAddenda05() *Addenda05 { addenda05 := new(Addenda05) addenda05.recordType = "7" @@ -65,10 +62,9 @@ func (addenda05 *Addenda05) String() string { addenda05.EntryDetailSequenceNumberField()) } -// SetPaymentRealtedInformation +// SetPaymentRealtedInformation allows additional information about the transaction func (addenda05 *Addenda05) SetPaymentRelatedInformation(s string) *Addenda05 { addenda05.PaymentRelatedInformation = s - return addenda05 } diff --git a/entryDetail.go b/entryDetail.go index 0fad56f29..c9a773e39 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -85,9 +85,12 @@ type EntryDetail struct { } const ( + // CategoryForward defines the entry as being sent to the receiving institution CategoryForward = "Forward" - CategoryReturn = "Return" - CategoryNOC = "NOC" + // CategoryReturn defines the entry as being a return of a forward entry back to the originating institution + CategoryReturn = "Return" + // CategoryNOC defines the entry as being a notification of change of a forward entry to the originating institution + CategoryNOC = "NOC" ) // NewEntryDetail returns a new EntryDetail with default values for non exported fields From 090418ac5d446110bed7ae92f0a9564020448db0 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 15 May 2018 08:58:03 -0600 Subject: [PATCH 0096/1694] Comment of function misspelled --- addenda05.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addenda05.go b/addenda05.go index bbc9b69db..ef614ebed 100644 --- a/addenda05.go +++ b/addenda05.go @@ -62,7 +62,7 @@ func (addenda05 *Addenda05) String() string { addenda05.EntryDetailSequenceNumberField()) } -// SetPaymentRealtedInformation allows additional information about the transaction +// SetPaymentRelatedInformation allows additional information about the transaction func (addenda05 *Addenda05) SetPaymentRelatedInformation(s string) *Addenda05 { addenda05.PaymentRelatedInformation = s return addenda05 From 7d04f9b34b03aa5e92ee1c67c6aecee7b1f8dcd1 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 15 May 2018 09:07:58 -0600 Subject: [PATCH 0097/1694] Adding authors --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index c6d6c4c87..d4a4a600e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1 +1,2 @@ - Wade Arnold (@wadearnold) +- Brooke Kline (@bkmoovio) From d1faf0cbd2c2b51658b277a1b0b840c3d557d6c1 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 15 May 2018 09:55:24 -0600 Subject: [PATCH 0098/1694] Contributors guide --- CONTRIBUTING.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ccd4d07bb..1e168904e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Stability is the hallmark of any good software. If you find an edge case that is Have a use case that we don't handle; or handle well! Start the discussion on our Google Group or open a GitHub Issue. We want to make the project meet the needs of the community and keeps you using our code. -Please review our [Code of Conduct](CODE_OF_CONDUCT.md) to ensure you agree with the values of this project. +Please review our [Code of Conduct](CODE_OF_CONDUCT.md) to ensure you agree with the values of this project. We use GitHub to manage reviews of pull requests. @@ -25,6 +25,8 @@ We use GitHub to manage reviews of pull requests. and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). + +* When in doubt follow the [Go Proverbs](https://go-proverbs.github.io/) ## Pull Requests @@ -35,6 +37,7 @@ A good quality PR will have the following characteristics: * There will be well written commit messages, with well crafted commits that tell the story of the development of this work. * Ideally it will be small and easy to understand. Single commit PRs are usually easy to submit, review, and merge. * The code contained within will meet the best practices set by the team wherever possible. +* The code is able to be merged. * A PR does not end at submission though. A code change is not made until it is merged and used in production. A good PR should be able to flow through a peer review system easily and quickly. From 1a06e797c69c1fa5ffd5219b4bd30e4d4ce552d9 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 16 May 2018 10:54:45 -0600 Subject: [PATCH 0099/1694] JSON encoding keys Adding JSON encoding keys to the structs. --- addenda05.go | 6 +++--- addenda98.go | 10 +++++----- addenda99.go | 12 ++++++------ batch.go | 8 ++++---- batchControl.go | 35 ++++++++++++++++++----------------- batchHeader.go | 22 +++++++++++----------- entryDetail.go | 26 +++++++++++++------------- file.go | 6 +++--- fileControl.go | 12 ++++++------ fileHeader.go | 16 ++++++++-------- 10 files changed, 77 insertions(+), 76 deletions(-) diff --git a/addenda05.go b/addenda05.go index ef614ebed..d4989089d 100644 --- a/addenda05.go +++ b/addenda05.go @@ -13,16 +13,16 @@ type Addenda05 struct { // TypeCode Addenda05 types code '05' typeCode string // PaymentRelatedInformation - PaymentRelatedInformation string + PaymentRelatedInformation string `json:"paymentRelatedInformation"` // SequenceNumber is consecutively assigned to each Addenda05 Record following // an Entry Detail Record. The first addenda05 sequence number must always // be a "1". - SequenceNumber int + SequenceNumber int `json:"sequenceNumber,omitempty"` // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry // Detail or Corporate Entry Detail Record's trace number This number is // the same as the last seven digits of the trace number of the related // Entry Detail Record or Corporate Entry Detail Record. - EntryDetailSequenceNumber int + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to GoLang Converters diff --git a/addenda98.go b/addenda98.go index da86a09ca..5b850bbe9 100644 --- a/addenda98.go +++ b/addenda98.go @@ -14,17 +14,17 @@ type Addenda98 struct { typeCode string // ChangeCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for a change Entry. // Must exist in changeCodeDict - ChangeCode string + ChangeCode string `json:"changeCode"` // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. - OriginalTrace int + OriginalTrace int `json:"originalTrace"` // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI string + OriginalDFI string `json:"originalDFI"` // CorrectedData - CorrectedData string + CorrectedData string `json:"correctedData"` // TraceNumber matches the Entry Detail Trace Number of the entry being returned. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // validator is composed for data validation validator diff --git a/addenda99.go b/addenda99.go index 250238742..88075261f 100644 --- a/addenda99.go +++ b/addenda99.go @@ -34,19 +34,19 @@ type Addenda99 struct { typeCode string // ReturnCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for returning an Entry. // Must exist in returnCodeDict - ReturnCode string + ReturnCode string `json:"returnCode"` // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. - OriginalTrace int + OriginalTrace int `json:"originalTrace"` // DateOfDeath The field date of death is to be supplied on Entries being returned for reason of death (return reason codes R14 and R15). - DateOfDeath time.Time + DateOfDeath time.Time `json:"dateOfDeath"` // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI string + OriginalDFI string `json:"originalDFI"` // AddendaInformation - AddendaInformation string + AddendaInformation string `json:"addendaInformation,omitempty"` // TraceNumber matches the Entry Detail Trace Number of the entry being returned. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // validator is composed for data validation validator diff --git a/batch.go b/batch.go index c89323daa..2e4dede8e 100644 --- a/batch.go +++ b/batch.go @@ -8,12 +8,12 @@ import ( // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries type batch struct { - header *BatchHeader - entries []*EntryDetail - control *BatchControl + header *BatchHeader `json:"batchHeader,omitempty"` + entries []*EntryDetail `json:"entryDetails,omitempty"` + control *BatchControl `json:"batchControl,omitempty"` // category defines if the entry is a Forward, Return, or NOC - category string + category string `json:"category,omitempty"` // Converters is composed for ACH to GoLang Converters converters } diff --git a/batchControl.go b/batchControl.go index a5003e232..11de9739c 100644 --- a/batchControl.go +++ b/batchControl.go @@ -13,55 +13,56 @@ import ( // BatchControl contains entry counts, dollar total and has totals for all // entries contained in the preceding batch type BatchControl struct { - // RecordType defines the type of record in the block. batchControlPos 8 + // RecordType defines the type of record in the block. + // batchControlPos 8 recordType string // ServiceClassCode ACH Mixed Debits and Credits ‘200’ // ACH Credits Only ‘220’ // ACH Debits Only ‘225' // Same as 'ServiceClassCode' in BatchHeaderRecord - ServiceClassCode int + ServiceClassCode int `json:"serviceClassCode"` // EntryAddendaCount is a tally of each Entry Detail Record and each Addenda // Record processed, within either the batch or file as appropriate. - EntryAddendaCount int + EntryAddendaCount int `json:"entryAddendaÇount"` // validate the Receiving DFI Identification in each Entry Detail Record is hashed // to provide a check against inadvertent alteration of data contents due - // to hardware failure or program erro + // to hardware failure or program error // // In this context the Entry Hash is the sum of the corresponding fields in the // Entry Detail Records on the file. - EntryHash int + EntryHash int `json:"entryHash"` // TotalDebitEntryDollarAmount Contains accumulated Entry debit totals within the batch. - TotalDebitEntryDollarAmount int + TotalDebitEntryDollarAmount int `json:"totalDebit"` // TotalCreditEntryDollarAmount Contains accumulated Entry credit totals within the batch. - TotalCreditEntryDollarAmount int - // CompanyIdentification is an alphameric code used to identify an Originato + TotalCreditEntryDollarAmount int `json:"totalCredit"` + // CompanyIdentification is an alphameric code used to identify an Originator // The Company Identification Field must be included on all - // prenotification records and on each entry initiated puruant to such + // prenotification records and on each entry initiated pursuant to such // prenotification. The Company ID may begin with the ANSI one-digit - // Identification Code Designators (ICD), followed by the identification - // numbe The ANSI Identification Numbers and related Identification Code - // Designators (ICD) are: + // Identification Code Designator (ICD), followed by the identification + // number The ANSI Identification Numbers and related Identification Code + // Designator (ICD) are: // // IRS Employer Identification Number (EIN) "1" // Data Universal Numbering Systems (DUNS) "3" // User Assigned Number "9" - CompanyIdentification string + CompanyIdentification string `json:"companyIdentification"` // MessageAuthenticationCode the MAC is an eight character code derived from a special key used in // conjunction with the DES algorithm. The purpose of the MAC is to // validate the authenticity of ACH entries. The DES algorithm and key // message standards must be in accordance with standards adopted by the // American National Standards Institute. The remaining eleven characters // of this field are blank. - MessageAuthenticationCode string + MessageAuthenticationCode string `json:"messageAuthentication,omitempty"` // Reserved for the future - Blank, 6 characters long reserved string - // OdfiIdentification the routing number is used to identify the DFI originating entries within a given branch. - ODFIIdentification string + // ODFIIdentification the routing number is used to identify the DFI originating entries within a given branch. + ODFIIdentification string `json:"ODFIIdentification"` // BatchNumber this number is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number // in the Batch Header Record and the Batch Control Record is the same, // the ascending sequence number should be assigned by batch and not by record. - BatchNumber int + BatchNumber int `json:"batchNumber"` // validator is composed for data validation validator // converters is composed for ACH to golang Converters diff --git a/batchHeader.go b/batchHeader.go index 9c475c8f8..6baee7f0e 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -25,21 +25,21 @@ type BatchHeader struct { // ServiceClassCode ACH Mixed Debits and Credits ‘200’ // ACH Credits Only ‘220’ // ACH Debits Only ‘225' - ServiceClassCode int + ServiceClassCode int `json:"serviceClassCode"` // CompanyName the company originating the entries in the batch - CompanyName string + CompanyName string `json:"companyName"` // CompanyDiscretionaryData allows Originators and/or ODFIs to include codes (one or more), // of significance only to them, to enable specialized handling of all // subsequent entries in that batch. There will be no standardized // interpretation for the value of the field. This field must be returned // intact on any return entry. - CompanyDiscretionaryData string + CompanyDiscretionaryData string `json:"companyDiscretionaryData,omitempty"` // CompanyIdentification The 9 digit FEIN number (proceeded by a predetermined // alpha or numeric character) of the entity in the company name field - CompanyIdentification string + CompanyIdentification string `json:"companyIdentification"` // StandardEntryClassCode // Identifies the payment type (product) found within an ACH batch-using a 3-character code. @@ -48,7 +48,7 @@ type BatchHeader struct { // Determines addenda records (required or optional PLUS one or up to 9,999 records). // Determines rules to follow (return time frames). // Some SEC codes require specific data in predetermined fields within the ACH record - StandardEntryClassCode string + StandardEntryClassCode string `json:"standardEntryClassCode,omitempty"` // CompanyEntryDescription A description of the entries contained in the batch // @@ -65,7 +65,7 @@ type BatchHeader struct { // // This field must contain the word "NONSETTLED" (left justified) when the // batch contains entries which could not settle. - CompanyEntryDescription string + CompanyEntryDescription string `json:"companyEntryDescription,omitempty"` // CompanyDescriptiveDate except as otherwise noted below, the Originator establishes this field // as the date it would like to see displayed to the receiver for @@ -73,10 +73,10 @@ type BatchHeader struct { // computer or manual operation. It is solely for descriptive purposes. // The RDFI should not assume any specific format. Examples of possible // entries in this field are "011392,", "01 92," "JAN 13," "JAN 92," etc. - CompanyDescriptiveDate string + CompanyDescriptiveDate string `json:"companyDescriptiveDate,omitempty"` // EffectiveEntryDate the date on which the entries are to settle - EffectiveEntryDate time.Time + EffectiveEntryDate time.Time `json:"effectiveEntryDate,omitempty"` // SettlementDate Leave blank, this field is inserted by the ACH operator settlementDate string @@ -85,17 +85,17 @@ type BatchHeader struct { // 0 ADV File prepared by an ACH Operator. // 1 This code identifies the Originator as a depository financial institution. // 2 This code identifies the Originator as a Federal Government entity or agency. - OriginatorStatusCode int + OriginatorStatusCode int `json:"originatorStatusCode,omitempty"` //ODFIIdentification First 8 digits of the originating DFI transit routing number - ODFIIdentification string + ODFIIdentification string `json:"ODFIIdentification"` // BatchNumber is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number // in the Batch Header Record and the Batch Control Record is the same, // the ascending sequence number should be assigned by batch and not by // record. - BatchNumber int + BatchNumber int `json:"batchNumber,omitempty"` // validator is composed for data validation validator diff --git a/entryDetail.go b/entryDetail.go index c9a773e39..1e21122b7 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -26,28 +26,28 @@ type EntryDetail struct { // Prenote for credit to savings account ‘33’ // Debit to savings account ‘37’ // Prenote for debit to savings account ‘38’ - TransactionCode int + TransactionCode int `json:"transactionCode"` // RDFIIdentification is the RDFI's routing number without the last digit. // Receiving Depository Financial Institution - RDFIIdentification string + RDFIIdentification string `json:"RDFIIdentification"` // CheckDigit the last digit of the RDFI's routing number - CheckDigit string + CheckDigit string `json:"checkDigit"` // DFIAccountNumber is the receiver's bank account number you are crediting/debiting. // It important to note that this is an alphanumeric field, so its space padded, no zero padded - DFIAccountNumber string + DFIAccountNumber string `json:"DFIAccountNumber"` // Amount Number of cents you are debiting/crediting this account - Amount int + Amount int `json:"amount"` - // IdentificationNumber n internal identification (alphanumeric) that + // IdentificationNumber an internal identification (alphanumeric) that // you use to uniquely identify this Entry Detail Record - IdentificationNumber string + IdentificationNumber string `json:"identificationNumber,omitempty"` // IndividualName The name of the receiver, usually the name on the bank account - IndividualName string + IndividualName string `json:"individualName"` // DiscretionaryData allows ODFIs to include codes, of significance only to them, // to enable specialized handling of the entry. There will be no @@ -57,12 +57,12 @@ type EntryDetail struct { // field must be returned intact for any returned entry. // // WEB uses the Discretionary Data Field as the Payment Type Code - DiscretionaryData string + DiscretionaryData string `json:"discretionaryData,omitempty"` // AddendaRecordIndicator indicates the existence of an Addenda Record. // A value of "1" indicates that one ore more addenda records follow, // and "0" means no such record is present. - AddendaRecordIndicator int + AddendaRecordIndicator int `json:"addendaRecordIndicator,omitempty"` // TraceNumber assigned by the ODFI in ascending sequence, is included in each // Entry Detail Record, Corporate Entry Detail Record, and addenda Record. @@ -72,12 +72,12 @@ type EntryDetail struct { // For addenda Records, the Trace Number will be identical to the Trace Number // in the associated Entry Detail Record, since the Trace Number is associated // with an entry or item rather than a physical record. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // Addendum a list of Addenda for the Entry Detail - Addendum []Addendumer + Addendum []Addendumer `json:"addendum,omitempty"` // Category defines if the entry is a Forward, Return, or NOC - Category string + Category string `json:"category,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to golang Converters diff --git a/file.go b/file.go index e248372e8..ad16502b9 100644 --- a/file.go +++ b/file.go @@ -53,9 +53,9 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { - Header FileHeader - Batches []Batcher - Control FileControl + Header FileHeader `json:"fileHeader"` + Batches []Batcher `json:"batches"` + Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches NotificationOfChange []*BatchCOR diff --git a/fileControl.go b/fileControl.go index 4c96fc130..a70cfb6cc 100644 --- a/fileControl.go +++ b/fileControl.go @@ -13,23 +13,23 @@ type FileControl struct { recordType string // BatchCount total number of batches (i.e., ‘5’ records) in the file - BatchCount int + BatchCount int `json:"batchCount"` // BlockCount total number of records in the file (include all headers and trailer) divided // by 10 (This number must be evenly divisible by 10. If not, additional records consisting of all 9’s are added to the file after the initial ‘9’ record to fill out the block 10.) - BlockCount int + BlockCount int `json:"blockCount,omitempty"` // EntryAddendaCount total detail and addenda records in the file - EntryAddendaCount int + EntryAddendaCount int `json:"entryAddendaCount"` // EntryHash calculated in the same manner as the batch has total but includes total from entire file - EntryHash int + EntryHash int `json:"entryHash"` // TotalDebitEntryDollarAmountInFile contains accumulated Batch debit totals within the file. - TotalDebitEntryDollarAmountInFile int + TotalDebitEntryDollarAmountInFile int `json:"totalDebit"` // TotalCreditEntryDollarAmountInFile contains accumulated Batch credit totals within the file. - TotalCreditEntryDollarAmountInFile int + TotalCreditEntryDollarAmountInFile int `json:"totalCredit"` // Reserved should be blank. reserved string // validator is composed for data validation diff --git a/fileHeader.go b/fileHeader.go index 4f3a10bff..d9b7b2a8a 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -35,7 +35,7 @@ type FileHeader struct { // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). ImmediateDestinationField() will append the blank space to the // routing number. - ImmediateDestination string + ImmediateDestination string `json:"immediateDestination"` // ImmediateOrigin contains the Routing Number of the ACH Operator or sending // point that is sending the file. The ach file format specifies a 10 character field @@ -43,23 +43,23 @@ type FileHeader struct { // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). ImmediateOriginField() will append the blank space to the routing // number. - ImmediateOrigin string + ImmediateOrigin string `json:"immediateOrigin"` // FileCreationDate is expressed in a "YYMMDD" format. The File Creation // Date is the date on which the file is prepared by an ODFI (ACH input files) // or the date (exchange date) on which a file is transmitted from ACH Operator // to ACH Operator, or from ACH Operator to RDFIs (ACH output files). - FileCreationDate time.Time + FileCreationDate time.Time `json:"fileCreationDate"` // FileCreationTime is expressed ina n "HHMM" (24 hour clock) format. // The system time when the ACH file was created - FileCreationTime time.Time + FileCreationTime time.Time `json:"fileCreationTime"` // This field should start at zero and increment by 1 (up to 9) and then go to // letters starting at A through Z for each subsequent file that is created for // a single system date. (34-34) 1 numeric 0-9 or uppercase alpha A-Z. // I have yet to see this ID not A - FileIDModifier string + FileIDModifier string `json:"fileIDModifier,omitempty"` // RecordSize indicates the number of characters contained in each // record. At this time, the value "094" must be used. @@ -78,14 +78,14 @@ type FileHeader struct { // ImmediateDestinationName us the name of the ACH or receiving point for which that // file is destined. Name corresponding to the ImmediateDestination - ImmediateDestinationName string + ImmediateDestinationName string `json:"immediateDestinationName"` // ImmediateOriginName is the name of the ACH operator or sending point that is // sending the file. Name corresponding to the ImmediateOrigin - ImmediateOriginName string + ImmediateOriginName string `json:"immediateOriginName"` // ReferenceCode is reserved for information pertinent to the Originator. - ReferenceCode string + ReferenceCode string `json:"referenceCode,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to GoLang Converters From b0b13a9f916cb51bb2c54e5d3c2bf2f9c5341c19 Mon Sep 17 00:00:00 2001 From: Matias Insaurralde Date: Thu, 17 May 2018 05:44:00 -0400 Subject: [PATCH 0100/1694] Wrap existing addenda and batch tests with benchmark code --- addenda05_test.go | 26 ++++- addenda98_test.go | 143 ++++++++++++++++++++++++--- addenda99_test.go | 129 ++++++++++++++++++++++-- batch_test.go | 243 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 498 insertions(+), 43 deletions(-) diff --git a/addenda05_test.go b/addenda05_test.go index 29d3b9854..cdedd0e43 100644 --- a/addenda05_test.go +++ b/addenda05_test.go @@ -27,7 +27,7 @@ func TestMockAddenda05(t *testing.T) { } } -func TestParseAddenda05(t *testing.T) { +func testParseAddenda05(t testing.TB) { addendaPPD := NewAddenda05() var line = "705PPD DIEGO MAY 00010000001" addendaPPD.Parse(line) @@ -65,8 +65,19 @@ func TestParseAddenda05(t *testing.T) { } } +func TestParseAddenda05(t *testing.T) { + testParseAddenda05(t) +} + +func BenchmarkParseAddenda05(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseAddenda05(b) + } +} + // TestAddenda05 String validates that a known parsed file can be return to a string of the same value -func TestAddenda05String(t *testing.T) { +func testAddenda05String(t testing.TB) { addenda05 := NewAddenda05() var line = "705WEB DIEGO MAY 00010000001" addenda05.Parse(line) @@ -76,6 +87,17 @@ func TestAddenda05String(t *testing.T) { } } +func TestAddenda05String(t *testing.T) { + testAddenda05String(t) +} + +func BenchmarkAddenda05String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda05String(b) + } +} + func TestValidateAddenda05RecordType(t *testing.T) { addenda05 := mockAddenda05() addenda05.recordType = "63" diff --git a/addenda98_test.go b/addenda98_test.go index c09576cc1..95756cc88 100644 --- a/addenda98_test.go +++ b/addenda98_test.go @@ -15,7 +15,7 @@ func mockAddenda98() *Addenda98 { return addenda98 } -func TestAddenda98Parse(t *testing.T) { +func testAddenda98Parse(t testing.TB) { addenda98 := NewAddenda98() line := "798C01099912340000015 091012981918171614 091012980000088" addenda98.Parse(line) @@ -43,7 +43,18 @@ func TestAddenda98Parse(t *testing.T) { } } -func TestAddenda98String(t *testing.T) { +func TestAddenda98Parse(t *testing.T) { + testAddenda98Parse(t) +} + +func BenchmarkAddenda98Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98Parse(b) + } +} + +func testAddenda98String(t testing.TB) { addenda98 := NewAddenda98() line := "798C01099912340000015 091012981918171614 091012980000088" addenda98.Parse(line) @@ -53,7 +64,18 @@ func TestAddenda98String(t *testing.T) { } } -func TestAddenda98ValidRecordType(t *testing.T) { +func TestAddenda98String(t *testing.T) { + testAddenda98String(t) +} + +func BenchmarkAddenda98String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98String(b) + } +} + +func testAddenda98ValidRecordType(t testing.TB) { addenda98 := mockAddenda98() addenda98.recordType = "63" if err := addenda98.Validate(); err != nil { @@ -66,8 +88,18 @@ func TestAddenda98ValidRecordType(t *testing.T) { } } } +func TestAddenda98ValidRecordType(t *testing.T) { + testAddenda98ValidRecordType(t) +} -func TestAddenda98ValidTypeCode(t *testing.T) { +func BenchmarkAddenda98ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98ValidRecordType(b) + } +} + +func testAddenda98ValidTypeCode(t testing.TB) { addenda98 := mockAddenda98() addenda98.typeCode = "05" if err := addenda98.Validate(); err != nil { @@ -81,7 +113,18 @@ func TestAddenda98ValidTypeCode(t *testing.T) { } } -func TestAddenda98ValidCorrectedData(t *testing.T) { +func TestAddenda98ValidTypeCode(t *testing.T) { + testAddenda98ValidTypeCode(t) +} + +func BenchmarkAddenda98ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98ValidTypeCode(b) + } +} + +func testAddenda98ValidCorrectedData(t testing.TB) { addenda98 := mockAddenda98() addenda98.CorrectedData = "" if err := addenda98.Validate(); err != nil { @@ -95,7 +138,18 @@ func TestAddenda98ValidCorrectedData(t *testing.T) { } } -func TestAddenda98ValidateTrue(t *testing.T) { +func TestAddenda98ValidCorrectedData(t *testing.T) { + testAddenda98ValidCorrectedData(t) +} + +func BenchmarkAddenda98ValidCorrectedData(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98ValidCorrectedData(b) + } +} + +func testAddenda98ValidateTrue(t testing.TB) { addenda98 := mockAddenda98() addenda98.ChangeCode = "C11" if err := addenda98.Validate(); err != nil { @@ -108,7 +162,19 @@ func TestAddenda98ValidateTrue(t *testing.T) { } } } -func TestAddenda98ValidateChangeCodeFalse(t *testing.T) { + +func TestAddenda98ValidateTrue(t *testing.T) { + testAddenda98ValidateTrue(t) +} + +func BenchmarkAddenda98ValidateTrue(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98ValidateTrue(b) + } +} + +func testAddenda98ValidateChangeCodeFalse(t testing.TB) { addenda98 := mockAddenda98() addenda98.ChangeCode = "C63" if err := addenda98.Validate(); err != nil { @@ -122,7 +188,18 @@ func TestAddenda98ValidateChangeCodeFalse(t *testing.T) { } } -func TestAddenda98OriginalTraceField(t *testing.T) { +func TestAddenda98ValidateChangeCodeFalse(t *testing.T) { + testAddenda98ValidateChangeCodeFalse(t) +} + +func BenchmarkAddenda98ValidateChangeCodeFalse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98ValidateChangeCodeFalse(b) + } +} + +func testAddenda98OriginalTraceField(t testing.TB) { addenda98 := mockAddenda98() exp := "000000000012345" if addenda98.OriginalTraceField() != exp { @@ -130,7 +207,18 @@ func TestAddenda98OriginalTraceField(t *testing.T) { } } -func TestAddenda98OriginalDFIField(t *testing.T) { +func TestAddenda98OriginalTraceField(t *testing.T) { + testAddenda98OriginalTraceField(t) +} + +func BenchmarkAddenda98OriginalTraceField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98OriginalTraceField(b) + } +} + +func testAddenda98OriginalDFIField(t testing.TB) { addenda98 := mockAddenda98() exp := "09101298" if addenda98.OriginalDFIField() != exp { @@ -138,7 +226,18 @@ func TestAddenda98OriginalDFIField(t *testing.T) { } } -func TestAddenda98CorrectedDataField(t *testing.T) { +func TestAddenda98OriginalDFIField(t *testing.T) { + testAddenda98OriginalDFIField(t) +} + +func BenchmarkAddenda98OriginalDFIField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98OriginalDFIField(b) + } +} + +func testAddenda98CorrectedDataField(t testing.TB) { addenda98 := mockAddenda98() exp := "1918171614 " // 29 char if addenda98.CorrectedDataField() != exp { @@ -146,10 +245,32 @@ func TestAddenda98CorrectedDataField(t *testing.T) { } } -func TestAddenda98TraceNumberField(t *testing.T) { +func TestAddenda98CorrectedDataField(t *testing.T) { + testAddenda98CorrectedDataField(t) +} + +func BenchmarkAddenda98CorrectedDataField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98CorrectedDataField(b) + } +} + +func testAddenda98TraceNumberField(t testing.TB) { addenda98 := mockAddenda98() exp := "091012980000088" if addenda98.TraceNumberField() != exp { t.Errorf("expected %v received %v", exp, addenda98.TraceNumberField()) } } + +func TestAddenda98TraceNumberField(t *testing.T) { + testAddenda98TraceNumberField(t) +} + +func BenchmarkAddenda98TraceNumberField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98TraceNumberField(b) + } +} diff --git a/addenda99_test.go b/addenda99_test.go index eedc6a67d..7e569d7cc 100644 --- a/addenda99_test.go +++ b/addenda99_test.go @@ -19,7 +19,7 @@ func mockAddenda99() *Addenda99 { return addenda99 } -func TestAddenda99Parse(t *testing.T) { +func testAddenda99Parse(t testing.TB) { addenda99 := NewAddenda99() line := "799R07099912340000015 09101298Authorization revoked 091012980000066" addenda99.Parse(line) @@ -50,7 +50,18 @@ func TestAddenda99Parse(t *testing.T) { } } -func TestAddenda99String(t *testing.T) { +func TestAddenda99Parse(t *testing.T) { + testAddenda99Parse(t) +} + +func BenchmarkAddenda99Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99Parse(b) + } +} + +func testAddenda99String(t testing.TB) { addenda99 := NewAddenda99() line := "799R07099912340000015 09101298Authorization revoked 091012980000066" addenda99.Parse(line) @@ -60,8 +71,19 @@ func TestAddenda99String(t *testing.T) { } } +func TestAddenda99String(t *testing.T) { + testAddenda99String(t) +} + +func BenchmarkAddenda99String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99String(b) + } +} + // This is not an exported function but utilized for validation -func TestAddenda99MakeReturnCodeDict(t *testing.T) { +func testAddenda99MakeReturnCodeDict(t testing.TB) { codes := makeReturnCodeDict() // check if known code is present _, prs := codes["R01"] @@ -75,7 +97,18 @@ func TestAddenda99MakeReturnCodeDict(t *testing.T) { } } -func TestAddenda99ValidateTrue(t *testing.T) { +func TestAddenda99MakeReturnCodeDict(t *testing.T) { + testAddenda99MakeReturnCodeDict(t) +} + +func BenchmarkAddenda99MakeReturnCodeDict(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99MakeReturnCodeDict(b) + } +} + +func testAddenda99ValidateTrue(t testing.TB) { addenda99 := mockAddenda99() addenda99.ReturnCode = "R13" if err := addenda99.Validate(); err != nil { @@ -89,7 +122,18 @@ func TestAddenda99ValidateTrue(t *testing.T) { } } -func TestAddenda99ValidateReturnCodeFalse(t *testing.T) { +func TestAddenda99ValidateTrue(t *testing.T) { + testAddenda99ValidateTrue(t) +} + +func BenchmarkAddenda99ValidateTrue(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99ValidateTrue(b) + } +} + +func testAddenda99ValidateReturnCodeFalse(t testing.TB) { addenda99 := mockAddenda99() addenda99.ReturnCode = "" if err := addenda99.Validate(); err != nil { @@ -103,7 +147,18 @@ func TestAddenda99ValidateReturnCodeFalse(t *testing.T) { } } -func TestAddenda99OriginalTraceField(t *testing.T) { +func TestAddenda99ValidateReturnCodeFalse(t *testing.T) { + testAddenda99ValidateReturnCodeFalse(t) +} + +func BenchmarkAddenda99ValidateReturnCodeFalse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99ValidateReturnCodeFalse(b) + } +} + +func testAddenda99OriginalTraceField(t testing.TB) { addenda99 := mockAddenda99() addenda99.OriginalTrace = 12345 if addenda99.OriginalTraceField() != "000000000012345" { @@ -111,7 +166,18 @@ func TestAddenda99OriginalTraceField(t *testing.T) { } } -func TestAddenda99DateOfDeathField(t *testing.T) { +func TestAddenda99OriginalTraceField(t *testing.T) { + testAddenda99OriginalTraceField(t) +} + +func BenchmarkAddenda99OriginalTraceField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99OriginalTraceField(b) + } +} + +func testAddenda99DateOfDeathField(t testing.TB) { addenda99 := mockAddenda99() // Check for all zeros if addenda99.DateOfDeathField() != " " { @@ -124,7 +190,17 @@ func TestAddenda99DateOfDeathField(t *testing.T) { } } -func TestAddenda99OriginalDFIField(t *testing.T) { +func TestAddenda99DateOfDeathField(t *testing.T) { + testAddenda99DateOfDeathField(t) +} +func BenchmarkAddenda99DateOfDeathField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99DateOfDeathField(b) + } +} + +func testAddenda99OriginalDFIField(t testing.TB) { addenda99 := mockAddenda99() exp := "09101298" if addenda99.OriginalDFIField() != exp { @@ -132,7 +208,18 @@ func TestAddenda99OriginalDFIField(t *testing.T) { } } -func TestAddenda99AddendaInformationField(t *testing.T) { +func TestAddenda99OriginalDFIField(t *testing.T) { + testAddenda99OriginalDFIField(t) +} + +func BenchmarkAddenda99OriginalDFIField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99OriginalDFIField(b) + } +} + +func testAddenda99AddendaInformationField(t testing.TB) { addenda99 := mockAddenda99() exp := "Authorization Revoked " if addenda99.AddendaInformationField() != exp { @@ -140,7 +227,18 @@ func TestAddenda99AddendaInformationField(t *testing.T) { } } -func TestAddenda99TraceNumberField(t *testing.T) { +func TestAddenda99AddendaInformationField(t *testing.T) { + testAddenda99AddendaInformationField(t) +} + +func BenchmarkAddenda99AddendaInformationField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99AddendaInformationField(b) + } +} + +func testAddenda99TraceNumberField(t testing.TB) { addenda99 := mockAddenda99() addenda99.TraceNumber = 91012980000066 exp := "091012980000066" @@ -148,3 +246,14 @@ func TestAddenda99TraceNumberField(t *testing.T) { t.Errorf("expected %v received %v", exp, addenda99.TraceNumberField()) } } + +func TestAddenda99TraceNumberField(t *testing.T) { + testAddenda99TraceNumberField(t) +} + +func BenchmarkAddenda99TraceNumberField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99TraceNumberField(b) + } +} diff --git a/batch_test.go b/batch_test.go index 2795ab79f..665366307 100644 --- a/batch_test.go +++ b/batch_test.go @@ -59,8 +59,7 @@ func mockBatchInvalidSECHeader() *BatchHeader { } // Test cases that apply to all batch types - -func TestBatchNumberMismatch(t *testing.T) { +func testBatchNumberMismatch(t testing.TB) { mockBatch := mockBatch() mockBatch.GetControl().BatchNumber = 2 if err := mockBatch.verify(); err != nil { @@ -74,7 +73,17 @@ func TestBatchNumberMismatch(t *testing.T) { } } -func TestCreditBatchisBatchAmount(t *testing.T) { +func TestBatchNumberMismatch(t *testing.T) { + testBatchNumberMismatch(t) +} +func BenchmarkBatchNumberMismatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchNumberMismatch(b) + } +} + +func testCreditBatchisBatchAmount(t testing.TB) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) e1 := mockBatch.GetEntries()[0] @@ -101,7 +110,18 @@ func TestCreditBatchisBatchAmount(t *testing.T) { } } -func TestSavingsBatchisBatchAmount(t *testing.T) { +func TestCreditBatchisBatchAmount(t *testing.T) { + testCreditBatchisBatchAmount(t) +} + +func BenchmarkCreditBatchisBatchAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testCreditBatchisBatchAmount(b) + } + +} +func testSavingsBatchisBatchAmount(t testing.TB) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) e1 := mockBatch.GetEntries()[0] @@ -129,7 +149,18 @@ func TestSavingsBatchisBatchAmount(t *testing.T) { } } -func TestBatchisEntryHash(t *testing.T) { +func TestSavingsBatchisBatchAmount(t *testing.T) { + testSavingsBatchisBatchAmount(t) +} + +func BenchmarkSavingsBatchisBatchAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testSavingsBatchisBatchAmount(b) + } +} + +func testBatchisEntryHash(t testing.TB) { mockBatch := mockBatch() mockBatch.GetControl().EntryHash = 1 if err := mockBatch.verify(); err != nil { @@ -143,7 +174,18 @@ func TestBatchisEntryHash(t *testing.T) { } } -func TestBatchDNEMismatch(t *testing.T) { +func TestBatchisEntryHash(t *testing.T) { + testBatchisEntryHash(t) +} + +func BenchmarkBatchisEntryHash(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchisEntryHash(b) + } +} + +func testBatchDNEMismatch(t testing.TB) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) ed := mockBatch.GetEntries()[0] @@ -164,7 +206,18 @@ func TestBatchDNEMismatch(t *testing.T) { } } -func TestBatchTraceNumberNotODFI(t *testing.T) { +func TestBatchDNEMismatch(t *testing.T) { + testBatchDNEMismatch(t) +} + +func BenchmarkBatchDNEMismatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchDNEMismatch(b) + } +} + +func testBatchTraceNumberNotODFI(t testing.TB) { mockBatch := mockBatch() mockBatch.GetEntries()[0].SetTraceNumber("12345678", 1) if err := mockBatch.verify(); err != nil { @@ -178,7 +231,18 @@ func TestBatchTraceNumberNotODFI(t *testing.T) { } } -func TestBatchEntryCountEquality(t *testing.T) { +func TestBatchTraceNumberNotODFI(t *testing.T) { + testBatchTraceNumberNotODFI(t) +} + +func BenchmarkBatchTraceNumberNotODFI(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTraceNumberNotODFI(b) + } +} + +func testBatchEntryCountEquality(t testing.TB) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) e := mockEntryDetail() @@ -201,7 +265,18 @@ func TestBatchEntryCountEquality(t *testing.T) { } } -func TestBatchAddendaIndicator(t *testing.T) { +func TestBatchEntryCountEquality(t *testing.T) { + testBatchEntryCountEquality(t) +} + +func BenchmarkBatchEntryCountEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchEntryCountEquality(b) + } +} + +func testBatchAddendaIndicator(t testing.TB) { mockBatch := mockBatch() mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) mockBatch.GetEntries()[0].AddendaRecordIndicator = 0 @@ -217,7 +292,18 @@ func TestBatchAddendaIndicator(t *testing.T) { } } -func TestBatchIsAddendaSeqAscending(t *testing.T) { +func TestBatchAddendaIndicator(t *testing.T) { + testBatchAddendaIndicator(t) +} + +func BenchmarkBatchAddendaIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchAddendaIndicator(b) + } +} + +func testBatchIsAddendaSeqAscending(t testing.TB) { mockBatch := mockBatch() ed := mockBatch.GetEntries()[0] ed.AddAddenda(mockAddenda05()) @@ -235,10 +321,19 @@ func TestBatchIsAddendaSeqAscending(t *testing.T) { t.Errorf("%T: %s", err, err) } } +} +func TestBatchIsAddendaSeqAscending(t *testing.T) { + testBatchIsAddendaSeqAscending(t) +} +func BenchmarkBatchIsAddendaSeqAscending(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchIsAddendaSeqAscending(b) + } } -func TestBatchIsSequenceAscending(t *testing.T) { +func testBatchIsSequenceAscending(t testing.TB) { mockBatch := mockBatch() e3 := mockEntryDetail() e3.TraceNumber = 1 @@ -255,7 +350,18 @@ func TestBatchIsSequenceAscending(t *testing.T) { } } -func TestBatchAddendaTraceNumber(t *testing.T) { +func TestBatchIsSequenceAscending(t *testing.T) { + testBatchIsSequenceAscending(t) +} + +func BenchmarkBatchIsSequenceAscending(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchIsSequenceAscending(b) + } +} + +func testBatchAddendaTraceNumber(t testing.TB) { mockBatch := mockBatch() mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) if err := mockBatch.build(); err != nil { @@ -274,7 +380,17 @@ func TestBatchAddendaTraceNumber(t *testing.T) { } } -func TestNewBatchDefault(t *testing.T) { +func TestBatchAddendaTraceNumber(t *testing.T) { + testBatchAddendaTraceNumber(t) +} + +func BenchmarkBatchAddendaTraceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchAddendaTraceNumber(b) + } +} +func testNewBatchDefault(t testing.TB) { _, err := NewBatch(mockBatchInvalidSECHeader()) if e, ok := err.(*FileError); ok { @@ -286,7 +402,18 @@ func TestNewBatchDefault(t *testing.T) { } } -func TestBatchCategory(t *testing.T) { +func TestNewBatchDefault(t *testing.T) { + testNewBatchDefault(t) +} + +func BenchmarkNewBatchDefault(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testNewBatchDefault(b) + } +} + +func testBatchCategory(t testing.TB) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch entry := mockEntryDetail() @@ -302,7 +429,18 @@ func TestBatchCategory(t *testing.T) { } } -func TestBatchCategoryForwardReturn(t *testing.T) { +func TestBatchCategory(t *testing.T) { + testBatchCategory(t) +} + +func BenchmarkBatchCategory(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCategory(b) + } +} + +func testBatchCategoryForwardReturn(t testing.TB) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch entry := mockEntryDetail() @@ -323,8 +461,18 @@ func TestBatchCategoryForwardReturn(t *testing.T) { } } +func TestBatchCategoryForwardReturn(t *testing.T) { + testBatchCategoryForwardReturn(t) +} +func BenchmarkBatchCategoryForwardReturn(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCategoryForwardReturn(b) + } +} + // Don't over write a batch trace number when building if it already exists -func TestBatchTraceNumberExists(t *testing.T) { +func testBatchTraceNumberExists(t testing.TB) { mockBatch := mockBatch() entry := mockEntryDetail() traceBefore := entry.TraceNumberField() @@ -336,7 +484,18 @@ func TestBatchTraceNumberExists(t *testing.T) { } } -func TestBatchFieldInclusion(t *testing.T) { +func TestBatchTraceNumberExists(t *testing.T) { + testBatchTraceNumberExists(t) +} + +func BenchmarkBatchTraceNumberExists(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTraceNumberExists(b) + } +} + +func testBatchFieldInclusion(t testing.TB) { mockBatch := mockBatch() mockBatch.header.ODFIIdentification = "" if err := mockBatch.verify(); err != nil { @@ -350,14 +509,36 @@ func TestBatchFieldInclusion(t *testing.T) { } } -func TestBatchInvalidTraceNumberODFI(t *testing.T) { +func TestBatchFieldInclusion(t *testing.T) { + testBatchFieldInclusion(t) +} + +func BenchmarkBatchFieldInclusion(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchFieldInclusion(b) + } +} + +func testBatchInvalidTraceNumberODFI(t testing.TB) { mockBatch := mockBatchInvalidTraceNumberODFI() if err := mockBatch.build(); err != nil { t.Errorf("%T: %s", err, err) } } -func TestBatchNoEntry(t *testing.T) { +func TestBatchInvalidTraceNumberODFI(t *testing.T) { + testBatchInvalidTraceNumberODFI(t) +} + +func BenchmarkBatchInvalidTraceNumberODFI(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchInvalidTraceNumberODFI(b) + } +} + +func testBatchNoEntry(t testing.TB) { mockBatch := mockBatchNoEntry() if err := mockBatch.build(); err != nil { if e, ok := err.(*BatchError); ok { @@ -370,7 +551,18 @@ func TestBatchNoEntry(t *testing.T) { } } -func TestBatchControl(t *testing.T) { +func TestBatchNoEntry(t *testing.T) { + testBatchNoEntry(t) +} + +func BenchmarkBatchNoEntry(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchNoEntry(b) + } +} + +func testBatchControl(t testing.TB) { mockBatch := mockBatch() mockBatch.control.ODFIIdentification = "" if err := mockBatch.verify(); err != nil { @@ -383,3 +575,14 @@ func TestBatchControl(t *testing.T) { } } } + +func TestBatchControl(t *testing.T) { + testBatchControl(t) +} + +func BenchmarkBatchControl(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchControl(b) + } +} From 50693c9b37f0cc742bb0133d8d30a6d79c8de982 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 09:52:41 -0400 Subject: [PATCH 0101/1694] 165 remove github.com/go-randomdata 165 remove github.com/go-randomdata --- cmd/writeACH/main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/writeACH/main.go b/cmd/writeACH/main.go index d25496854..51a37e85c 100644 --- a/cmd/writeACH/main.go +++ b/cmd/writeACH/main.go @@ -3,7 +3,6 @@ package main import ( "flag" "fmt" - "github.com/go-randomdata" "github.com/moov-io/ach" "log" "os" @@ -68,9 +67,11 @@ func main() { entryEntrySeq.SetRDFI("231380104") entryEntrySeq.DFIAccountNumber = "81967038518" entryEntrySeq.Amount = 100000 - entryEntrySeq.IndividualName = randomdata.FullName(randomdata.RandomGender) + //entryEntrySeq.IndividualName = randomdata.FullName(randomdata.RandomGender) + entryEntrySeq.IndividualName = "Steven Tander" entryEntrySeq.SetTraceNumber(bh.ODFIIdentification, entrySeq) - entryEntrySeq.IdentificationNumber = "#" + randomdata.RandStringRunes(13) + "#" + //entryEntrySeq.IdentificationNumber = "#" + randomdata.RandStringRunes(13) + "#" + entryEntrySeq.IdentificationNumber = "#83738AB#" entryEntrySeq.Category = ach.CategoryForward // Add addenda record for an entry From 2eeeef79e001c722ccbc9ad135460c32e8d918da Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 17 May 2018 08:31:16 -0600 Subject: [PATCH 0102/1694] Updating travis to ignore /cm/d --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7f575e492..fd02cc0e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ before_install: - go get github.com/fzipp/gocyclo before_script: - - GO_FILES=$(find . -iname '*.go' -type f | grep -v /example/) # All the .go files, excluding example/ vendor/ + - GO_FILES=$(find . -iname '*.go' -type f | grep -v /example/ | grep -v /cmd/) # All the .go files, excluding example/ vendor/ # script always run to completion (set +e). All of these code checks are must haves # in a modern Go project. From 40ffbb25b101a412d6d7bc07ae6f2ea495dc1fcd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 10:38:11 -0400 Subject: [PATCH 0103/1694] update ach write and read main_test,go --- cmd/readACH/main_test.go | 6 +++--- cmd/writeACH/main_test.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/readACH/main_test.go b/cmd/readACH/main_test.go index a9eae0fd3..9e25eab8c 100644 --- a/cmd/readACH/main_test.go +++ b/cmd/readACH/main_test.go @@ -3,18 +3,18 @@ package main import "testing" func TestFileRead(t *testing.T) { - FileRead(t) + testFileRead(t) } //BenchmarkTestFileCreate benchmarks creating an ACH File func BenchmarkTestFileRead(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - FileRead(b) + testFileRead(b) } } // FileCreate creates an ACH File -func FileRead(t testing.TB) { +func testFileRead(t testing.TB) { main() } diff --git a/cmd/writeACH/main_test.go b/cmd/writeACH/main_test.go index 0bdfb8dd1..3e6e34a52 100644 --- a/cmd/writeACH/main_test.go +++ b/cmd/writeACH/main_test.go @@ -6,18 +6,18 @@ import ( // TestFileCreate tests creating an ACH File func TestFileWrite(t *testing.T) { - FileWrite(t) + testFileWrite(t) } //BenchmarkTestFileCreate benchmarks creating an ACH File func BenchmarkTestFileWrite(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - FileWrite(b) + testFileWrite(b) } } // FileCreate creates an ACH File -func FileWrite(t testing.TB) { +func testFileWrite(t testing.TB) { main() } From 11f9d54a638375af0c8fddb696078c567d20715a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 12:21:20 -0400 Subject: [PATCH 0104/1694] #166 batchCCD_test.go benchmark tests --- batchCCD_test.go | 118 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 13 deletions(-) diff --git a/batchCCD_test.go b/batchCCD_test.go index d4c96e24d..8e2d2636d 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -38,18 +38,30 @@ func mockBatchCCD() *BatchCCD { return mockBatch } -// Test Batch Header -func TestBatchCCDHeader(t *testing.T) { +// Batch CCD Header +func testBatchCCDHeader(t testing.TB) { batch, _ := NewBatch(mockBatchCCDHeader()) - _, ok := batch.(*BatchCCD) if !ok { t.Error("Expecting BatchCCD") } } -// A Batch CCD can only have one addendum per entry detail -func TestBatchCCDAddendumCount(t *testing.T) { +// Test Batch CCD Header +func TestBatchCCDHeader(t *testing.T) { + testBatchCCDHeader(t) +} + +// Benchmark Batch CCD Header +func BenchmarkBatchCCDHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDHeader(b) + } +} + +// Batch control CCD can only have one addendum per entry detail +func testBatchCCDAddendumCount(t testing.TB) { mockBatch := mockBatchCCD() // Adding a second addenda to the mock entry mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) @@ -64,8 +76,21 @@ func TestBatchCCDAddendumCount(t *testing.T) { } } -// receiving company / Individual name is a mandatory field -func TestBatchCCDReceivingCompanyName(t *testing.T) { +// Test batch control CCD can only have one addendum per entry detail +func TestBatchCCDAddendumCount(t *testing.T) { + testBatchCCDAddendumCount(t) +} + +// Benchmark batch control CCD can only have one addendum per entry detail +func BenchmarkBatchCCDAddendumCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDAddendumCount(b) + } +} + +// Receiving company / Individual name is a mandatory field +func testBatchCCDReceivingCompanyName(t testing.TB) { mockBatch := mockBatchCCD() // modify the Individual name / receiving company to nothing mockBatch.GetEntries()[0].SetReceivingCompany("") @@ -80,8 +105,21 @@ func TestBatchCCDReceivingCompanyName(t *testing.T) { } } -// verify addenda type code is 05 -func TestBatchCCDAddendaTypeCode(t *testing.T) { +// Test receiving company / Individual name is a mandatory field +func TestBatchCCDReceivingCompanyName(t *testing.T) { + testBatchCCDReceivingCompanyName(t) +} + +// Benchmark receiving company / Individual name is a mandatory field +func BenchmarkBatchCCDReceivingCompanyName(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDReceivingCompanyName(b) + } +} + +// Verify addenda type code is 05 +func testBatchCCDAddendaTypeCode(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).typeCode = "07" if err := mockBatch.Validate(); err != nil { @@ -95,8 +133,21 @@ func TestBatchCCDAddendaTypeCode(t *testing.T) { } } -// verify that the standard entry class code is CCD for batchCCD -func TestBatchCCDSEC(t *testing.T) { +// Test verify addenda type code is 05 +func TestBatchCCDAddendaTypeCode(t *testing.T) { + testBatchCCDAddendaTypeCode(t) +} + +// Benchmark verify addenda type code is 05 +func BenchmarkBatchCCDAddendaTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDAddendaTypeCode(b) + } +} + +// Verify that the standard entry class code is CCD for batchCCD +func testBatchCCDSEC(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { @@ -110,7 +161,21 @@ func TestBatchCCDSEC(t *testing.T) { } } -func TestBatchCCDAddendaCount(t *testing.T) { +// Test verify that the standard entry class code is CCD for batchCCD +func TestBatchCCDSEC(t *testing.T) { + testBatchCCDSEC(t) +} + +// Benchmark verify that the standard entry class code is CCD for batchCCD +func BenchmarkBatchCCDSEC(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDSEC(b) + } +} + +// Verify batch CCD addenda count +func testBatchCCDAddendaCount(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) mockBatch.Create() @@ -125,7 +190,21 @@ func TestBatchCCDAddendaCount(t *testing.T) { } } -func TestBatchCCDCreate(t *testing.T) { +// Test verify batch CCD addenda count +func TestBatchCCDAddendaCount(t *testing.T) { + testBatchCCDAddendaCount(t) +} + +// Benchmark verify batch CCD addenda count +func BenchmarkBatchCCDAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDAddendaCount(b) + } +} + +// Batch CCD create +func testBatchCCDCreate(t testing.TB) { mockBatch := mockBatchCCD() // Batch Header information is required to Create a batch. mockBatch.GetHeader().ServiceClassCode = 0 @@ -140,3 +219,16 @@ func TestBatchCCDCreate(t *testing.T) { } } } + +// Test batch CCD create +func TestBatchCCDCreate(t *testing.T) { + testBatchCCDCreate(t) +} + +// Benchmark batch CCD create +func BenchmarkBatchCCDCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDCreate(b) + } +} From d2f45a566f9cd6379273db01e75f892f77a4bd61 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 13:57:53 -0400 Subject: [PATCH 0105/1694] 166 Benchmark Test Functions 166 Benchmark Test Functions --- batchCCD_test.go | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/batchCCD_test.go b/batchCCD_test.go index 8e2d2636d..2c63f9220 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -38,7 +38,7 @@ func mockBatchCCD() *BatchCCD { return mockBatch } -// Batch CCD Header +// testBatchCCDHeader creates a batch CCD header func testBatchCCDHeader(t testing.TB) { batch, _ := NewBatch(mockBatchCCDHeader()) _, ok := batch.(*BatchCCD) @@ -47,12 +47,12 @@ func testBatchCCDHeader(t testing.TB) { } } -// Test Batch CCD Header +// TestBatchCCDHeader tests creating a batch CCD header func TestBatchCCDHeader(t *testing.T) { testBatchCCDHeader(t) } -// Benchmark Batch CCD Header +// BenchmarkBatchCCDHeader benchmark creating a batch CCD header func BenchmarkBatchCCDHeader(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -60,7 +60,7 @@ func BenchmarkBatchCCDHeader(b *testing.B) { } } -// Batch control CCD can only have one addendum per entry detail +// testBatchCCDAddendumCount batch control CCD can only have one addendum per entry detail func testBatchCCDAddendumCount(t testing.TB) { mockBatch := mockBatchCCD() // Adding a second addenda to the mock entry @@ -76,12 +76,12 @@ func testBatchCCDAddendumCount(t testing.TB) { } } -// Test batch control CCD can only have one addendum per entry detail +// TestBatchCCDAddendumCount tests batch control CCD can only have one addendum per entry detail func TestBatchCCDAddendumCount(t *testing.T) { testBatchCCDAddendumCount(t) } -// Benchmark batch control CCD can only have one addendum per entry detail +// BenchmarkBatchCCDAddendumCoun benchmarks batch control CCD can only have one addendum per entry detail func BenchmarkBatchCCDAddendumCount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -89,7 +89,7 @@ func BenchmarkBatchCCDAddendumCount(b *testing.B) { } } -// Receiving company / Individual name is a mandatory field +// testBatchCCDReceivingCompanyName validates Receiving company / Individual name is a mandatory field func testBatchCCDReceivingCompanyName(t testing.TB) { mockBatch := mockBatchCCD() // modify the Individual name / receiving company to nothing @@ -105,12 +105,12 @@ func testBatchCCDReceivingCompanyName(t testing.TB) { } } -// Test receiving company / Individual name is a mandatory field +// TestBatchCCDReceivingCompanyName tests validating receiving company / Individual name is a mandatory field func TestBatchCCDReceivingCompanyName(t *testing.T) { testBatchCCDReceivingCompanyName(t) } -// Benchmark receiving company / Individual name is a mandatory field +// BenchmarkBatchCCDReceivingCompanyName benchmarks validating receiving company / Individual name is a mandatory field func BenchmarkBatchCCDReceivingCompanyName(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -118,7 +118,7 @@ func BenchmarkBatchCCDReceivingCompanyName(b *testing.B) { } } -// Verify addenda type code is 05 +// testBatchCCDAddendaTypeCode verifies addenda type code is 05 func testBatchCCDAddendaTypeCode(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).typeCode = "07" @@ -133,12 +133,12 @@ func testBatchCCDAddendaTypeCode(t testing.TB) { } } -// Test verify addenda type code is 05 +// TestBatchCCDAddendaTypeCode tests verifying addenda type code is 05 func TestBatchCCDAddendaTypeCode(t *testing.T) { testBatchCCDAddendaTypeCode(t) } -// Benchmark verify addenda type code is 05 +// BenchmarkBatchCCDAddendaTypeCod benchmarks verifying addenda type code is 05 func BenchmarkBatchCCDAddendaTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -146,7 +146,7 @@ func BenchmarkBatchCCDAddendaTypeCode(b *testing.B) { } } -// Verify that the standard entry class code is CCD for batchCCD +// testBatchCCDSEC verifies that the standard entry class code is CCD for batchCCD func testBatchCCDSEC(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.header.StandardEntryClassCode = "RCK" @@ -161,12 +161,12 @@ func testBatchCCDSEC(t testing.TB) { } } -// Test verify that the standard entry class code is CCD for batchCCD +// TestBatchCCDSEC test verifying that the standard entry class code is CCD for batchCCD func TestBatchCCDSEC(t *testing.T) { testBatchCCDSEC(t) } -// Benchmark verify that the standard entry class code is CCD for batchCCD +// BenchmarkBatchCCDSEC benchmarks verifying that the standard entry class code is CCD for batchCCD func BenchmarkBatchCCDSEC(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -174,7 +174,7 @@ func BenchmarkBatchCCDSEC(b *testing.B) { } } -// Verify batch CCD addenda count +// testBatchCCDAddendaCount verifies batch CCD addenda count func testBatchCCDAddendaCount(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) @@ -190,12 +190,12 @@ func testBatchCCDAddendaCount(t testing.TB) { } } -// Test verify batch CCD addenda count +// TestBatchCCDAddendaCount tests verifying batch CCD addenda count func TestBatchCCDAddendaCount(t *testing.T) { testBatchCCDAddendaCount(t) } -// Benchmark verify batch CCD addenda count +// BenchmarkBatchCCDAddendaCount benchmarks verifying batch CCD addenda count func BenchmarkBatchCCDAddendaCount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -203,7 +203,7 @@ func BenchmarkBatchCCDAddendaCount(b *testing.B) { } } -// Batch CCD create +// testBatchCCDCreate creates a batch CCD func testBatchCCDCreate(t testing.TB) { mockBatch := mockBatchCCD() // Batch Header information is required to Create a batch. @@ -220,12 +220,12 @@ func testBatchCCDCreate(t testing.TB) { } } -// Test batch CCD create +// TestBatchCCDCreate Test creating a batch CCD func TestBatchCCDCreate(t *testing.T) { testBatchCCDCreate(t) } -// Benchmark batch CCD create +// BenchmarkBatchCCDCreate benchmark creating a batch CCD func BenchmarkBatchCCDCreate(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From 6c70529f15eb97f46863fa0c594c3c153299c139 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 14:52:49 -0400 Subject: [PATCH 0106/1694] 166 batchControl_internal_test --- batchControl_internal_test.go | 190 +++++++++++++++++++++++++++++++--- 1 file changed, 177 insertions(+), 13 deletions(-) diff --git a/batchControl_internal_test.go b/batchControl_internal_test.go index 2b85409b1..578e07085 100644 --- a/batchControl_internal_test.go +++ b/batchControl_internal_test.go @@ -17,7 +17,8 @@ func mockBatchControl() *BatchControl { return bc } -func TestMockBatchControl(t *testing.T) { +// testMockBatchControl tests mock batch control +func testMockBatchControl(t testing.TB) { bc := mockBatchControl() if err := bc.Validate(); err != nil { t.Error("mockBatchControl does not validate and will break other tests") @@ -33,8 +34,21 @@ func TestMockBatchControl(t *testing.T) { } } +// TestMockBatchControl test mock batch control +func TestMockBatchControl(t *testing.T) { + testMockBatchControl(t) +} + +// BenchmarkMockBatchControl benchmarks mock batch control +func BenchmarkMockBatchControl(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testMockBatchControl(b) + } +} + // TestParseBatchControl parses a known Batch ControlRecord string. -func TestParseBatchControl(t *testing.T) { +func testParseBatchControl(t testing.TB) { var line = "82250000010005320001000000010500000000000000origid 076401250000001" r := NewReader(strings.NewReader(line)) r.line = line @@ -85,8 +99,21 @@ func TestParseBatchControl(t *testing.T) { } } -// TestBCString validats that a known parsed file can be return to a string of the same value -func TestBCString(t *testing.T) { +// TestParseBatchControl tests parsing a known Batch ControlRecord string. +func TestParseBatchControl(t *testing.T) { + testParseBatchControl(t) +} + +// BenchmarkParseBatchControl benchmarks parsing a known Batch ControlRecord string. +func BenchmarkParseBatchControl(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseBatchControl(b) + } +} + +// TestBCString validates that a known parsed file can be return to a string of the same value +func testBCString(t testing.TB) { var line = "82250000010005320001000000010500000000000000origid 076401250000001" r := NewReader(strings.NewReader(line)) r.line = line @@ -107,8 +134,21 @@ func TestBCString(t *testing.T) { } } +// TestBCString tests validating that a known parsed file can be return to a string of the same value +func TestBCString(t *testing.T) { + testBCString(t) +} + +// BenchmarkBCString benchmarks validating that a known parsed file can be return to a string of the same value +func BenchmarkBCString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBCString(b) + } +} + // TestValidateBCRecordType ensure error if recordType is not 8 -func TestValidateBCRecordType(t *testing.T) { +func testValidateBCRecordType(t testing.TB) { bc := mockBatchControl() bc.recordType = "2" if err := bc.Validate(); err != nil { @@ -120,7 +160,21 @@ func TestValidateBCRecordType(t *testing.T) { } } -func TestBCisServiceClassErr(t *testing.T) { +// TestValidateBCRecordType tests ensuring an error if recordType is not 8 +func TestValidateBCRecordType(t *testing.T) { + testValidateBCRecordType(t) +} + +// BenchmarkValidateBCRecordType benchmarks ensuring an error if recordType is not 8 +func BenchmarkValidateBCRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateBCRecordType(b) + } +} + +// testBCisServiceClassErr verifies service class code +func testBCisServiceClassErr(t testing.TB) { bc := mockBatchControl() bc.ServiceClassCode = 123 if err := bc.Validate(); err != nil { @@ -132,7 +186,20 @@ func TestBCisServiceClassErr(t *testing.T) { } } -func TestBCBatchNumber(t *testing.T) { +// TestBCisServiceClassErr tests verifying service class code +func TestBCisServiceClassErr(t *testing.T) { + testBCisServiceClassErr(t) +} + +// BenchmarkBCisServiceClassErr benchmarks verifying service class code +func BenchmarkBCisServiceClassErr(b *testing.B) { + for i := 0; i < b.N; i++ { + testBCisServiceClassErr(b) + } +} + +// testBCBatchNumber verifies batch number +func testBCBatchNumber(t testing.TB) { bc := mockBatchControl() bc.BatchNumber = 0 if err := bc.Validate(); err != nil { @@ -144,7 +211,21 @@ func TestBCBatchNumber(t *testing.T) { } } -func TestBCCompanyIdentificationAlphaNumeric(t *testing.T) { +// TestBCBatchNumber tests verifying batch number +func TestBCBatchNumber(t *testing.T) { + testBCBatchNumber(t) +} + +// BenchmarkBCBatchNumber benchmarks verifying batch number +func BenchmarkBCBatchNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBCBatchNumber(b) + } +} + +// testBCCompanyIdentificationAlphaNumeric verifies Company Identification is AlphaNumeric +func testBCCompanyIdentificationAlphaNumeric(t testing.TB) { bc := mockBatchControl() bc.CompanyIdentification = "®" if err := bc.Validate(); err != nil { @@ -156,7 +237,21 @@ func TestBCCompanyIdentificationAlphaNumeric(t *testing.T) { } } -func TestBCMessageAuthenticationCodeAlphaNumeric(t *testing.T) { +// TestBCCompanyIdentificationAlphaNumeric tests verifying Company Identification is AlphaNumeric +func TestBCCompanyIdentificationAlphaNumeric(t *testing.T) { + testBCCompanyIdentificationAlphaNumeric(t) +} + +// BenchmarkBCCompanyIdentificationAlphaNumeric benchmarks verifying Company Identification is AlphaNumeric +func BenchmarkBCCompanyIdentificationAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBCCompanyIdentificationAlphaNumeric(b) + } +} + +// testBCMessageAuthenticationCodeAlphaNumeric verifies AuthenticationCode is AlphaNumeric +func testBCMessageAuthenticationCodeAlphaNumeric(t testing.TB) { bc := mockBatchControl() bc.MessageAuthenticationCode = "®" if err := bc.Validate(); err != nil { @@ -168,7 +263,21 @@ func TestBCMessageAuthenticationCodeAlphaNumeric(t *testing.T) { } } -func TestBCFieldInclusionRecordType(t *testing.T) { +// TestBCMessageAuthenticationCodeAlphaNumeric tests verifying AuthenticationCode is AlphaNumeric +func TestBCMessageAuthenticationCodeAlphaNumeric(t *testing.T) { + testBCMessageAuthenticationCodeAlphaNumeric(t) +} + +// TestBCMessageAuthenticationCodeAlphaNumeric benchmarks verifying AuthenticationCode is AlphaNumeric +func BenchmarkBCMessageAuthenticationCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBCMessageAuthenticationCodeAlphaNumeric(b) + } +} + +// testBCFieldInclusionRecordType verifies Record Type is included +func testBCFieldInclusionRecordType(t testing.TB) { bc := mockBatchControl() bc.recordType = "" if err := bc.Validate(); err != nil { @@ -180,7 +289,21 @@ func TestBCFieldInclusionRecordType(t *testing.T) { } } -func TestBCFieldInclusionServiceClassCode(t *testing.T) { +// TestBCFieldInclusionRecordType tests verifying Record Type is included +func TestBCFieldInclusionRecordType(t *testing.T) { + testBCFieldInclusionRecordType(t) +} + +// BenchmarkBCFieldInclusionRecordType benchmarks verifying Record Type is included +func BenchmarkBCFieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBCFieldInclusionRecordType(b) + } +} + +// testBCFieldInclusionServiceClassCode verifies Service Class Code is included +func testBCFieldInclusionServiceClassCode(t testing.TB) { bc := mockBatchControl() bc.ServiceClassCode = 0 if err := bc.Validate(); err != nil { @@ -192,7 +315,21 @@ func TestBCFieldInclusionServiceClassCode(t *testing.T) { } } -func TestBCFieldInclusionODFIIdentification(t *testing.T) { +// TestBCFieldInclusionServiceClassCode tests verifying Service Class Code is included +func TestBCFieldInclusionServiceClassCode(t *testing.T) { + testBCFieldInclusionServiceClassCode(t) +} + +// BenchmarkBCFieldInclusionServiceClassCod benchmarks verifying Service Class Code is included +func BenchmarkBCFieldInclusionServiceClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBCFieldInclusionServiceClassCode(b) + } +} + +// testBCFieldInclusionODFIIdentification verifies batch control ODFIIdentification +func testBCFieldInclusionODFIIdentification(t testing.TB) { bc := mockBatchControl() bc.ODFIIdentification = "000000000" if err := bc.Validate(); err != nil { @@ -204,10 +341,37 @@ func TestBCFieldInclusionODFIIdentification(t *testing.T) { } } -func TestBatchControlLength(t *testing.T) { +// TestBCFieldInclusionODFIIdentification tests verifying batch control ODFIIdentification +func TestBCFieldInclusionODFIIdentification(t *testing.T) { + testBCFieldInclusionODFIIdentification(t) +} + +// BenchmarkBCFieldInclusionODFIIdentification benchmarks verifying batch control ODFIIdentification +func BenchmarkBCFieldInclusionODFIIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBCFieldInclusionODFIIdentification(b) + } +} + +// testBatchControlLength verifies batch control length +func testBatchControlLength(t testing.TB) { bc := NewBatchControl() recordLength := len(bc.String()) if recordLength != 94 { t.Errorf("Instantiated length of Batch Control string is not 94 but %v", recordLength) } } + +// TestBatchControlLength tests verifying batch control length +func TestBatchControlLength(t *testing.T) { + testBatchControlLength(t) +} + +// BenchmarkBatchControlLength benchmarks verifying batch control length +func BenchmarkBatchControlLength(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchControlLength(b) + } +} From ef1d9889f26b1c20bb3493d017b8425e3870f4b7 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 14:54:48 -0400 Subject: [PATCH 0107/1694] 166 batchControl_internal_test --- batchControl_internal_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/batchControl_internal_test.go b/batchControl_internal_test.go index 578e07085..390b4c3e1 100644 --- a/batchControl_internal_test.go +++ b/batchControl_internal_test.go @@ -112,7 +112,7 @@ func BenchmarkParseBatchControl(b *testing.B) { } } -// TestBCString validates that a known parsed file can be return to a string of the same value +// testBCString validates that a known parsed file can be return to a string of the same value func testBCString(t testing.TB) { var line = "82250000010005320001000000010500000000000000origid 076401250000001" r := NewReader(strings.NewReader(line)) @@ -147,7 +147,7 @@ func BenchmarkBCString(b *testing.B) { } } -// TestValidateBCRecordType ensure error if recordType is not 8 +// testValidateBCRecordType ensure error if recordType is not 8 func testValidateBCRecordType(t testing.TB) { bc := mockBatchControl() bc.recordType = "2" @@ -268,7 +268,7 @@ func TestBCMessageAuthenticationCodeAlphaNumeric(t *testing.T) { testBCMessageAuthenticationCodeAlphaNumeric(t) } -// TestBCMessageAuthenticationCodeAlphaNumeric benchmarks verifying AuthenticationCode is AlphaNumeric +// BenchmarkBCMessageAuthenticationCodeAlphaNumeric benchmarks verifying AuthenticationCode is AlphaNumeric func BenchmarkBCMessageAuthenticationCodeAlphaNumeric(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From 5b68755feb7df780d27e751b7d28646193867224 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 15:41:14 -0400 Subject: [PATCH 0108/1694] #166 batchCOR_test.go #166 batchCOR_test.go --- batchCOR_test.go | 132 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 12 deletions(-) diff --git a/batchCOR_test.go b/batchCOR_test.go index 98e478eb7..ab8b713dd 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -40,8 +40,8 @@ func mockBatchCOR() *BatchCOR { return mockBatch } -// Test Batch Header -func TestBatchCORHeader(t *testing.T) { +// testBatchCORHeader creates a COR batch header +func testBatchCORHeader(t testing.TB) { batch, _ := NewBatch(mockBatchCORHeader()) _, ok := batch.(*BatchCOR) @@ -50,7 +50,21 @@ func TestBatchCORHeader(t *testing.T) { } } -func TestBatchCORSEC(t *testing.T) { +// TestBatchCORHeader tests creating a COR batch header +func TestBatchCORHeader(t *testing.T) { + testBatchCORHeader(t) +} + +// BenchmarkBatchCORHeader benchmarks creating a COR batch header +func BenchmarkBatchCORHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORHeader(b) + } +} + +// testBatchCORSEC verifies COR SEC code +func testBatchCORSEC(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { @@ -64,13 +78,25 @@ func TestBatchCORSEC(t *testing.T) { } } -func TestBatchCORAddendumCountTwo(t *testing.T) { +// TestBatchCORSEC tests verifying COR SEC code +func TestBatchCORSEC(t *testing.T) { + testBatchCORSEC(t) +} + +// BenchmarkBatchCORSEC benchmarks verifying COR SEC code +func BenchmarkBatchCORSEC(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORSEC(b) + } +} + +// testBatchCORAddendumCountTwo verifies addendum count of 2 +func testBatchCORAddendumCountTwo(t testing.TB) { mockBatch := mockBatchCOR() // Adding a second addenda to the mock entry mockBatch.GetEntries()[0].AddAddenda(mockAddenda98()) - if err := mockBatch.Create(); err != nil { - // fmt.Printf("err: %v \n", err) if e, ok := err.(*BatchError); ok { if e.FieldName != "Addendum" { t.Errorf("%T: %s", err, err) @@ -81,7 +107,21 @@ func TestBatchCORAddendumCountTwo(t *testing.T) { } } -func TestBatchCORAddendaCountZero(t *testing.T) { +// TestBatchCORAddendumCountTwo tests verifying addendum count of 2 +func TestBatchCORAddendumCountTwo(t *testing.T) { + testBatchCORAddendumCountTwo(t) +} + +// BenchmarkBatchCORAddendumCountTwo benchmarks verifying addendum count of 2 +func BenchmarkBatchCORAddendumCountTwo(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORAddendumCountTwo(b) + } +} + +// testBatchCORAddendaCountZero verifies addendum count of 0 +func testBatchCORAddendaCountZero(t testing.TB) { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) if err := mockBatch.Create(); err != nil { @@ -95,8 +135,21 @@ func TestBatchCORAddendaCountZero(t *testing.T) { } } -// check that Addendum is of type Addenda98 -func TestBatchCORAddendaType(t *testing.T) { +// TestBatchCORAddendaCountZero tests verifying addendum count of 0 +func TestBatchCORAddendaCountZero(t *testing.T) { + testBatchCORAddendaCountZero(t) +} + +// BenchmarkBatchCORAddendaCountZero benchmarks verifying addendum count of 0 +func BenchmarkBatchCORAddendaCountZero(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORAddendaCountZero(b) + } +} + +// testBatchCORAddendaType verifies that Addendum is of type Addenda98 +func testBatchCORAddendaType(t testing.TB) { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) @@ -111,7 +164,21 @@ func TestBatchCORAddendaType(t *testing.T) { } } -func TestBatchCORAddendaTypeCode(t *testing.T) { +// TestBatchCORAddendaType tests verifying that Addendum is of type Addenda98 +func TestBatchCORAddendaType(t *testing.T) { + testBatchCORAddendaType(t) +} + +// BenchmarkBatchCORAddendaType benchmarks verifying that Addendum is of type Addenda98 +func BenchmarkBatchCORAddendaType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORAddendaType(b) + } +} + +// testBatchCORAddendaTypeCode verifies Type Code +func testBatchCORAddendaTypeCode(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.GetEntries()[0].Addendum[0].(*Addenda98).typeCode = "07" if err := mockBatch.Validate(); err != nil { @@ -125,7 +192,21 @@ func TestBatchCORAddendaTypeCode(t *testing.T) { } } -func TestBatchCORAmount(t *testing.T) { +// TestBatchCORAddendaTypeCode tests verifying Type Code +func TestBatchCORAddendaTypeCode(t *testing.T) { + testBatchCORAddendaTypeCode(t) +} + +// BenchmarkBatchCORAddendaTypeCode benchmarks verifying Type Code +func BenchmarkBatchCORAddendaTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORAddendaTypeCode(b) + } +} + +// testBatchCORAmount verifies batch COR amount +func testBatchCORAmount(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.GetEntries()[0].Amount = 9999 if err := mockBatch.Create(); err != nil { @@ -139,7 +220,21 @@ func TestBatchCORAmount(t *testing.T) { } } -func TestBatchCORCreate(t *testing.T) { +// TestBatchCORAmount tests verifying batch COR amount +func TestBatchCORAmount(t *testing.T) { + testBatchCORAmount(t) +} + +// BenchmarkBatchCORAmount benchmarks verifying batch COR amount +func BenchmarkBatchCORAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORAmount(b) + } +} + +// testBatchCORCreate verifies creates batch COR +func testBatchCORCreate(t testing.TB) { mockBatch := mockBatchCOR() // Must have valid batch header to create a batch mockBatch.GetHeader().ServiceClassCode = 63 @@ -153,3 +248,16 @@ func TestBatchCORCreate(t *testing.T) { } } } + +// TestBatchCORCreate tests creating batch COR +func TestBatchCORCreate(t *testing.T) { + testBatchCORCreate(t) +} + +// BenchmarkBatchCORCreate benchmarks creating batch COR +func BenchmarkBatchCORCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORCreate(b) + } +} From 28269ff7cf18faa6092143803d4d676f19d4160b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 16:47:46 -0400 Subject: [PATCH 0109/1694] #166 batchHeader_internal_test #166 batchHeader_internal_test --- batchHeader_internal_test.go | 301 ++++++++++++++++++++++++++++++++--- 1 file changed, 276 insertions(+), 25 deletions(-) diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index 1926d1e8c..4a9086ab3 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -9,6 +9,7 @@ import ( "testing" ) +// mockBatchheader creates a batch header func mockBatchHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 @@ -20,7 +21,8 @@ func mockBatchHeader() *BatchHeader { return bh } -func TestMockBatchHeader(t *testing.T) { +// testMockBatchHeader creates a batch header +func testMockBatchHeader(t testing.TB) { bh := mockBatchHeader() if err := bh.Validate(); err != nil { t.Error("mockBatchHeader does not validate and will break other tests") @@ -45,8 +47,8 @@ func TestMockBatchHeader(t *testing.T) { } } -// TestParseBatchHeader parses a known Batch Header Record string. -func TestParseBatchHeader(t *testing.T) { +// testParseBatchHeader parses a known batch header record string +func testParseBatchHeader(t testing.TB) { var line = "5225companyname origid PPDCHECKPAYMT000002080730 1076401250000001" r := NewReader(strings.NewReader(line)) r.line = line @@ -96,8 +98,21 @@ func TestParseBatchHeader(t *testing.T) { } } -// TestBHString validats that a known parsed file can be return to a string of the same value -func TestBHString(t *testing.T) { +// TestParseBatchHeader tests parsing a known batch header record string +func TestParseBatchHeader(t *testing.T) { + testParseBatchHeader(t) +} + +// BenchmarkParseBatchHeader benchmarks parsing a known batch header record string +func BenchmarkParseBatchHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseBatchHeader(b) + } +} + +// testBHString validates that a known parsed file can be return to a string of the same value +func testBHString(t testing.TB) { var line = "5225companyname origid PPDCHECKPAYMT000002080730 1076401250000001" r := NewReader(strings.NewReader(line)) r.line = line @@ -111,8 +126,21 @@ func TestBHString(t *testing.T) { } } -// TestValidateBHRecordType ensure error if recordType is not 5 -func TestValidateBHRecordType(t *testing.T) { +// TestBHString tests validating that a known parsed file can be return to a string of the same value +func TestBHString(t *testing.T) { + testBHString(t) +} + +// BenchmarkBHString benchmarks validating that a known parsed file can be return to a string of the same value +func BenchmarkBHString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHString(b) + } +} + +// testValidateBHRecordType verifies error if recordType is not 5 +func testValidateBHRecordType(t testing.TB) { bh := mockBatchHeader() bh.recordType = "2" if err := bh.Validate(); err != nil { @@ -124,8 +152,21 @@ func TestValidateBHRecordType(t *testing.T) { } } -// TestInvalidServiceCode ensure error if service class is not valid -func TestInvalidServiceCode(t *testing.T) { +// TestValidateBHRecordType tests verifying error if recordType is not 5 +func TestValidateBHRecordType(t *testing.T) { + testValidateBHRecordType(t) +} + +// BenchmarkValidateBHRecordType benchmarks verifying error if recordType is not 5 +func BenchmarkValidateBHRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateBHRecordType(b) + } +} + +// testInvalidServiceCode verifies error if service code is not valid +func testInvalidServiceCode(t testing.TB) { bh := mockBatchHeader() bh.ServiceClassCode = 123 if err := bh.Validate(); err != nil { @@ -137,8 +178,22 @@ func TestInvalidServiceCode(t *testing.T) { } } -// TestValidateInvalidServiceCode ensure error if service class is not valid -func TestInvalidSECCode(t *testing.T) { +// TestInvalidServiceCode tests verifying error if service code is not valid +func TestInvalidServiceCode(t *testing.T) { + testInvalidServiceCode(t) +} + +// BenchmarkInvalidServiceCode benchmarks verifying error if service code is not valid +func BenchmarkInvalidServiceCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testInvalidServiceCode(b) + } +} + + +// testValidateInvalidSECCode verifies error if service class is not valid +func testInvalidSECCode(t testing.TB) { bh := mockBatchHeader() bh.StandardEntryClassCode = "123" if err := bh.Validate(); err != nil { @@ -150,8 +205,21 @@ func TestInvalidSECCode(t *testing.T) { } } -// TestInvalidOrigStatusCode ensure error if originator status code is not valid -func TestInvalidOrigStatusCode(t *testing.T) { +// TestInvalidSECCode tests verifying error if service class is not valid +func TestInvalidSECCode(t *testing.T) { + testInvalidSECCode(t) +} + +// BenchmarkInvalidSECCode benchmarks verifying error if service class is not valid +func BenchmarkInvalidSECCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testInvalidSECCode(b) + } +} + +// testInvalidOrigStatusCode verifies error if originator status code is not valid +func testInvalidOrigStatusCode(t testing.TB) { bh := mockBatchHeader() bh.OriginatorStatusCode = 3 if err := bh.Validate(); err != nil { @@ -163,7 +231,21 @@ func TestInvalidOrigStatusCode(t *testing.T) { } } -func TestBatchHeaderFieldInclusion(t *testing.T) { +// TestInvalidOrigStatusCode tests verifying error if originator status code is not valid +func TestInvalidOrigStatusCode(t *testing.T) { + testInvalidOrigStatusCode(t) +} + +// BenchmarkInvalidOrigStatusCode benchmarks verifying error if originator status code is not valid +func BenchmarkInvalidOrigStatusCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testInvalidOrigStatusCode(b) + } +} + +// testBatchHeaderFieldInclusion verifies batch header field inclusion +func testBatchHeaderFieldInclusion(t testing.TB) { bh := mockBatchHeader() bh.BatchNumber = 0 if err := bh.Validate(); err != nil { @@ -175,7 +257,21 @@ func TestBatchHeaderFieldInclusion(t *testing.T) { } } -func TestBatchHeaderCompanyNameAlphaNumeric(t *testing.T) { +// TestBatchHeaderFieldInclusion tests verifying batch header field inclusion +func TestBatchHeaderFieldInclusion(t *testing.T) { + testBatchHeaderFieldInclusion(t) +} + +// BenchmarkBatchHeaderFieldInclusion benchmarks verifying batch header field inclusion +func BenchmarkBatchHeaderFieldInclusion(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchHeaderFieldInclusion(b) + } +} + +// testBatchHeaderCompanyNameAlphaNumeric verifies batch header company name is alphanumeric +func testBatchHeaderCompanyNameAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyName = "AT&T®" if err := bh.Validate(); err != nil { @@ -187,7 +283,21 @@ func TestBatchHeaderCompanyNameAlphaNumeric(t *testing.T) { } } -func TestBatchCompanyDiscretionaryDataAlphaNumeric(t *testing.T) { +// TestBatchHeaderCompanyNameAlphaNumeric tests verifying batch header company name is alphanumeric +func TestBatchHeaderCompanyNameAlphaNumeric(t *testing.T) { + testBatchHeaderCompanyNameAlphaNumeric(t) +} + +// BenchmarkBatchHeaderCompanyNameAlphaNumeric benchmarks verifying batch header company name is alphanumeric +func BenchmarkBatchHeaderCompanyNameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchHeaderCompanyNameAlphaNumeric(b) + } +} + +// testBatchCompanyDiscretionaryDataAlphaNumeric verifies company discretionary data is alphanumeric +func testBatchCompanyDiscretionaryDataAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyDiscretionaryData = "®" if err := bh.Validate(); err != nil { @@ -199,7 +309,21 @@ func TestBatchCompanyDiscretionaryDataAlphaNumeric(t *testing.T) { } } -func TestBatchCompanyIdentificationAlphaNumeric(t *testing.T) { +// TestBatchCompanyDiscretionaryDataAlphaNumeric tests verifying company discretionary data is alphanumeric +func TestBatchCompanyDiscretionaryDataAlphaNumeric(t *testing.T) { + testBatchCompanyDiscretionaryDataAlphaNumeric(t) +} + +// BenchmarkBatchCompanyDiscretionaryDataAlphaNumeric benchmarks verifying company discretionary data is alphanumeric +func BenchmarkBatchCompanyDiscretionaryDataAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCompanyDiscretionaryDataAlphaNumeric(b) + } +} + +// testBatchCompanyIdentificationAlphaNumeric verifies company identification is alphanumeric +func testBatchCompanyIdentificationAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyIdentification = "®" if err := bh.Validate(); err != nil { @@ -211,7 +335,21 @@ func TestBatchCompanyIdentificationAlphaNumeric(t *testing.T) { } } -func TestBatchCompanyEntryDescriptionAlphaNumeric(t *testing.T) { +// TestBatchCompanyIdentificationAlphaNumeric tests verifying company identification is alphanumeric +func TestBatchCompanyIdentificationAlphaNumeric(t *testing.T) { + testBatchCompanyIdentificationAlphaNumeric(t) +} + +// BenchmarkBatchCompanyIdentificationAlphaNumeric benchmarks verifying company identification is alphanumeric +func BenchmarkBatchCompanyIdentificationAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCompanyIdentificationAlphaNumeric(b) + } +} + +// testBatchCompanyEntryDescriptionAlphaNumeric verifies company entry description is alphanumeric +func testBatchCompanyEntryDescriptionAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyEntryDescription = "P®YROLL" if err := bh.Validate(); err != nil { @@ -223,7 +361,21 @@ func TestBatchCompanyEntryDescriptionAlphaNumeric(t *testing.T) { } } -func TestBHFieldInclusionRecordType(t *testing.T) { +// TestBatchCompanyEntryDescriptionAlphaNumeric tests verifying company entry description is alphanumeric +func TestBatchCompanyEntryDescriptionAlphaNumeric(t *testing.T) { + testBatchCompanyEntryDescriptionAlphaNumeric(t) +} + +// BenchmarkBatchCompanyEntryDescriptionAlphaNumeric benchmarks verifying company entry description is alphanumeric +func BenchmarkBatchCompanyEntryDescriptionAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCompanyEntryDescriptionAlphaNumeric(b) + } +} + +// testBHFieldInclusionRecordType verifies record type field inclusion +func testBHFieldInclusionRecordType(t testing.TB) { bh := mockBatchHeader() bh.recordType = "" if err := bh.Validate(); err != nil { @@ -235,7 +387,21 @@ func TestBHFieldInclusionRecordType(t *testing.T) { } } -func TestBHFieldInclusionCompanyName(t *testing.T) { +// TestBHFieldInclusionRecordType tests verifying record type field inclusion +func TestBHFieldInclusionRecordType(t *testing.T) { + testBHFieldInclusionRecordType(t) +} + +// BenchmarkBHFieldInclusionRecordType benchmarks verifying record type field inclusion +func BenchmarkBHFieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHFieldInclusionRecordType(b) + } +} + +// testBHFieldInclusionCompanyName verifies company name field inclusion +func testBHFieldInclusionCompanyName(t testing.TB) { bh := mockBatchHeader() bh.CompanyName = "" if err := bh.Validate(); err != nil { @@ -247,7 +413,21 @@ func TestBHFieldInclusionCompanyName(t *testing.T) { } } -func TestBHFieldInclusionCompanyIdentification(t *testing.T) { +// TestBHFieldInclusionCompanyName tests verifying company name field inclusion +func TestBHFieldInclusionCompanyName(t *testing.T) { + testBHFieldInclusionCompanyName(t) +} + +// BenchmarkBHFieldInclusionCompanyName benchmarks verifying company name field inclusion +func BenchmarkBHFieldInclusionCompanyName(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHFieldInclusionCompanyName(b) + } +} + +// testBHFieldInclusionCompanyIdentification verifies company identification field inclusion +func testBHFieldInclusionCompanyIdentification(t testing.TB) { bh := mockBatchHeader() bh.CompanyIdentification = "" if err := bh.Validate(); err != nil { @@ -259,7 +439,21 @@ func TestBHFieldInclusionCompanyIdentification(t *testing.T) { } } -func TestBHFieldInclusionStandardEntryClassCode(t *testing.T) { +// TestBHFieldInclusionCompanyIdentification tests verifying company identification field inclusion +func TestBHFieldInclusionCompanyIdentification(t *testing.T) { + testBHFieldInclusionCompanyIdentification(t) +} + +// BenchmarkBHFieldInclusionCompanyIdentification benchmarks verifying company identification field inclusion +func BenchmarkBHFieldInclusionCompanyIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHFieldInclusionCompanyIdentification(b) + } +} + +// testBHFieldInclusionStandardEntryClassCode verifies SEC Code field inclusion +func testBHFieldInclusionStandardEntryClassCode(t testing.TB) { bh := mockBatchHeader() bh.StandardEntryClassCode = "" if err := bh.Validate(); err != nil { @@ -271,7 +465,21 @@ func TestBHFieldInclusionStandardEntryClassCode(t *testing.T) { } } -func TestBHFieldInclusionCompanyEntryDescription(t *testing.T) { +// TestBHFieldInclusionStandardEntryClassCode tests verifying SEC Code field inclusion +func TestBHFieldInclusionStandardEntryClassCode(t *testing.T) { + testBHFieldInclusionStandardEntryClassCode(t) +} + +// BenchmarkBHFieldInclusionStandardEntryClassCode benchmarks verifying SEC Code field inclusion +func BenchmarkBHFieldInclusionStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHFieldInclusionStandardEntryClassCode(b) + } +} + +// testBHFieldInclusionCompanyEntryDescription verifies Company Entry Description field inclusion +func testBHFieldInclusionCompanyEntryDescription(t testing.TB) { bh := mockBatchHeader() bh.CompanyEntryDescription = "" if err := bh.Validate(); err != nil { @@ -283,7 +491,22 @@ func TestBHFieldInclusionCompanyEntryDescription(t *testing.T) { } } -func TestBHFieldInclusionOriginatorStatusCode(t *testing.T) { +// TestBHFieldInclusionCompanyEntryDescription tests verifying Company Entry Description field inclusion +func Test(t *testing.T) { + testBHFieldInclusionCompanyEntryDescription(t) +} + +// BenchmarkBHFieldInclusionCompanyEntryDescription benchmarks verifying Company Entry Description field inclusion +func BenchmarkBHFieldInclusionCompanyEntryDescription(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHFieldInclusionCompanyEntryDescription(b) + } +} + + +// testBHFieldInclusionOriginatorStatusCode verifies Originator Status Code field inclusion +func testBHFieldInclusionOriginatorStatusCode(t testing.TB) { bh := mockBatchHeader() bh.OriginatorStatusCode = 0 if err := bh.Validate(); err != nil { @@ -295,7 +518,22 @@ func TestBHFieldInclusionOriginatorStatusCode(t *testing.T) { } } -func TestBHFieldInclusionODFIIdentification(t *testing.T) { +// TestBHFieldInclusionOriginatorStatusCode tests verifying Originator Status Code field inclusion +func TestBHFieldInclusionOriginatorStatusCode(t *testing.T) { + testBHFieldInclusionOriginatorStatusCode(t) +} + +// BenchmarkBHFieldInclusionOriginatorStatusCode benchmarks verifying Originator Status Code field inclusion +func BenchmarkBHFieldInclusionOriginatorStatusCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHFieldInclusionOriginatorStatusCode(b) + } +} + + +// testBHFieldInclusionODFIIdentification verifies ODFIIdentification field inclusion +func testBHFieldInclusionODFIIdentification(t testing.TB) { bh := mockBatchHeader() bh.ODFIIdentification = "" if err := bh.Validate(); err != nil { @@ -306,3 +544,16 @@ func TestBHFieldInclusionODFIIdentification(t *testing.T) { } } } + +// TestBHFieldInclusionODFIIdentification tests verifying ODFIIdentification field inclusion +func TestBHFieldInclusionODFIIdentification(t *testing.T) { + testBHFieldInclusionODFIIdentification(t) +} + +// BenchmarkBHFieldInclusionODFIIdentification benchmarks verifying ODFIIdentification field inclusion +func BenchmarkBHFieldInclusionODFIIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBHFieldInclusionODFIIdentification(b) + } +} \ No newline at end of file From aa262c052f3bdfca57463390a1950f2547f61be2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 17:07:20 -0400 Subject: [PATCH 0110/1694] #166 batchHeader_internal_test benchmark #166 batchHeader_internal_test benchmark --- batchHeader_internal_test.go | 109 ++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index 4a9086ab3..86bc47f4d 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -47,6 +47,19 @@ func testMockBatchHeader(t testing.TB) { } } +// TestMockBatchHeader tests creating a batch header +func TestMockBatchHeader(t *testing.T) { + testMockBatchHeader(t) +} + +// BenchmarkMockBatchHeader benchmarks creating a batch header +func BenchmarkMockBatchHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testMockBatchHeader(b) + } +} + // testParseBatchHeader parses a known batch header record string func testParseBatchHeader(t testing.TB) { var line = "5225companyname origid PPDCHECKPAYMT000002080730 1076401250000001" @@ -139,7 +152,7 @@ func BenchmarkBHString(b *testing.B) { } } -// testValidateBHRecordType verifies error if recordType is not 5 +// testValidateBHRecordType validates error if recordType is not 5 func testValidateBHRecordType(t testing.TB) { bh := mockBatchHeader() bh.recordType = "2" @@ -152,12 +165,12 @@ func testValidateBHRecordType(t testing.TB) { } } -// TestValidateBHRecordType tests verifying error if recordType is not 5 +// TestValidateBHRecordType tests validating error if recordType is not 5 func TestValidateBHRecordType(t *testing.T) { testValidateBHRecordType(t) } -// BenchmarkValidateBHRecordType benchmarks verifying error if recordType is not 5 +// BenchmarkValidateBHRecordType benchmarks validating error if recordType is not 5 func BenchmarkValidateBHRecordType(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -165,7 +178,7 @@ func BenchmarkValidateBHRecordType(b *testing.B) { } } -// testInvalidServiceCode verifies error if service code is not valid +// testInvalidServiceCode validates error if service code is not valid func testInvalidServiceCode(t testing.TB) { bh := mockBatchHeader() bh.ServiceClassCode = 123 @@ -178,12 +191,12 @@ func testInvalidServiceCode(t testing.TB) { } } -// TestInvalidServiceCode tests verifying error if service code is not valid +// TestInvalidServiceCode tests validating error if service code is not valid func TestInvalidServiceCode(t *testing.T) { testInvalidServiceCode(t) } -// BenchmarkInvalidServiceCode benchmarks verifying error if service code is not valid +// BenchmarkInvalidServiceCode benchmarks validating error if service code is not valid func BenchmarkInvalidServiceCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -192,7 +205,7 @@ func BenchmarkInvalidServiceCode(b *testing.B) { } -// testValidateInvalidSECCode verifies error if service class is not valid +// testValidateInvalidSECCode validates error if service class is not valid func testInvalidSECCode(t testing.TB) { bh := mockBatchHeader() bh.StandardEntryClassCode = "123" @@ -205,12 +218,12 @@ func testInvalidSECCode(t testing.TB) { } } -// TestInvalidSECCode tests verifying error if service class is not valid +// TestInvalidSECCode tests validating error if service class is not valid func TestInvalidSECCode(t *testing.T) { testInvalidSECCode(t) } -// BenchmarkInvalidSECCode benchmarks verifying error if service class is not valid +// BenchmarkInvalidSECCode benchmarks validating error if service class is not valid func BenchmarkInvalidSECCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -218,7 +231,7 @@ func BenchmarkInvalidSECCode(b *testing.B) { } } -// testInvalidOrigStatusCode verifies error if originator status code is not valid +// testInvalidOrigStatusCode validates error if originator status code is not valid func testInvalidOrigStatusCode(t testing.TB) { bh := mockBatchHeader() bh.OriginatorStatusCode = 3 @@ -231,12 +244,12 @@ func testInvalidOrigStatusCode(t testing.TB) { } } -// TestInvalidOrigStatusCode tests verifying error if originator status code is not valid +// TestInvalidOrigStatusCode tests validating error if originator status code is not valid func TestInvalidOrigStatusCode(t *testing.T) { testInvalidOrigStatusCode(t) } -// BenchmarkInvalidOrigStatusCode benchmarks verifying error if originator status code is not valid +// BenchmarkInvalidOrigStatusCode benchmarks validating error if originator status code is not valid func BenchmarkInvalidOrigStatusCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -244,7 +257,7 @@ func BenchmarkInvalidOrigStatusCode(b *testing.B) { } } -// testBatchHeaderFieldInclusion verifies batch header field inclusion +// testBatchHeaderFieldInclusion validates batch header field inclusion func testBatchHeaderFieldInclusion(t testing.TB) { bh := mockBatchHeader() bh.BatchNumber = 0 @@ -257,12 +270,12 @@ func testBatchHeaderFieldInclusion(t testing.TB) { } } -// TestBatchHeaderFieldInclusion tests verifying batch header field inclusion +// TestBatchHeaderFieldInclusion tests validating batch header field inclusion func TestBatchHeaderFieldInclusion(t *testing.T) { testBatchHeaderFieldInclusion(t) } -// BenchmarkBatchHeaderFieldInclusion benchmarks verifying batch header field inclusion +// BenchmarkBatchHeaderFieldInclusion benchmarks validating batch header field inclusion func BenchmarkBatchHeaderFieldInclusion(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -270,7 +283,7 @@ func BenchmarkBatchHeaderFieldInclusion(b *testing.B) { } } -// testBatchHeaderCompanyNameAlphaNumeric verifies batch header company name is alphanumeric +// testBatchHeaderCompanyNameAlphaNumeric validates batch header company name is alphanumeric func testBatchHeaderCompanyNameAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyName = "AT&T®" @@ -283,12 +296,12 @@ func testBatchHeaderCompanyNameAlphaNumeric(t testing.TB) { } } -// TestBatchHeaderCompanyNameAlphaNumeric tests verifying batch header company name is alphanumeric +// TestBatchHeaderCompanyNameAlphaNumeric tests validating batch header company name is alphanumeric func TestBatchHeaderCompanyNameAlphaNumeric(t *testing.T) { testBatchHeaderCompanyNameAlphaNumeric(t) } -// BenchmarkBatchHeaderCompanyNameAlphaNumeric benchmarks verifying batch header company name is alphanumeric +// BenchmarkBatchHeaderCompanyNameAlphaNumeric benchmarks validating batch header company name is alphanumeric func BenchmarkBatchHeaderCompanyNameAlphaNumeric(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -296,7 +309,7 @@ func BenchmarkBatchHeaderCompanyNameAlphaNumeric(b *testing.B) { } } -// testBatchCompanyDiscretionaryDataAlphaNumeric verifies company discretionary data is alphanumeric +// testBatchCompanyDiscretionaryDataAlphaNumeric validates company discretionary data is alphanumeric func testBatchCompanyDiscretionaryDataAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyDiscretionaryData = "®" @@ -309,12 +322,12 @@ func testBatchCompanyDiscretionaryDataAlphaNumeric(t testing.TB) { } } -// TestBatchCompanyDiscretionaryDataAlphaNumeric tests verifying company discretionary data is alphanumeric +// TestBatchCompanyDiscretionaryDataAlphaNumeric tests validating company discretionary data is alphanumeric func TestBatchCompanyDiscretionaryDataAlphaNumeric(t *testing.T) { testBatchCompanyDiscretionaryDataAlphaNumeric(t) } -// BenchmarkBatchCompanyDiscretionaryDataAlphaNumeric benchmarks verifying company discretionary data is alphanumeric +// BenchmarkBatchCompanyDiscretionaryDataAlphaNumeric benchmarks validating company discretionary data is alphanumeric func BenchmarkBatchCompanyDiscretionaryDataAlphaNumeric(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -322,7 +335,7 @@ func BenchmarkBatchCompanyDiscretionaryDataAlphaNumeric(b *testing.B) { } } -// testBatchCompanyIdentificationAlphaNumeric verifies company identification is alphanumeric +// testBatchCompanyIdentificationAlphaNumeric validates company identification is alphanumeric func testBatchCompanyIdentificationAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyIdentification = "®" @@ -335,12 +348,12 @@ func testBatchCompanyIdentificationAlphaNumeric(t testing.TB) { } } -// TestBatchCompanyIdentificationAlphaNumeric tests verifying company identification is alphanumeric +// TestBatchCompanyIdentificationAlphaNumeric tests validating company identification is alphanumeric func TestBatchCompanyIdentificationAlphaNumeric(t *testing.T) { testBatchCompanyIdentificationAlphaNumeric(t) } -// BenchmarkBatchCompanyIdentificationAlphaNumeric benchmarks verifying company identification is alphanumeric +// BenchmarkBatchCompanyIdentificationAlphaNumeric benchmarks validating company identification is alphanumeric func BenchmarkBatchCompanyIdentificationAlphaNumeric(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -348,7 +361,7 @@ func BenchmarkBatchCompanyIdentificationAlphaNumeric(b *testing.B) { } } -// testBatchCompanyEntryDescriptionAlphaNumeric verifies company entry description is alphanumeric +// testBatchCompanyEntryDescriptionAlphaNumeric validates company entry description is alphanumeric func testBatchCompanyEntryDescriptionAlphaNumeric(t testing.TB) { bh := mockBatchHeader() bh.CompanyEntryDescription = "P®YROLL" @@ -361,12 +374,12 @@ func testBatchCompanyEntryDescriptionAlphaNumeric(t testing.TB) { } } -// TestBatchCompanyEntryDescriptionAlphaNumeric tests verifying company entry description is alphanumeric +// TestBatchCompanyEntryDescriptionAlphaNumeric tests validating company entry description is alphanumeric func TestBatchCompanyEntryDescriptionAlphaNumeric(t *testing.T) { testBatchCompanyEntryDescriptionAlphaNumeric(t) } -// BenchmarkBatchCompanyEntryDescriptionAlphaNumeric benchmarks verifying company entry description is alphanumeric +// BenchmarkBatchCompanyEntryDescriptionAlphaNumeric benchmarks validating company entry description is alphanumeric func BenchmarkBatchCompanyEntryDescriptionAlphaNumeric(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -374,7 +387,7 @@ func BenchmarkBatchCompanyEntryDescriptionAlphaNumeric(b *testing.B) { } } -// testBHFieldInclusionRecordType verifies record type field inclusion +// testBHFieldInclusionRecordType validates record type field inclusion func testBHFieldInclusionRecordType(t testing.TB) { bh := mockBatchHeader() bh.recordType = "" @@ -387,12 +400,12 @@ func testBHFieldInclusionRecordType(t testing.TB) { } } -// TestBHFieldInclusionRecordType tests verifying record type field inclusion +// TestBHFieldInclusionRecordType tests validating record type field inclusion func TestBHFieldInclusionRecordType(t *testing.T) { testBHFieldInclusionRecordType(t) } -// BenchmarkBHFieldInclusionRecordType benchmarks verifying record type field inclusion +// BenchmarkBHFieldInclusionRecordType benchmarks validating record type field inclusion func BenchmarkBHFieldInclusionRecordType(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -400,7 +413,7 @@ func BenchmarkBHFieldInclusionRecordType(b *testing.B) { } } -// testBHFieldInclusionCompanyName verifies company name field inclusion +// testBHFieldInclusionCompanyName validates company name field inclusion func testBHFieldInclusionCompanyName(t testing.TB) { bh := mockBatchHeader() bh.CompanyName = "" @@ -413,12 +426,12 @@ func testBHFieldInclusionCompanyName(t testing.TB) { } } -// TestBHFieldInclusionCompanyName tests verifying company name field inclusion +// TestBHFieldInclusionCompanyName tests validating company name field inclusion func TestBHFieldInclusionCompanyName(t *testing.T) { testBHFieldInclusionCompanyName(t) } -// BenchmarkBHFieldInclusionCompanyName benchmarks verifying company name field inclusion +// BenchmarkBHFieldInclusionCompanyName benchmarks validating company name field inclusion func BenchmarkBHFieldInclusionCompanyName(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -426,7 +439,7 @@ func BenchmarkBHFieldInclusionCompanyName(b *testing.B) { } } -// testBHFieldInclusionCompanyIdentification verifies company identification field inclusion +// testBHFieldInclusionCompanyIdentification validates company identification field inclusion func testBHFieldInclusionCompanyIdentification(t testing.TB) { bh := mockBatchHeader() bh.CompanyIdentification = "" @@ -439,12 +452,12 @@ func testBHFieldInclusionCompanyIdentification(t testing.TB) { } } -// TestBHFieldInclusionCompanyIdentification tests verifying company identification field inclusion +// TestBHFieldInclusionCompanyIdentification tests validating company identification field inclusion func TestBHFieldInclusionCompanyIdentification(t *testing.T) { testBHFieldInclusionCompanyIdentification(t) } -// BenchmarkBHFieldInclusionCompanyIdentification benchmarks verifying company identification field inclusion +// BenchmarkBHFieldInclusionCompanyIdentification benchmarks validating company identification field inclusion func BenchmarkBHFieldInclusionCompanyIdentification(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -452,7 +465,7 @@ func BenchmarkBHFieldInclusionCompanyIdentification(b *testing.B) { } } -// testBHFieldInclusionStandardEntryClassCode verifies SEC Code field inclusion +// testBHFieldInclusionStandardEntryClassCode validates SEC Code field inclusion func testBHFieldInclusionStandardEntryClassCode(t testing.TB) { bh := mockBatchHeader() bh.StandardEntryClassCode = "" @@ -465,12 +478,12 @@ func testBHFieldInclusionStandardEntryClassCode(t testing.TB) { } } -// TestBHFieldInclusionStandardEntryClassCode tests verifying SEC Code field inclusion +// TestBHFieldInclusionStandardEntryClassCode tests validating SEC Code field inclusion func TestBHFieldInclusionStandardEntryClassCode(t *testing.T) { testBHFieldInclusionStandardEntryClassCode(t) } -// BenchmarkBHFieldInclusionStandardEntryClassCode benchmarks verifying SEC Code field inclusion +// BenchmarkBHFieldInclusionStandardEntryClassCode benchmarks validating SEC Code field inclusion func BenchmarkBHFieldInclusionStandardEntryClassCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -478,7 +491,7 @@ func BenchmarkBHFieldInclusionStandardEntryClassCode(b *testing.B) { } } -// testBHFieldInclusionCompanyEntryDescription verifies Company Entry Description field inclusion +// testBHFieldInclusionCompanyEntryDescription validates Company Entry Description field inclusion func testBHFieldInclusionCompanyEntryDescription(t testing.TB) { bh := mockBatchHeader() bh.CompanyEntryDescription = "" @@ -491,12 +504,12 @@ func testBHFieldInclusionCompanyEntryDescription(t testing.TB) { } } -// TestBHFieldInclusionCompanyEntryDescription tests verifying Company Entry Description field inclusion +// TestBHFieldInclusionCompanyEntryDescription tests validating Company Entry Description field inclusion func Test(t *testing.T) { testBHFieldInclusionCompanyEntryDescription(t) } -// BenchmarkBHFieldInclusionCompanyEntryDescription benchmarks verifying Company Entry Description field inclusion +// BenchmarkBHFieldInclusionCompanyEntryDescription benchmarks validating Company Entry Description field inclusion func BenchmarkBHFieldInclusionCompanyEntryDescription(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -505,7 +518,7 @@ func BenchmarkBHFieldInclusionCompanyEntryDescription(b *testing.B) { } -// testBHFieldInclusionOriginatorStatusCode verifies Originator Status Code field inclusion +// testBHFieldInclusionOriginatorStatusCode validates Originator Status Code field inclusion func testBHFieldInclusionOriginatorStatusCode(t testing.TB) { bh := mockBatchHeader() bh.OriginatorStatusCode = 0 @@ -518,12 +531,12 @@ func testBHFieldInclusionOriginatorStatusCode(t testing.TB) { } } -// TestBHFieldInclusionOriginatorStatusCode tests verifying Originator Status Code field inclusion +// TestBHFieldInclusionOriginatorStatusCode tests validating Originator Status Code field inclusion func TestBHFieldInclusionOriginatorStatusCode(t *testing.T) { testBHFieldInclusionOriginatorStatusCode(t) } -// BenchmarkBHFieldInclusionOriginatorStatusCode benchmarks verifying Originator Status Code field inclusion +// BenchmarkBHFieldInclusionOriginatorStatusCode benchmarks validating Originator Status Code field inclusion func BenchmarkBHFieldInclusionOriginatorStatusCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -532,7 +545,7 @@ func BenchmarkBHFieldInclusionOriginatorStatusCode(b *testing.B) { } -// testBHFieldInclusionODFIIdentification verifies ODFIIdentification field inclusion +// testBHFieldInclusionODFIIdentification validates ODFIIdentification field inclusion func testBHFieldInclusionODFIIdentification(t testing.TB) { bh := mockBatchHeader() bh.ODFIIdentification = "" @@ -545,12 +558,12 @@ func testBHFieldInclusionODFIIdentification(t testing.TB) { } } -// TestBHFieldInclusionODFIIdentification tests verifying ODFIIdentification field inclusion +// TestBHFieldInclusionODFIIdentification tests validating ODFIIdentification field inclusion func TestBHFieldInclusionODFIIdentification(t *testing.T) { testBHFieldInclusionODFIIdentification(t) } -// BenchmarkBHFieldInclusionODFIIdentification benchmarks verifying ODFIIdentification field inclusion +// BenchmarkBHFieldInclusionODFIIdentification benchmarks validating ODFIIdentification field inclusion func BenchmarkBHFieldInclusionODFIIdentification(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From aa092fd09ce1557a04278e8166ebb36190112257 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 17:16:06 -0400 Subject: [PATCH 0111/1694] #166 batchCOR_test #166 batchCOR_test --- batchCOR_test.go | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/batchCOR_test.go b/batchCOR_test.go index ab8b713dd..20da9d8c3 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -6,6 +6,7 @@ import ( // TODO make all the mock values cor fields +// mockBatchCORHeader creates a COR batch header func mockBatchCORHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 @@ -17,6 +18,7 @@ func mockBatchCORHeader() *BatchHeader { return bh } +// mockCOREntryDetail creates a COR entry detail func mockCOREntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 @@ -30,6 +32,7 @@ func mockCOREntryDetail() *EntryDetail { return entry } +// mockBatchCOR creates a COR batch func mockBatchCOR() *BatchCOR { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) @@ -63,7 +66,7 @@ func BenchmarkBatchCORHeader(b *testing.B) { } } -// testBatchCORSEC verifies COR SEC code +// testBatchCORSEC validates COR SEC code func testBatchCORSEC(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.header.StandardEntryClassCode = "RCK" @@ -78,12 +81,12 @@ func testBatchCORSEC(t testing.TB) { } } -// TestBatchCORSEC tests verifying COR SEC code +// TestBatchCORSEC tests validating COR SEC code func TestBatchCORSEC(t *testing.T) { testBatchCORSEC(t) } -// BenchmarkBatchCORSEC benchmarks verifying COR SEC code +// BenchmarkBatchCORSEC benchmarks validating COR SEC code func BenchmarkBatchCORSEC(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -91,7 +94,7 @@ func BenchmarkBatchCORSEC(b *testing.B) { } } -// testBatchCORAddendumCountTwo verifies addendum count of 2 +// testBatchCORAddendumCountTwo validates addendum count of 2 func testBatchCORAddendumCountTwo(t testing.TB) { mockBatch := mockBatchCOR() // Adding a second addenda to the mock entry @@ -107,12 +110,12 @@ func testBatchCORAddendumCountTwo(t testing.TB) { } } -// TestBatchCORAddendumCountTwo tests verifying addendum count of 2 +// TestBatchCORAddendumCountTwo tests validating addendum count of 2 func TestBatchCORAddendumCountTwo(t *testing.T) { testBatchCORAddendumCountTwo(t) } -// BenchmarkBatchCORAddendumCountTwo benchmarks verifying addendum count of 2 +// BenchmarkBatchCORAddendumCountTwo benchmarks validating addendum count of 2 func BenchmarkBatchCORAddendumCountTwo(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -120,7 +123,7 @@ func BenchmarkBatchCORAddendumCountTwo(b *testing.B) { } } -// testBatchCORAddendaCountZero verifies addendum count of 0 +// testBatchCORAddendaCountZero validates addendum count of 0 func testBatchCORAddendaCountZero(t testing.TB) { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) @@ -135,12 +138,12 @@ func testBatchCORAddendaCountZero(t testing.TB) { } } -// TestBatchCORAddendaCountZero tests verifying addendum count of 0 +// TestBatchCORAddendaCountZero tests validating addendum count of 0 func TestBatchCORAddendaCountZero(t *testing.T) { testBatchCORAddendaCountZero(t) } -// BenchmarkBatchCORAddendaCountZero benchmarks verifying addendum count of 0 +// BenchmarkBatchCORAddendaCountZero benchmarks validating addendum count of 0 func BenchmarkBatchCORAddendaCountZero(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -148,7 +151,7 @@ func BenchmarkBatchCORAddendaCountZero(b *testing.B) { } } -// testBatchCORAddendaType verifies that Addendum is of type Addenda98 +// testBatchCORAddendaType validates that Addendum is of type Addenda98 func testBatchCORAddendaType(t testing.TB) { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) @@ -164,12 +167,12 @@ func testBatchCORAddendaType(t testing.TB) { } } -// TestBatchCORAddendaType tests verifying that Addendum is of type Addenda98 +// TestBatchCORAddendaType tests validating that Addendum is of type Addenda98 func TestBatchCORAddendaType(t *testing.T) { testBatchCORAddendaType(t) } -// BenchmarkBatchCORAddendaType benchmarks verifying that Addendum is of type Addenda98 +// BenchmarkBatchCORAddendaType benchmarks validating that Addendum is of type Addenda98 func BenchmarkBatchCORAddendaType(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -177,7 +180,7 @@ func BenchmarkBatchCORAddendaType(b *testing.B) { } } -// testBatchCORAddendaTypeCode verifies Type Code +// testBatchCORAddendaTypeCode validates Type Code func testBatchCORAddendaTypeCode(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.GetEntries()[0].Addendum[0].(*Addenda98).typeCode = "07" @@ -192,12 +195,12 @@ func testBatchCORAddendaTypeCode(t testing.TB) { } } -// TestBatchCORAddendaTypeCode tests verifying Type Code +// TestBatchCORAddendaTypeCode tests validating Type Code func TestBatchCORAddendaTypeCode(t *testing.T) { testBatchCORAddendaTypeCode(t) } -// BenchmarkBatchCORAddendaTypeCode benchmarks verifying Type Code +// BenchmarkBatchCORAddendaTypeCode benchmarks validating Type Code func BenchmarkBatchCORAddendaTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -205,7 +208,7 @@ func BenchmarkBatchCORAddendaTypeCode(b *testing.B) { } } -// testBatchCORAmount verifies batch COR amount +// testBatchCORAmount validates batch COR amount func testBatchCORAmount(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.GetEntries()[0].Amount = 9999 @@ -220,12 +223,12 @@ func testBatchCORAmount(t testing.TB) { } } -// TestBatchCORAmount tests verifying batch COR amount +// TestBatchCORAmount tests validating batch COR amount func TestBatchCORAmount(t *testing.T) { testBatchCORAmount(t) } -// BenchmarkBatchCORAmount benchmarks verifying batch COR amount +// BenchmarkBatchCORAmount benchmarks validating batch COR amount func BenchmarkBatchCORAmount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -233,7 +236,7 @@ func BenchmarkBatchCORAmount(b *testing.B) { } } -// testBatchCORCreate verifies creates batch COR +// testBatchCORCreate creates batch COR func testBatchCORCreate(t testing.TB) { mockBatch := mockBatchCOR() // Must have valid batch header to create a batch @@ -260,4 +263,4 @@ func BenchmarkBatchCORCreate(b *testing.B) { for i := 0; i < b.N; i++ { testBatchCORCreate(b) } -} +} \ No newline at end of file From d7a58ccc96b48dc2f24bb37684c8401afab16960 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 17 May 2018 17:22:10 -0400 Subject: [PATCH 0112/1694] #166 batchCCD_test benchmark #166 batchCCD_test benchmark --- batchCCD_test.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/batchCCD_test.go b/batchCCD_test.go index 2c63f9220..1cd991188 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -4,6 +4,7 @@ import ( "testing" ) +// mockBatchCCDHeader creates a CCD batch header func mockBatchCCDHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 @@ -15,6 +16,7 @@ func mockBatchCCDHeader() *BatchHeader { return bh } +// mockCCDEntryDetail creates a CCD entry detail func mockCCDEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 @@ -28,6 +30,7 @@ func mockCCDEntryDetail() *EntryDetail { return entry } +// mockBatchCCD creates a CCD batch func mockBatchCCD() *BatchCCD { mockBatch := NewBatchCCD(mockBatchCCDHeader()) mockBatch.AddEntry(mockCCDEntryDetail()) @@ -38,7 +41,7 @@ func mockBatchCCD() *BatchCCD { return mockBatch } -// testBatchCCDHeader creates a batch CCD header +// testBatchCCDHeader creates a CCD batch header func testBatchCCDHeader(t testing.TB) { batch, _ := NewBatch(mockBatchCCDHeader()) _, ok := batch.(*BatchCCD) @@ -47,12 +50,12 @@ func testBatchCCDHeader(t testing.TB) { } } -// TestBatchCCDHeader tests creating a batch CCD header +// TestBatchCCDHeader tests creating a CCD batch header func TestBatchCCDHeader(t *testing.T) { testBatchCCDHeader(t) } -// BenchmarkBatchCCDHeader benchmark creating a batch CCD header +// BenchmarkBatchCCDHeader benchmark creating a CCD batch header func BenchmarkBatchCCDHeader(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -118,7 +121,7 @@ func BenchmarkBatchCCDReceivingCompanyName(b *testing.B) { } } -// testBatchCCDAddendaTypeCode verifies addenda type code is 05 +// testBatchCCDAddendaTypeCode validates addenda type code is 05 func testBatchCCDAddendaTypeCode(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).typeCode = "07" @@ -133,12 +136,12 @@ func testBatchCCDAddendaTypeCode(t testing.TB) { } } -// TestBatchCCDAddendaTypeCode tests verifying addenda type code is 05 +// TestBatchCCDAddendaTypeCode tests validating addenda type code is 05 func TestBatchCCDAddendaTypeCode(t *testing.T) { testBatchCCDAddendaTypeCode(t) } -// BenchmarkBatchCCDAddendaTypeCod benchmarks verifying addenda type code is 05 +// BenchmarkBatchCCDAddendaTypeCod benchmarks validating addenda type code is 05 func BenchmarkBatchCCDAddendaTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -146,7 +149,7 @@ func BenchmarkBatchCCDAddendaTypeCode(b *testing.B) { } } -// testBatchCCDSEC verifies that the standard entry class code is CCD for batchCCD +// testBatchCCDSEC validates that the standard entry class code is CCD for batchCCD func testBatchCCDSEC(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.header.StandardEntryClassCode = "RCK" @@ -161,12 +164,12 @@ func testBatchCCDSEC(t testing.TB) { } } -// TestBatchCCDSEC test verifying that the standard entry class code is CCD for batchCCD +// TestBatchCCDSEC tests validating that the standard entry class code is CCD for batchCCD func TestBatchCCDSEC(t *testing.T) { testBatchCCDSEC(t) } -// BenchmarkBatchCCDSEC benchmarks verifying that the standard entry class code is CCD for batchCCD +// BenchmarkBatchCCDSEC benchmarks validating that the standard entry class code is CCD for batchCCD func BenchmarkBatchCCDSEC(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -174,7 +177,7 @@ func BenchmarkBatchCCDSEC(b *testing.B) { } } -// testBatchCCDAddendaCount verifies batch CCD addenda count +// testBatchCCDAddendaCount validates batch CCD addenda count func testBatchCCDAddendaCount(t testing.TB) { mockBatch := mockBatchCCD() mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) @@ -190,12 +193,12 @@ func testBatchCCDAddendaCount(t testing.TB) { } } -// TestBatchCCDAddendaCount tests verifying batch CCD addenda count +// TestBatchCCDAddendaCount tests validating batch CCD addenda count func TestBatchCCDAddendaCount(t *testing.T) { testBatchCCDAddendaCount(t) } -// BenchmarkBatchCCDAddendaCount benchmarks verifying batch CCD addenda count +// BenchmarkBatchCCDAddendaCount benchmarks validating batch CCD addenda count func BenchmarkBatchCCDAddendaCount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From 807d7531ffea072ed8cf16bad43ccb12a582ca37 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 18 May 2018 09:03:41 -0400 Subject: [PATCH 0113/1694] #166 batchPPD_test benchmark #166 batchPPD_test benchmark --- batchPPD_test.go | 123 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 9 deletions(-) diff --git a/batchPPD_test.go b/batchPPD_test.go index 2ace3b126..6f474cbb4 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -9,6 +9,7 @@ import ( "time" ) +// mockBatchPPDHeader creates a PPD batch header func mockBatchPPDHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 @@ -21,6 +22,7 @@ func mockBatchPPDHeader() *BatchHeader { return bh } +// mockPPDEntryDetail creates a PPD Entry Detail func mockPPDEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 @@ -33,6 +35,7 @@ func mockPPDEntryDetail() *EntryDetail { return entry } +// mockBatchPPDHeader2 creates a 2nd PPD batch header func mockBatchPPDHeader2() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 200 @@ -46,7 +49,8 @@ func mockBatchPPDHeader2() *BatchHeader { return bh } -func mockPPDEntry2() *EntryDetail { +// mockPPDEntryDetail2 creates a 2nd PPD entry detail +func mockPPDEntryDetail2() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 // ACH Credit entry.SetRDFI("231380104") @@ -59,6 +63,7 @@ func mockPPDEntry2() *EntryDetail { return entry } +// mockBatchPPD creates a PPD batch func mockBatchPPD() *BatchPPD { mockBatch := NewBatchPPD(mockBatchPPDHeader()) mockBatch.AddEntry(mockPPDEntryDetail()) @@ -68,14 +73,29 @@ func mockBatchPPD() *BatchPPD { return mockBatch } -func TestBatchError(t *testing.T) { +// testBatchError validates batch error handling +func testBatchError(t testing.TB) { err := &BatchError{BatchNumber: 1, FieldName: "mock", Msg: "test message"} if err.Error() != "BatchNumber 1 mock test message" { t.Error("BatchError Error has changed formatting") } } -func TestBatchServiceClassCodeEquality(t *testing.T) { +// TestBatchError tests validating batch error handling +func TestBatchError(t *testing.T) { + testBatchError(t) +} + +// BenchmarkBatchError benchmarks validating batch error handling +func BenchmarkBatchError(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchError(b) + } +} + +// testBatchServiceClassCodeEquality validates service class code equality +func testBatchServiceClassCodeEquality(t testing.TB) { mockBatch := mockBatchPPD() mockBatch.GetControl().ServiceClassCode = 225 if err := mockBatch.Validate(); err != nil { @@ -89,7 +109,21 @@ func TestBatchServiceClassCodeEquality(t *testing.T) { } } -func TestBatchPPDCreate(t *testing.T) { +// TestBatchServiceClassCodeEquality tests validating service class code equality +func TestBatchServiceClassCodeEquality(t *testing.T) { + testBatchServiceClassCodeEquality(t) +} + +// BenchmarkBatchServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchServiceClassCodeEquality(b) + } +} + +// BatchPPDCreate validates batch create for an invalid service code +func testBatchPPDCreate(t testing.TB) { mockBatch := mockBatchPPD() // can not have default values in Batch Header to build batch mockBatch.GetHeader().ServiceClassCode = 0 @@ -105,7 +139,22 @@ func TestBatchPPDCreate(t *testing.T) { } } -func TestBatchPPDTypeCode(t *testing.T) { +// TestBatchPPDCreate tests validating batch create for an invalid service code +func TestBatchPPDCreate(t *testing.T) { + testBatchPPDCreate(t) +} + +// BenchmarkBatchPPDCreate benchmarks validating batch create for an invalid service code +func BenchmarkBatchPPDCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPPDCreate(b) + } +} + + +// testBatchPPDTypeCode validates batch PPD type code +func testBatchPPDTypeCode(t testing.TB) { mockBatch := mockBatchPPD() // change an addendum to an invalid type code a := mockAddenda05() @@ -123,7 +172,21 @@ func TestBatchPPDTypeCode(t *testing.T) { } } -func TestBatchCompanyIdentification(t *testing.T) { +// TestBatchPPDTypeCode tests validating batch PPD type code +func TestBatchPPDTypeCode(t *testing.T) { + testBatchPPDTypeCode(t) +} + +// BenchmarkBatchPPDTypeCode benchmarks validating batch PPD type code +func BenchmarkBatchPPDTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPPDTypeCode(b) + } +} + +// testBatchCompanyIdentification validates batch PPD company identification +func testBatchCompanyIdentification(t testing.TB) { mockBatch := mockBatchPPD() mockBatch.GetControl().CompanyIdentification = "XYZ Inc" if err := mockBatch.Validate(); err != nil { @@ -137,7 +200,22 @@ func TestBatchCompanyIdentification(t *testing.T) { } } -func TestBatchODFIIDMismatch(t *testing.T) { +// TestBatchCompanyIdentification tests validating batch PPD company identification +func TestBatchCompanyIdentification(t *testing.T) { + testBatchCompanyIdentification(t) +} + +// BenchmarkBatchCompanyIdentification benchmarks validating batch PPD company identification +func BenchmarkBatchCompanyIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCompanyIdentification(b) + } +} + + +// testBatchODFIIDMismatch validates ODFIIdentification mismatch +func testBatchODFIIDMismatch(t testing.TB) { mockBatch := mockBatchPPD() mockBatch.GetControl().ODFIIdentification = "987654321" if err := mockBatch.Validate(); err != nil { @@ -151,9 +229,23 @@ func TestBatchODFIIDMismatch(t *testing.T) { } } -func TestBatchBuild(t *testing.T) { +// TestBatchODFIIDMismatch tests validating ODFIIdentification mismatch +func TestBatchODFIIDMismatch(t *testing.T) { + testBatchODFIIDMismatch(t) +} + +// BenchmarkBatchODFIIDMismatch benchmarks validating ODFIIdentification mismatch +func BenchmarkBatchODFIIDMismatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchODFIIDMismatch(b) + } +} + +// testBatchBuild builds a PPD batch +func testBatchBuild(t testing.TB) { mockBatch := NewBatchPPD(mockBatchPPDHeader2()) - entry := mockPPDEntry2() + entry := mockPPDEntryDetail2() addenda05 := NewAddenda05() entry.AddAddenda(addenda05) mockBatch.AddEntry(entry) @@ -161,3 +253,16 @@ func TestBatchBuild(t *testing.T) { t.Errorf("%T: %s", err, err) } } + +// TestBatchBuild tests building a PPD batch +func TestBatchBuild(t *testing.T) { + testBatchBuild(t) +} + +// BenchmarkBatchBuild benchmarks building a PPD batch +func BenchmarkBatchBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBuild(b) + } +} \ No newline at end of file From 5ecbb31bc5d1456b651ccd7c04c583efe76338bd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 18 May 2018 10:42:03 -0400 Subject: [PATCH 0114/1694] #166 Benchmark #166 Benchmark benchmark code for batchTEL_test.go and batchWEB_test.go --- batchCCD_test.go | 2 +- batchPPD_test.go | 1 - batchTEL_test.go | 103 ++++++++++++++++++++++++++++++++++++++++--- batchWEB_test.go | 111 ++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 196 insertions(+), 21 deletions(-) diff --git a/batchCCD_test.go b/batchCCD_test.go index 1cd991188..4bd33d328 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -169,7 +169,7 @@ func TestBatchCCDSEC(t *testing.T) { testBatchCCDSEC(t) } -// BenchmarkBatchCCDSEC benchmarks validating that the standard entry class code is CCD for batchCCD +// BenchmarkBatchCCDSEC benchmarks validating that the standard entry class code is CCD for batch CCD func BenchmarkBatchCCDSEC(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/batchPPD_test.go b/batchPPD_test.go index 6f474cbb4..0af1d0f0e 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -152,7 +152,6 @@ func BenchmarkBatchPPDCreate(b *testing.B) { } } - // testBatchPPDTypeCode validates batch PPD type code func testBatchPPDTypeCode(t testing.TB) { mockBatch := mockBatchPPD() diff --git a/batchTEL_test.go b/batchTEL_test.go index 16967ceb7..2c9293c66 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -4,6 +4,7 @@ import ( "testing" ) +// mockBatchTELHeader creates a TEL batch header func mockBatchTELHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 225 @@ -15,6 +16,7 @@ func mockBatchTELHeader() *BatchHeader { return bh } +// mockTELEntryDetail creates a TEL entry detail func mockTELEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 @@ -28,6 +30,7 @@ func mockTELEntryDetail() *EntryDetail { return entry } +// mockBatchTEL creates a TEL batch func mockBatchTEL() *BatchTEL { mockBatch := NewBatchTEL(mockBatchTELHeader()) mockBatch.AddEntry(mockTELEntryDetail()) @@ -37,7 +40,8 @@ func mockBatchTEL() *BatchTEL { return mockBatch } -func TestBatchTELHeader(t *testing.T) { +// testBatchTELHeader creates a TEL batch header +func testBatchTELHeader(t testing.TB) { batch, _ := NewBatch(mockBatchTELHeader()) err, ok := batch.(*BatchTEL) if !ok { @@ -45,7 +49,21 @@ func TestBatchTELHeader(t *testing.T) { } } -func TestBatchTELCreate(t *testing.T) { +// TestBatchTELHeader tests creating a TEL batch header +func TestBatchTELHeader(t *testing.T) { + testBatchTELHeader(t) +} + +// BenchmarkBatchTELHeader benchmarks creating a TEL batch header +func BenchmarkBatchTELHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTELHeader(b) + } +} + +// testBatchTELCreate validates batch create for an invalid service code +func testBatchTELCreate(t testing.TB) { mockBatch := mockBatchTEL() // Batch Header information is required to Create a batch. mockBatch.GetHeader().ServiceClassCode = 0 @@ -61,7 +79,21 @@ func TestBatchTELCreate(t *testing.T) { } } -func TestBatchTELAddendaCount(t *testing.T) { +// TestBatchTELCreate tests validating batch create for an invalid service code +func TestBatchTELCreate(t *testing.T) { + testBatchTELCreate(t) +} + +// BenchmarkBatchTELCreate benchmarks validating batch create for an invalid service code +func BenchmarkBatchTELCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTELCreate(b) + } +} + +// testBatchTELAddendaCount validates addenda count for batch TEL +func testBatchTELAddendaCount(t testing.TB) { mockBatch := mockBatchTEL() // TEL can not have an addendum mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) @@ -77,7 +109,21 @@ func TestBatchTELAddendaCount(t *testing.T) { } } -func TestBatchTELSEC(t *testing.T) { +// TestBatchTELAddendaCount tests validating addenda count for batch TEL +func TestBatchTELAddendaCount(t *testing.T) { + testBatchTELAddendaCount(t) +} + +// BenchmarkBatchTELAddendaCount benchmarks validating addenda count for batch TEL +func BenchmarkBatchTELAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTELAddendaCount(b) + } +} + +// testBatchTELSEC validates SEC code for batch TEL +func testBatchTELSEC(t testing.TB) { mockBatch := mockBatchTEL() mockBatch.header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { @@ -91,7 +137,21 @@ func TestBatchTELSEC(t *testing.T) { } } -func TestBatchTELDebit(t *testing.T) { +// TestBatchTELSEC tests validating SEC code for batch TEL +func TestBatchTELSEC(t *testing.T) { + testBatchTELSEC(t) +} + +// BenchmarkBatchTELSEC benchmarks validating SEC code for batch TEL +func BenchmarkBatchTELSEC(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTELSEC(b) + } +} + +// testBatchTELDebit validates Transaction code for TEL entry detail +func testBatchTELDebit(t testing.TB) { mockBatch := mockBatchTEL() mockBatch.GetEntries()[0].TransactionCode = 22 if err := mockBatch.Create(); err != nil { @@ -105,8 +165,22 @@ func TestBatchTELDebit(t *testing.T) { } } -// verify that the entry detail payment type / discretionary data is either single or reoccurring for the -func TestBatchTELPaymentType(t *testing.T) { +// TestBatchTELDebit tests validating Transaction code for TEL entry detail +func TestBatchTELDebit(t *testing.T) { + testBatchTELDebit(t) +} + +// BenchmarkBatchTELDebit benchmarks validating Transaction code for TEL entry detail +func BenchmarkBatchTELDebit(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTELDebit(b) + } +} + +// testBatchTELPaymentType validates that the entry detail +// payment type / discretionary data is either single or reoccurring +func testBatchTELPaymentType(t testing.TB) { mockBatch := mockBatchTEL() mockBatch.GetEntries()[0].DiscretionaryData = "AA" if err := mockBatch.Validate(); err != nil { @@ -120,3 +194,18 @@ func TestBatchTELPaymentType(t *testing.T) { } } } + +// TestBatchTELPaymentType tests validating that the entry detail +// payment type / discretionary data is either single or reoccurring +func TestBatchTELPaymentType(t *testing.T) { + testBatchTELPaymentType(t) +} + +// BenchmarkBatchTELPaymentTyp benchmarks validating that the entry detail +// payment type / discretionary data is either single or reoccurring +func BenchmarkBatchTELPaymentType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchTELPaymentType(b) + } +} diff --git a/batchWEB_test.go b/batchWEB_test.go index e1814e094..06d2fae6e 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -2,6 +2,7 @@ package ach import "testing" +// mockBatchWEBHeader creates a WEB batch header func mockBatchWEBHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 @@ -13,6 +14,7 @@ func mockBatchWEBHeader() *BatchHeader { return bh } +// mockWEBEntryDetail creates a WEB entry Detail func mockWEBEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 @@ -25,6 +27,7 @@ func mockWEBEntryDetail() *EntryDetail { return entry } +// mockBatchWEB creates a Web batch func mockBatchWEB() *BatchWEB { mockBatch := NewBatchWEB(mockBatchWEBHeader()) mockBatch.AddEntry(mockWEBEntryDetail()) @@ -35,9 +38,9 @@ func mockBatchWEB() *BatchWEB { return mockBatch } -// No more than 1 batch per entry detail record can exist -// No more than 1 addenda record per entry detail record can exist -func TestBatchWebAddenda(t *testing.T) { +// testBatchWebAddenda validates No more than 1 batch per entry detail record can exist +// and no more than 1 addenda record per entry detail record can exist +func testBatchWebAddenda(t testing.TB) { mockBatch := mockBatchWEB() // mock batch already has one addenda. Creating two addenda should error mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) @@ -52,8 +55,23 @@ func TestBatchWebAddenda(t *testing.T) { } } -// Individual name is a mandatory field -func TestBatchWebIndividualNameRequired(t *testing.T) { +// TestBatchWebAddenda tests validating No more than 1 batch per entry detail +// record can exist and no more than 1 addenda record per entry detail record can exist +func TestBatchWebAddenda(t *testing.T) { + testBatchWebAddenda(t) +} + +// BenchmarkBatchWebAddenda benchmarks validating No more than 1 batch per entry detail +// record can exist and no more than 1 addenda record per entry detail record can exist +func BenchmarkBatchWebAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchWebAddenda(b) + } +} + +// testBatchWebIndividualNameRequired validates Individual name is a mandatory field +func testBatchWebIndividualNameRequired(t testing.TB) { mockBatch := mockBatchWEB() // mock batch already has one addenda. Creating two addenda should error mockBatch.GetEntries()[0].IndividualName = "" @@ -68,8 +86,21 @@ func TestBatchWebIndividualNameRequired(t *testing.T) { } } -// verify addenda type code is 05 -func TestBatchWEBAddendaTypeCode(t *testing.T) { +// TestBatchWebIndividualNameRequired tests validating Individual name is a mandatory field +func TestBatchWebIndividualNameRequired(t *testing.T) { + testBatchWebIndividualNameRequired(t) +} + +// BenchmarkBatchWebIndividualNameRequired benchmarks validating Individual name is a mandatory field +func BenchmarkBatchWebIndividualNameRequired(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchWebIndividualNameRequired(b) + } +} + +// testBatchWEBAddendaTypeCod validates addenda type code is 05 +func testBatchWEBAddendaTypeCode(t testing.TB) { mockBatch := mockBatchWEB() mockBatch.GetEntries()[0].Addendum[0].(*Addenda05).typeCode = "07" if err := mockBatch.Validate(); err != nil { @@ -83,8 +114,21 @@ func TestBatchWEBAddendaTypeCode(t *testing.T) { } } -// verify that the standard entry class code is WEB for batchWeb -func TestBatchWebSEC(t *testing.T) { +// TestBatchWEBAddendaTypeCode tests validating addenda type code is 05 +func TestBatchWEBAddendaTypeCode(t *testing.T) { + testBatchWEBAddendaTypeCode(t) +} + +// BenchmarkBatchWEBAddendaTypeCode benchmarks validating addenda type code is 05 +func BenchmarkBatchWEBAddendaTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchWEBAddendaTypeCode(b) + } +} + +// testBatchWebSEC validates that the standard entry class code is WEB for batch Web +func testBatchWebSEC(t testing.TB) { mockBatch := mockBatchWEB() mockBatch.header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { @@ -98,8 +142,22 @@ func TestBatchWebSEC(t *testing.T) { } } -// verify that the entry detail payment type / discretionary data is either single or reoccurring for the -func TestBatchWebPaymentType(t *testing.T) { +// TestBatchWebSEC tests validating that the standard entry class code is WEB for batch Web +func TestBatchWebSEC(t *testing.T) { + testBatchWebSEC(t) +} + +// BenchmarkBatchWebSEC benchmarks validating that the standard entry class code is WEB for batch Web +func BenchmarkBatchWebSEC(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchWebSEC(b) + } +} + +// testBatchWebPaymentType validates that the entry detail +// payment type / discretionary data is either single or reoccurring +func testBatchWebPaymentType(t testing.TB) { mockBatch := mockBatchWEB() mockBatch.GetEntries()[0].DiscretionaryData = "AA" if err := mockBatch.Validate(); err != nil { @@ -113,7 +171,23 @@ func TestBatchWebPaymentType(t *testing.T) { } } -func TestBatchWebCreate(t *testing.T) { +// TestBatchWebPaymentType tests validating that the entry detail +// payment type / discretionary data is either single or reoccurring +func TestBatchWebPaymentType(t *testing.T) { + testBatchWebPaymentType(t) +} + +// BenchmarkBatchWebPaymentType benchmarks validating that the entry detail +// payment type / discretionary data is either single or reoccurring +func BenchmarkBatchWebPaymentType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchWebPaymentType(b) + } +} + +// testBatchWebCreate creates a WEB batch +func testBatchWebCreate(t testing.TB) { mockBatch := mockBatchWEB() // Must have valid batch header to create a batch mockBatch.GetHeader().ServiceClassCode = 63 @@ -127,3 +201,16 @@ func TestBatchWebCreate(t *testing.T) { } } } + +// TestBatchWebCreate tests creating a WEB batch +func TestBatchWebCreate(t *testing.T) { + testBatchWebCreate(t) +} + +// BenchmarkBatchWebCreate benchmarks creating a WEB batch +func BenchmarkBatchWebCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchWebCreate(b) + } +} From f1b63fdf41cc23ba338fc38881b1d12443458629 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 18 May 2018 11:34:56 -0600 Subject: [PATCH 0115/1694] One character return breaking the build --- addenda05.go | 1 - 1 file changed, 1 deletion(-) diff --git a/addenda05.go b/addenda05.go index eb4c96e3e..ef614ebed 100644 --- a/addenda05.go +++ b/addenda05.go @@ -62,7 +62,6 @@ func (addenda05 *Addenda05) String() string { addenda05.EntryDetailSequenceNumberField()) } - // SetPaymentRelatedInformation allows additional information about the transaction func (addenda05 *Addenda05) SetPaymentRelatedInformation(s string) *Addenda05 { addenda05.PaymentRelatedInformation = s From 90f3d2b93380c56ecfa23eca9b141b2d9e258dcd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 21 May 2018 12:32:27 -0400 Subject: [PATCH 0116/1694] #166 add benchmarks to tests #166 add benchmarks to tests --- batchWEB_test.go | 12 +- converters.go | 2 +- converters_test.go | 158 ++++++++++++++-- entryDetail_internal_test.go | 344 ++++++++++++++++++++++++++++++++--- fileControl_internal_test.go | 148 +++++++++++++-- file_test.go | 184 +++++++++++++++++-- record_test.go | 112 +++++++++++- writer_test.go | 16 +- 8 files changed, 889 insertions(+), 87 deletions(-) diff --git a/batchWEB_test.go b/batchWEB_test.go index 06d2fae6e..ba716e247 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -14,7 +14,7 @@ func mockBatchWEBHeader() *BatchHeader { return bh } -// mockWEBEntryDetail creates a WEB entry Detail +// mockWEBEntryDetail creates a WEB entry detail func mockWEBEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 @@ -27,7 +27,7 @@ func mockWEBEntryDetail() *EntryDetail { return entry } -// mockBatchWEB creates a Web batch +// mockBatchWEB creates a WEB batch func mockBatchWEB() *BatchWEB { mockBatch := NewBatchWEB(mockBatchWEBHeader()) mockBatch.AddEntry(mockWEBEntryDetail()) @@ -55,13 +55,13 @@ func testBatchWebAddenda(t testing.TB) { } } -// TestBatchWebAddenda tests validating No more than 1 batch per entry detail +// TestBatchWebAddenda tests validating no more than 1 batch per entry detail // record can exist and no more than 1 addenda record per entry detail record can exist func TestBatchWebAddenda(t *testing.T) { testBatchWebAddenda(t) } -// BenchmarkBatchWebAddenda benchmarks validating No more than 1 batch per entry detail +// BenchmarkBatchWebAddenda benchmarks validating no more than 1 batch per entry detail // record can exist and no more than 1 addenda record per entry detail record can exist func BenchmarkBatchWebAddenda(b *testing.B) { b.ReportAllocs() @@ -142,12 +142,12 @@ func testBatchWebSEC(t testing.TB) { } } -// TestBatchWebSEC tests validating that the standard entry class code is WEB for batch Web +// TestBatchWebSEC tests validating that the standard entry class code is WEB for batch WEB func TestBatchWebSEC(t *testing.T) { testBatchWebSEC(t) } -// BenchmarkBatchWebSEC benchmarks validating that the standard entry class code is WEB for batch Web +// BenchmarkBatchWebSEC benchmarks validating that the standard entry class code is WEB for batch WEB func BenchmarkBatchWebSEC(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/converters.go b/converters.go index a80962d83..b89180af9 100644 --- a/converters.go +++ b/converters.go @@ -66,7 +66,7 @@ func (c *converters) numericField(n int, max uint) string { return s } -// stringField right-justified and zero filled +// stringRTNField slices to max length and zero filled func (c *converters) stringRTNField(s string, max uint) string { ln := uint(len(s)) if ln > max { diff --git a/converters_test.go b/converters_test.go index 53ab199f1..34ea860ef 100644 --- a/converters_test.go +++ b/converters_test.go @@ -6,8 +6,8 @@ package ach import "testing" -//testAlphaField ensire that padding and two long of strings get properly made -func TestAlphaFieldShort(t *testing.T) { +//testAlphaField ensures that padding and two long of strings get properly made +func testAlphaFieldShort(t testing.TB) { c := converters{} result := c.alphaField("ABC123", 10) if result != "ABC123 " { @@ -15,8 +15,21 @@ func TestAlphaFieldShort(t *testing.T) { } } -// TestAlphaFieldLong ensure that string is left justified and sliced to max -func TestAlphaFieldLong(t *testing.T) { +// TestAlphaFieldShort test ensures that padding and two long of strings get properly made +func TestAlphaFieldShort(t *testing.T) { + testAlphaFieldShort(t) +} + +// BenchmarkAlphaFieldShort benchmark ensures that padding and two long of strings get properly made +func BenchmarkAlphaFieldShort(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAlphaFieldShort(b) + } +} + +// testAlphaFieldLong ensures that string is left justified and sliced to max +func testAlphaFieldLong(t testing.TB) { c := converters{} result := c.alphaField("abcdEFGH123", 10) if result != "abcdEFGH12" { @@ -24,8 +37,21 @@ func TestAlphaFieldLong(t *testing.T) { } } -// TestNumericFieldShort ensures zero padding and right justified -func TestNumericFieldShort(t *testing.T) { +// TestAlphaFieldLong test ensures that string is left justified and sliced to max +func TestAlphaFieldLong(t *testing.T) { + testAlphaFieldLong(t) +} + +// BenchmarkAlphaFieldLong benchmark ensures that string is left justified and sliced to max +func BenchmarkAlphaFieldLong(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAlphaFieldLong(b) + } +} + +// testNumericFieldShort ensures zero padding and right justified +func testNumericFieldShort(t testing.TB) { c := converters{} result := c.numericField(12345, 10) if result != "0000012345" { @@ -33,8 +59,21 @@ func TestNumericFieldShort(t *testing.T) { } } -// TestNumericFieldLong right justified and sliced to max length -func TestNumericFieldLong(t *testing.T) { +// TestNumericFieldShort test ensures zero padding and right justified +func TestNumericFieldShort(t *testing.T) { + testNumericFieldShort(t) +} + +// BenchmarkNumericFieldShort benchmark ensures zero padding and right justified +func BenchmarkNumericFieldShort(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testNumericFieldShort(b) + } +} + +// testNumericFieldLong ensures right justified and sliced to max length +func testNumericFieldLong(t testing.TB) { c := converters{} result := c.numericField(123456, 5) if result != "23456" { @@ -42,8 +81,21 @@ func TestNumericFieldLong(t *testing.T) { } } -//TestParseNumField handle zero and spaces in number conversion -func TestParseNumField(t *testing.T) { +// TestNumericFieldLong test ensures right justified and sliced to max length +func TestNumericFieldLong(t *testing.T) { + testNumericFieldLong(t) +} + +// BenchmarkNumericFieldLong benchmark ensures right justified and sliced to max length +func BenchmarkNumericFieldLong(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testNumericFieldLong(b) + } +} + +// testParseNumField handles zero and spaces in number conversion +func testParseNumField(t testing.TB) { c := converters{} result := c.parseNumField(" 012345") if result != 12345 { @@ -51,8 +103,21 @@ func TestParseNumField(t *testing.T) { } } -//TestParseNumField handle zero and spaces in number conversion -func TestParseStringField(t *testing.T) { +// TestParseNumField test handles zero and spaces in number conversion +func TestParseNumField(t *testing.T) { + testParseNumField(t) +} + +// BenchmarkParseNumField benchmark handles zero and spaces in number conversion +func BenchmarkParseNumField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseNumField(b) + } +} + +// testParseStringField handles spaces in string conversion +func testParseStringField(t testing.TB) { c := converters{} result := c.parseStringField(" 012345") if result != "012345" { @@ -60,8 +125,21 @@ func TestParseStringField(t *testing.T) { } } -// TestNumericFieldShort ensures zero padding and right justified -func TestRTNFieldShort(t *testing.T) { +// TestParseStringField test handles spaces in string conversion +func TestParseStringField(t *testing.T) { + testParseStringField(t) +} + +// BenchmarkParseStringField benchmark handles spaces in string conversion +func BenchmarkParseStringField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseStringField(b) + } +} + +// testRTNFieldShort ensures zero padding and right justified +func testRTNFieldShort(t testing.TB) { c := converters{} result := c.stringRTNField("123456", 8) if result != "00123456" { @@ -69,8 +147,21 @@ func TestRTNFieldShort(t *testing.T) { } } -// TestNumericFieldLong right justified and sliced to max length -func TestRTNFieldLong(t *testing.T) { +// TestRTNFieldShort test ensures zero padding and right justified +func TestRTNFieldShort(t *testing.T) { + testRTNFieldShort(t) +} + +// BenchmarkRTNFieldShort benchmark ensures zero padding and right justified +func BenchmarkRTNFieldShort(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRTNFieldShort(b) + } +} + +// testRTNFieldLong ensures sliced to max length +func testRTNFieldLong(t testing.TB) { c := converters{} result := c.stringRTNField("1234567899", 8) if result != "12345678" { @@ -78,10 +169,37 @@ func TestRTNFieldLong(t *testing.T) { } } -func TestRTNFieldExact(t *testing.T) { +// TestRTNFieldLong test ensures sliced to max length +func TestRTNFieldLong(t *testing.T) { + testRTNFieldLong(t) +} + +// BenchmarkRTNFieldLong benchmark ensures sliced to max length +func BenchmarkRTNFieldLong(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRTNFieldLong(b) + } +} + +// testRTNFieldExact ensures exact match +func testRTNFieldExact(t testing.TB) { c := converters{} - result := c.stringRTNField("12345678", 8) - if result != "12345678" { - t.Errorf("first 8 character string: '%v'", result) + result := c.stringRTNField("123456789", 9) + if result != "123456789" { + t.Errorf("first 9 character string: '%v'", result) + } +} + +// TestRTNFieldExact test ensures exact match +func TestRTNFieldExact(t *testing.T) { + testRTNFieldExact(t) +} + +// BenchmarkRTNFieldExact benchmark ensures exact match +func BenchmarkRTNFieldExact(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRTNFieldExact(b) } } diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index b8c22b2c4..ec9e04168 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -9,6 +9,7 @@ import ( "testing" ) +// mockEntryDetail creates an entry detail func mockEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 @@ -22,7 +23,8 @@ func mockEntryDetail() *EntryDetail { return entry } -func TestMockEntryDetail(t *testing.T) { +// testMockEntryDetail validates an entry detail record +func testMockEntryDetail(t testing.TB) { entry := mockEntryDetail() if err := entry.Validate(); err != nil { t.Error("mockEntryDetail does not validate and will break other tests") @@ -44,8 +46,21 @@ func TestMockEntryDetail(t *testing.T) { } } -// TestParseEntryDetail Header parses a known Entry Detail Record string. -func TestParseEntryDetail(t *testing.T) { +// TestMockEntryDetail tests validating an entry detail record +func TestMockEntryDetail(t *testing.T) { + testMockEntryDetail(t) +} + +// BenchmarkMockEntryDetail benchmarks validating an entry detail record +func BenchmarkMockEntryDetail(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testMockEntryDetail(b) + } +} + +// testParseEntryDetail parses a known entry detail record string. +func testParseEntryDetail(t testing.TB) { var line = "62705320001912345 0000010500c-1 Arnold Wade DD0076401255655291" r := NewReader(strings.NewReader(line)) r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) @@ -92,8 +107,22 @@ func TestParseEntryDetail(t *testing.T) { } } -// TestEDString validats that a known parsed file can be return to a string of the same value -func TestEDString(t *testing.T) { +// TestParseEntryDetail tests parsing a known entry detail record string. +func TestParseEntryDetail(t *testing.T) { + testParseEntryDetail(t) +} + +// BenchmarkParseEntryDetail benchmarks parsing a known entry detail record string. +func BenchmarkParseEntryDetail(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseEntryDetail(b) + } +} + +// testEDString validates that a known parsed entry +// detail can be returned to a string of the same value +func testEDString(t testing.TB) { var line = "62705320001912345 0000010500c-1 Arnold Wade DD0076401255655291" r := NewReader(strings.NewReader(line)) r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) @@ -109,8 +138,23 @@ func TestEDString(t *testing.T) { } } -// TestValidateEDRecordType ensure error if recordType is not 6 -func TestValidateEDRecordType(t *testing.T) { +// TestEDString tests validating that a known parsed entry +// detail can be returned to a string of the same value +func TestEDString(t *testing.T) { + testEDString(t) +} + +// BenchmarkEDString benchmarks validating that a known parsed entry +// detail can be returned to a string of the same value +func BenchmarkEDString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDString(b) + } +} + +// testValidateEDRecordType validates error if recordType is not 6 +func testValidateEDRecordType(t testing.TB) { ed := mockEntryDetail() ed.recordType = "2" if err := ed.Validate(); err != nil { @@ -122,8 +166,21 @@ func TestValidateEDRecordType(t *testing.T) { } } -// TestValidateEDTransactionCode ensure error if TransactionCode is not valid -func TestValidateEDTransactionCode(t *testing.T) { +// TestValidateEDRecordType tests validating error if recordType is not 6 +func TestValidateEDRecordType(t *testing.T) { + testValidateEDRecordType(t) +} + +// BenchmarkValidateEDRecordType benchmarks validating error if recordType is not 6 +func BenchmarkValidateEDRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateEDRecordType(b) + } +} + +// testValidateEDTransactionCode validates error if transaction code is not valid +func testValidateEDTransactionCode(t testing.TB) { ed := mockEntryDetail() ed.TransactionCode = 63 if err := ed.Validate(); err != nil { @@ -135,7 +192,21 @@ func TestValidateEDTransactionCode(t *testing.T) { } } -func TestEDFieldInclusion(t *testing.T) { +// TestValidateEDTransactionCode tests validating error if transaction code is not valid +func TestValidateEDTransactionCode(t *testing.T) { + testValidateEDTransactionCode(t) +} + +// BenchmarkValidateEDTransactionCode benchmarks validating error if transaction code is not valid +func BenchmarkValidateEDTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateEDTransactionCode(b) + } +} + +// testEDFieldInclusion validates entry detail field inclusion +func testEDFieldInclusion(t testing.TB) { ed := mockEntryDetail() ed.Amount = 0 if err := ed.Validate(); err != nil { @@ -147,7 +218,21 @@ func TestEDFieldInclusion(t *testing.T) { } } -func TestEDdfiAccountNumberAlphaNumeric(t *testing.T) { +// TestEDFieldInclusion tests validating entry detail field inclusion +func TestEDFieldInclusion(t *testing.T) { + testEDFieldInclusion(t) +} + +// BenchmarkEDFieldInclusion benchmarks validating entry detail field inclusion +func BenchmarkEDFieldInclusion(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDFieldInclusion(b) + } +} + +// testEDdfiAccountNumberAlphaNumeric validates DFI account number is alpha numeric +func testEDdfiAccountNumberAlphaNumeric(t testing.TB) { ed := mockEntryDetail() ed.DFIAccountNumber = "®" if err := ed.Validate(); err != nil { @@ -159,7 +244,21 @@ func TestEDdfiAccountNumberAlphaNumeric(t *testing.T) { } } -func TestEDIdentificationNumberAlphaNumeric(t *testing.T) { +// TestEDdfiAccountNumberAlphaNumeric tests validating DFI account number is alpha numeric +func TestEDdfiAccountNumberAlphaNumeric(t *testing.T) { + testEDdfiAccountNumberAlphaNumeric(t) +} + +// BenchmarkEDdfiAccountNumberAlphaNumeric benchmarks validating DFI account number is alpha numeric +func BenchmarkEDdfiAccountNumberAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDdfiAccountNumberAlphaNumeric(b) + } +} + +// testEDIdentificationNumberAlphaNumeric validates identification number is alpha numeric +func testEDIdentificationNumberAlphaNumeric(t testing.TB) { ed := mockEntryDetail() ed.IdentificationNumber = "®" if err := ed.Validate(); err != nil { @@ -171,7 +270,21 @@ func TestEDIdentificationNumberAlphaNumeric(t *testing.T) { } } -func TestEDIndividualNameAlphaNumeric(t *testing.T) { +// TestEDIdentificationNumberAlphaNumeric tests validating identification number is alpha numeric +func TestEDIdentificationNumberAlphaNumeric(t *testing.T) { + testEDIdentificationNumberAlphaNumeric(t) +} + +// BenchmarkEDIdentificationNumberAlphaNumeric benchmarks validating identification number is alpha numeric +func BenchmarkEDIdentificationNumberAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDIdentificationNumberAlphaNumeric(b) + } +} + +// testEDIndividualNameAlphaNumeric validates individual name is alpha numeric +func testEDIndividualNameAlphaNumeric(t testing.TB) { ed := mockEntryDetail() ed.IndividualName = "W®DE" if err := ed.Validate(); err != nil { @@ -183,7 +296,21 @@ func TestEDIndividualNameAlphaNumeric(t *testing.T) { } } -func TestEDDiscretionaryDataAlphaNumeric(t *testing.T) { +// TestEDIndividualNameAlphaNumeric tests validating individual name is alpha numeric +func TestEDIndividualNameAlphaNumeric(t *testing.T) { + testEDIndividualNameAlphaNumeric(t) +} + +// BenchmarkEDIndividualNameAlphaNumeric benchmarks validating individual name is alpha numeric +func BenchmarkEDIndividualNameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDIndividualNameAlphaNumeric(b) + } +} + +// testEDDiscretionaryDataAlphaNumeric validates discretionary data is alpha numeric +func testEDDiscretionaryDataAlphaNumeric(t testing.TB) { ed := mockEntryDetail() ed.DiscretionaryData = "®!" if err := ed.Validate(); err != nil { @@ -195,7 +322,21 @@ func TestEDDiscretionaryDataAlphaNumeric(t *testing.T) { } } -func TestEDisCheckDigit(t *testing.T) { +// TestEDDiscretionaryDataAlphaNumeric tests validating discretionary data is alpha numeric +func TestEDDiscretionaryDataAlphaNumeric(t *testing.T) { + testEDDiscretionaryDataAlphaNumeric(t) +} + +// BenchmarkEDDiscretionaryDataAlphaNumeric benchmarks validating discretionary data is alpha numeric +func BenchmarkEDDiscretionaryDataAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDDiscretionaryDataAlphaNumeric(b) + } +} + +// testEDisCheckDigit validates check digit +func testEDisCheckDigit(t testing.TB) { ed := mockEntryDetail() ed.CheckDigit = "1" if err := ed.Validate(); err != nil { @@ -207,7 +348,21 @@ func TestEDisCheckDigit(t *testing.T) { } } -func TestEDSetRDFI(t *testing.T) { +// TestEDisCheckDigit tests validating check digit +func TestEDisCheckDigit(t *testing.T) { + testEDisCheckDigit(t) +} + +// BenchmarkEDSetRDFI benchmarks validating check digit +func BenchmarkEDisCheckDigit(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDisCheckDigit(b) + } +} + +// testEDSetRDFI validates setting RDFI +func testEDSetRDFI(t testing.TB) { ed := NewEntryDetail() ed.SetRDFI("810866774") if ed.RDFIIdentification != "81086677" { @@ -218,7 +373,21 @@ func TestEDSetRDFI(t *testing.T) { } } -func TestEDFieldInclusionRecordType(t *testing.T) { +// TestEDSetRDFI tests validating setting RDFI +func TestEDSetRDFI(t *testing.T) { + testEDSetRDFI(t) +} + +// Benchmark benchmarks validating setting RDFI +func BenchmarkEDSetRDFI(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDSetRDFI(b) + } +} + +// testEDFieldInclusionRecordType validates record type field inclusion +func testEDFieldInclusionRecordType(t testing.TB) { entry := mockEntryDetail() entry.recordType = "" if err := entry.Validate(); err != nil { @@ -230,7 +399,21 @@ func TestEDFieldInclusionRecordType(t *testing.T) { } } -func TestEDFieldInclusionTransactionCode(t *testing.T) { +// TestEDFieldInclusionRecordType tests validating record type field inclusion +func TestEDFieldInclusionRecordType(t *testing.T) { + testEDFieldInclusionRecordType(t) +} + +// BenchmarkEDFieldInclusionRecordType benchmarks validating record type field inclusion +func BenchmarkEDFieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDFieldInclusionRecordType(b) + } +} + +// testEDFieldInclusionTransactionCode validates transaction code field inclusion +func testEDFieldInclusionTransactionCode(t testing.TB) { entry := mockEntryDetail() entry.TransactionCode = 0 if err := entry.Validate(); err != nil { @@ -242,7 +425,21 @@ func TestEDFieldInclusionTransactionCode(t *testing.T) { } } -func TestEDFieldInclusionRDFIIdentification(t *testing.T) { +// TestEDFieldInclusionTransactionCode tests validating transaction code field inclusion +func TestEDFieldInclusionTransactionCode(t *testing.T) { + testEDFieldInclusionTransactionCode(t) +} + +// BenchmarkEDFieldInclusionTransactionCode benchmarks validating transaction code field inclusion +func BenchmarkEDFieldInclusionTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDFieldInclusionTransactionCode(b) + } +} + +// testEDFieldInclusionRDFIIdentification validates RDFI identification field inclusion +func testEDFieldInclusionRDFIIdentification(t testing.TB) { entry := mockEntryDetail() entry.RDFIIdentification = "" if err := entry.Validate(); err != nil { @@ -254,7 +451,21 @@ func TestEDFieldInclusionRDFIIdentification(t *testing.T) { } } -func TestEDFieldInclusionDFIAccountNumber(t *testing.T) { +// TestEDFieldInclusionRDFIIdentification tests validating RDFI identification field inclusion +func TestEDFieldInclusionRDFIIdentification(t *testing.T) { + testEDFieldInclusionRDFIIdentification(t) +} + +// BenchmarkEDFieldInclusionRDFIIdentification benchmarks validating RDFI identification field inclusion +func BenchmarkEDFieldInclusionRDFIIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDFieldInclusionRDFIIdentification(b) + } +} + +// testEDFieldInclusionDFIAccountNumber validates DFI account number field inclusion +func testEDFieldInclusionDFIAccountNumber(t testing.TB) { entry := mockEntryDetail() entry.DFIAccountNumber = "" if err := entry.Validate(); err != nil { @@ -266,7 +477,21 @@ func TestEDFieldInclusionDFIAccountNumber(t *testing.T) { } } -func TestEDFieldInclusionIndividualName(t *testing.T) { +// TestEDFieldInclusionDFIAccountNumber tests validating DFI account number field inclusion +func TestEDFieldInclusionDFIAccountNumber(t *testing.T) { + testEDFieldInclusionDFIAccountNumber(t) +} + +// BenchmarkEDFieldInclusionDFIAccountNumber benchmarks validating DFI account number field inclusion +func BenchmarkEDFieldInclusionDFIAccountNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDFieldInclusionDFIAccountNumber(b) + } +} + +// testEDFieldInclusionIndividualName validates individual name field inclusion +func testEDFieldInclusionIndividualName(t testing.TB) { entry := mockEntryDetail() entry.IndividualName = "" if err := entry.Validate(); err != nil { @@ -278,7 +503,21 @@ func TestEDFieldInclusionIndividualName(t *testing.T) { } } -func TestEDFieldInclusionTraceNumber(t *testing.T) { +// TestEDFieldInclusionIndividualName tests validating individual name field inclusion +func TestEDFieldInclusionIndividualName(t *testing.T) { + testEDFieldInclusionIndividualName(t) +} + +// BenchmarkEDFieldInclusionIndividualName benchmarks validating individual name field inclusion +func BenchmarkEDFieldInclusionIndividualName(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDFieldInclusionIndividualName(b) + } +} + +// testEDFieldInclusionTraceNumber validates trace number field inclusion +func testEDFieldInclusionTraceNumber(t testing.TB) { entry := mockEntryDetail() entry.TraceNumber = 0 if err := entry.Validate(); err != nil { @@ -290,7 +529,21 @@ func TestEDFieldInclusionTraceNumber(t *testing.T) { } } -func TestEDAddAddendaAddenda99(t *testing.T) { +// TestEDFieldInclusionTraceNumber tests validating trace number field inclusion +func TestEDFieldInclusionTraceNumber(t *testing.T) { + testEDFieldInclusionTraceNumber(t) +} + +// BenchmarkEDFieldInclusionTraceNumber benchmarks validating trace number field inclusion +func BenchmarkEDFieldInclusionTraceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDFieldInclusionTraceNumber(b) + } +} + +// testEDAddAddenda99 validates adding Addenda99 to an entry detail +func testEDAddAddenda99(t testing.TB) { entry := mockEntryDetail() entry.AddAddenda(mockAddenda99()) if entry.Category != CategoryReturn { @@ -302,7 +555,21 @@ func TestEDAddAddendaAddenda99(t *testing.T) { } -func TestEDAddAddendaAddenda99Twice(t *testing.T) { +// TestEDAddAddenda99 tests validating adding Addenda99 to an entry detail +func TestEDAddAddenda99(t *testing.T) { + testEDAddAddenda99(t) +} + +// BenchmarkEDAddAddenda99 benchmarks validating adding Addenda99 to an entry detail +func BenchmarkEDAddAddenda99(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDAddAddenda99(b) + } +} + +// testEDAddAddenda99Twice validates only one Addenda99 is added to an entry detail +func testEDAddAddenda99Twice(t testing.TB) { entry := mockEntryDetail() entry.AddAddenda(mockAddenda99()) entry.AddAddenda(mockAddenda99()) @@ -315,7 +582,21 @@ func TestEDAddAddendaAddenda99Twice(t *testing.T) { } } -func TestEDCreditOrDebit(t *testing.T) { +// TestEDAddAddenda99Twice tests validating only one Addenda99 is added to an entry detail +func TestEDAddAddenda99Twice(t *testing.T) { + testEDAddAddenda99Twice(t) +} + +// BenchmarkEDAddAddenda99Twice benchmarks validating only one Addenda99 is added to an entry detail +func BenchmarkEDAddAddenda99Twice(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDAddAddenda99Twice(b) + } +} + +// testEDCreditOrDebit validates debit and credit transaction code +func testEDCreditOrDebit(t testing.TB) { // TODO add more credit and debit transaction code's to this test entry := mockEntryDetail() if entry.CreditOrDebit() != "C" { @@ -326,3 +607,16 @@ func TestEDCreditOrDebit(t *testing.T) { t.Errorf("TransactionCode %v expected a Debit(D) got %v", entry.TransactionCode, entry.CreditOrDebit()) } } + +// TestEDCreditOrDebit tests validating debit and credit transaction code +func TestEDCreditOrDebit(t *testing.T) { + testEDCreditOrDebit(t) +} + +// BenchmarkEDCreditOrDebit benchmarks validating debit and credit transaction code +func BenchmarkEDCreditOrDebit(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDCreditOrDebit(b) + } +} \ No newline at end of file diff --git a/fileControl_internal_test.go b/fileControl_internal_test.go index 7f095b925..baef7c892 100644 --- a/fileControl_internal_test.go +++ b/fileControl_internal_test.go @@ -9,6 +9,7 @@ import ( "testing" ) +// mockFileControl create a file control func mockFileControl() FileControl { fc := NewFileControl() fc.BatchCount = 1 @@ -18,7 +19,8 @@ func mockFileControl() FileControl { return fc } -func TestMockFileControl(t *testing.T) { +// testMockFileControl validates a file control record +func testMockFileControl(t testing.TB) { fc := mockFileControl() if err := fc.Validate(); err != nil { t.Error("mockFileControl does not validate and will break other tests") @@ -37,8 +39,21 @@ func TestMockFileControl(t *testing.T) { } } -// TestParseFileControl parses a known File Control Record string. -func TestParseFileControl(t *testing.T) { +// TestMockFileControl tests validating a file control record +func TestMockFileControl(t *testing.T) { + testMockFileControl(t) +} + +// BenchmarkMockFileControl benchmarks validating a file control record +func BenchmarkMockFileControl(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testMockFileControl(b) + } +} + +// testParseFileControl parses a known file control record string +func testParseFileControl(t testing.TB) { var line = "9000001000001000000010005320001000000010500000000000000 " r := NewReader(strings.NewReader(line)) r.line = line @@ -74,8 +89,21 @@ func TestParseFileControl(t *testing.T) { } } -// TestFCString validats that a known parsed file can be return to a string of the same value -func TestFCString(t *testing.T) { +// TestParseFileControl tests parsing a known file control record string +func TestParseFileControl(t *testing.T) { + testParseFileControl(t) +} + +// BenchmarkParseFileControl benchmarks parsing a known file control record string +func BenchmarkParseFileControl(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseFileControl(b) + } +} + +// testFCString validates that a known parsed file can be return to a string of the same value +func testFCString(t testing.TB) { var line = "9000001000001000000010005320001000000010500000000000000 " r := NewReader(strings.NewReader(line)) r.line = line @@ -89,8 +117,21 @@ func TestFCString(t *testing.T) { } } -// TestValidateFCRecordType ensure error if recordType is not 9 -func TestValidateFCRecordType(t *testing.T) { +// TestFCString tests validating that a known parsed file can be return to a string of the same value +func TestFCString(t *testing.T) { + testFCString(t) +} + +// BenchmarkFCString benchmarks validating that a known parsed file can be return to a string of the same value +func BenchmarkFCString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFCString(b) + } +} + +// testValidateFCRecordType validates error if recordType is not 9 +func testValidateFCRecordType(t testing.TB) { fc := mockFileControl() fc.recordType = "2" @@ -103,7 +144,21 @@ func TestValidateFCRecordType(t *testing.T) { } } -func TestFCFieldInclusion(t *testing.T) { +// TestValidateFCRecordType tests validating error if recordType is not 9 +func TestValidateFCRecordType(t *testing.T) { + testValidateFCRecordType(t) +} + +// BenchmarkValidateFCRecordType benchmarks validating error if recordType is not 9 +func BenchmarkValidateFCRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateFCRecordType(b) + } +} + +// testFCFieldInclusion validates file control field inclusion +func testFCFieldInclusion(t testing.TB) { fc := mockFileControl() fc.BatchCount = 0 if err := fc.Validate(); err != nil { @@ -115,7 +170,21 @@ func TestFCFieldInclusion(t *testing.T) { } } -func TestFCFieldInclusionRecordType(t *testing.T) { +// TestFCFieldInclusion tests validating file control field inclusion +func TestFCFieldInclusion(t *testing.T) { + testFCFieldInclusion(t) +} + +// BenchmarkFCFieldInclusion benchmarks validating file control field inclusion +func BenchmarkFCFieldInclusion(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFCFieldInclusion(b) + } +} + +// testFCFieldInclusionRecordType validates file control record type field inclusion +func testFCFieldInclusionRecordType(t testing.TB) { fc := mockFileControl() fc.recordType = "" if err := fc.Validate(); err != nil { @@ -127,7 +196,21 @@ func TestFCFieldInclusionRecordType(t *testing.T) { } } -func TestFCFieldInclusionBlockCount(t *testing.T) { +// TestFCFieldInclusionRecordType tests validating file control record type field inclusion +func TestFCFieldInclusionRecordType(t *testing.T) { + testFCFieldInclusionRecordType(t) +} + +// BenchmarkFCFieldInclusionRecordType benchmarks tests validating file control record type field inclusion +func BenchmarkFCFieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFCFieldInclusionRecordType(b) + } +} + +// testFCFieldInclusionBlockCount validates file control block count field inclusion +func testFCFieldInclusionBlockCount(t testing.TB) { fc := mockFileControl() fc.BlockCount = 0 if err := fc.Validate(); err != nil { @@ -139,7 +222,21 @@ func TestFCFieldInclusionBlockCount(t *testing.T) { } } -func TestFCFieldInclusionEntryAddendaCount(t *testing.T) { +// TestFCFieldInclusionBlockCount tests validating file control block count field inclusion +func TestFCFieldInclusionBlockCount(t *testing.T) { + testFCFieldInclusionBlockCount(t) +} + +// BenchmarkFCFieldInclusionBlockCoun benchmarks validating file control block count field inclusion +func BenchmarkFCFieldInclusionBlockCoun(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFCFieldInclusionBlockCount(b) + } +} + +// testFCFieldInclusionEntryAddendaCount validates file control addenda count field inclusion +func testFCFieldInclusionEntryAddendaCount(t testing.TB) { fc := mockFileControl() fc.EntryAddendaCount = 0 if err := fc.Validate(); err != nil { @@ -151,7 +248,21 @@ func TestFCFieldInclusionEntryAddendaCount(t *testing.T) { } } -func TestFCFieldInclusionEntryHash(t *testing.T) { +// TestFCFieldInclusionEntryAddendaCount tests validating file control addenda count field inclusion +func TestFCFieldInclusionEntryAddendaCount(t *testing.T) { + testFCFieldInclusionEntryAddendaCount(t) +} + +// BenchmarkFCFieldInclusionEntryAddendaCount benchmarks validating file control addenda count field inclusion +func BenchmarkFCFieldInclusionEntryAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFCFieldInclusionEntryAddendaCount(b) + } +} + +// testFCFieldInclusionEntryHash validates file control entry hash field inclusion +func testFCFieldInclusionEntryHash(t testing.TB) { fc := mockFileControl() fc.EntryHash = 0 if err := fc.Validate(); err != nil { @@ -162,3 +273,16 @@ func TestFCFieldInclusionEntryHash(t *testing.T) { } } } + +// TestFCFieldInclusionEntryHash tests validating file control entry hash field inclusion +func TestFCFieldInclusionEntryHash(t *testing.T) { + testFCFieldInclusionEntryHash(t) +} + +// BenchmarkFCFieldInclusionEntryHash benchmarks validating file control entry hash field inclusion +func BenchmarkFCFieldInclusionEntryHash(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFCFieldInclusionEntryHash(b) + } +} \ No newline at end of file diff --git a/file_test.go b/file_test.go index bf4b5828c..1b3bc5b24 100644 --- a/file_test.go +++ b/file_test.go @@ -8,6 +8,7 @@ import ( "testing" ) +// mockFilePPD creates an ACH file with PPD batch and entry func mockFilePPD() *File { mockFile := NewFile() mockFile.SetHeader(mockFileHeader()) @@ -19,15 +20,29 @@ func mockFilePPD() *File { return mockFile } -func TestFileError(t *testing.T) { +// testFileError validates a file error +func testFileError(t testing.TB) { err := &FileError{FieldName: "mock", Msg: "test message"} if err.Error() != "mock test message" { t.Error("FileError Error has changed formatting") } } -// TestFileBatchCount if calculated count is different from control -func TestFileBatchCount(t *testing.T) { +// TestFileError tests validating a file error +func TestFileError(t *testing.T) { + testFileError(t) +} + +// BenchmarkFileError benchmarks validating a file error +func BenchmarkFileError(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileError(b) + } +} + +// testFileBatchCount validates if calculated count is different from control +func testFileBatchCount(t testing.TB) { file := mockFilePPD() // More batches than the file control count. @@ -43,7 +58,21 @@ func TestFileBatchCount(t *testing.T) { } } -func TestFileEntryAddenda(t *testing.T) { +// TestFileBatchCount tests validating if calculated count is different from control +func TestFileBatchCount(t *testing.T) { + testFileBatchCount(t) +} + +// BenchmarkFileBatchCount benchmarks validating if calculated count is different from control +func BenchmarkFileBatchCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBatchCount(b) + } +} + +// testFileEntryAddenda validates an addenda entry +func testFileEntryAddenda(t testing.TB) { file := mockFilePPD() // more entries than the file control @@ -59,7 +88,21 @@ func TestFileEntryAddenda(t *testing.T) { } } -func TestFileDebitAmount(t *testing.T) { +// TestFileEntryAddenda tests validating an addenda entry +func TestFileEntryAddenda(t *testing.T) { + testFileEntryAddenda(t) +} + +// BenchmarkFileEntryAddenda benchmarks validating an addenda entry +func BenchmarkFileEntryAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileEntryAddenda(b) + } +} + +// testFileDebitAmount validates file total debit amount +func testFileDebitAmount(t testing.TB) { file := mockFilePPD() // inequality in total debit amount @@ -75,10 +118,24 @@ func TestFileDebitAmount(t *testing.T) { } } -func TestFileCreditAmount(t *testing.T) { +// TestFileDebitAmount tests validating file total debit amount +func TestFileDebitAmount(t *testing.T) { + testFileDebitAmount(t) +} + +// BenchmarkFileDebitAmount benchmarks validating file total debit amount +func BenchmarkFileDebitAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileDebitAmount(b) + } +} + +// testFileCreditAmount validates file total credit amount +func testFileCreditAmount(t testing.TB) { file := mockFilePPD() - // inequality in total debit amount + // inequality in total credit amount file.Control.TotalCreditEntryDollarAmountInFile = 63 if err := file.Validate(); err != nil { if e, ok := err.(*FileError); ok { @@ -91,7 +148,21 @@ func TestFileCreditAmount(t *testing.T) { } } -func TestFileEntryHash(t *testing.T) { +// TestFileCreditAmount tests validating file total credit amount +func TestFileCreditAmount(t *testing.T) { + testFileCreditAmount(t) +} + +// BenchmarkFileCreditAmount benchmarks validating file total credit amount +func BenchmarkFileCreditAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileCreditAmount(b) + } +} + +// testFileEntryHash validates entry hash +func testFileEntryHash(t testing.TB) { file := mockFilePPD() file.AddBatch(mockBatchPPD()) file.Create() @@ -107,7 +178,21 @@ func TestFileEntryHash(t *testing.T) { } } -func TestFileBlockCount10(t *testing.T) { +// TestFileEntryHash tests validating entry hash +func TestFileEntryHash(t *testing.T) { + testFileEntryHash(t) +} + +// BenchmarkFileEntryHash benchmarks validating entry hash +func BenchmarkFileEntryHash(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileEntryHash(b) + } +} + +// testFileBlockCount10 validates file block count +func testFileBlockCount10(t testing.TB) { file := NewFile().SetHeader(mockFileHeader()) batch := NewBatchPPD(mockBatchPPDHeader()) batch.AddEntry(mockEntryDetail()) @@ -137,7 +222,21 @@ func TestFileBlockCount10(t *testing.T) { } } -func TestFileBuildBadFileHeader(t *testing.T) { +// TestFileBlockCount10 tests validating file block count +func TestFileBlockCount10(t *testing.T) { + testFileBlockCount10(t) +} + +// BenchmarkFileBlockCount10 benchmarks validating file block count +func BenchmarkFileBlockCount10(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBlockCount10(b) + } +} + +// testFileBuildBadFileHeader validates a bad file header +func testFileBuildBadFileHeader(t testing.TB) { file := NewFile().SetHeader(FileHeader{}) if err := file.Create(); err != nil { if e, ok := err.(*FieldError); ok { @@ -150,7 +249,21 @@ func TestFileBuildBadFileHeader(t *testing.T) { } } -func TestFileBuildNoBatch(t *testing.T) { +// TestFileBuildBadFileHeader tests validating a bad file header +func TestFileBuildBadFileHeader(t *testing.T) { + testFileBuildBadFileHeader(t) +} + +// BenchmarkFileBuildBadFileHeader benchmarks validating a bad file header +func BenchmarkFileBuildBadFileHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBuildBadFileHeader(b) + } +} + +// testFileBuildNoBatch validates a file with no batches +func testFileBuildNoBatch(t testing.TB) { file := NewFile().SetHeader(mockFileHeader()) if err := file.Create(); err != nil { if e, ok := err.(*FileError); ok { @@ -163,8 +276,22 @@ func TestFileBuildNoBatch(t *testing.T) { } } -// Check if a file contains BatchNOC notification of change -func TestFileNotificationOfChange(t *testing.T) { +// TestFileBuildNoBatch tests validating a file with no batches +func TestFileBuildNoBatch(t *testing.T) { + testFileBuildNoBatch(t) +} + +// BenchmarkFileBuildNoBatch benchmarks validating a file with no batches +func BenchmarkFileBuildNoBatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBuildNoBatch(b) + } +} + +// testFileNotificationOfChange validates if a file contains +// BatchNOC notification of change +func testFileNotificationOfChange(t testing.TB) { file := NewFile().SetHeader(mockFileHeader()) file.AddBatch(mockBatchPPD()) bCOR := mockBatchCOR() @@ -176,7 +303,23 @@ func TestFileNotificationOfChange(t *testing.T) { } } -func TestFileReturnEntries(t *testing.T) { +// TestFileNotificationOfChange tests validating if a file contains +// BatchNOC notification of change +func TestFileNotificationOfChange(t *testing.T) { + testFileNotificationOfChange(t) +} + +// BenchmarkFileNotificationOfChange benchmarks validating if a file contains +// BatchNOC notification of change +func BenchmarkFileNotificationOfChange(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileNotificationOfChange(b) + } +} + +// testFileReturnEntries validates file return entries +func testFileReturnEntries(t testing.TB) { // create or copy the entry to be returned record entry := mockEntryDetail() // Add the addenda return with appropriate ReturnCode and addenda information @@ -207,3 +350,16 @@ func TestFileReturnEntries(t *testing.T) { t.Errorf("1 file.ReturnEntries added and %v exist", len(file.ReturnEntries)) } } + +// TestFileReturnEntries tests validating file return entries +func TestFileReturnEntries(t *testing.T) { + testFileReturnEntries(t) +} + +// BenchmarkFileReturnEntries benchmarks validating file return entries +func BenchmarkFileReturnEntries(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileReturnEntries(b) + } +} diff --git a/record_test.go b/record_test.go index 2d4234933..a05454bb0 100644 --- a/record_test.go +++ b/record_test.go @@ -5,7 +5,8 @@ import ( "testing" ) -func TestFileRecord(t *testing.T) { +// testFileRecord validates a file record +func testFileRecord(t testing.TB) { f := NewFile() f.SetHeader(mockFileHeader()) if err := f.Header.Validate(); err != nil { @@ -17,7 +18,21 @@ func TestFileRecord(t *testing.T) { } } -func TestBatchRecord(t *testing.T) { +// TestFileRecord tests validating a file record +func TestFileRecord(t *testing.T) { + testFileRecord(t) +} + +// BenchmarkFileRecord benchmarks validating a file record +func BenchmarkFileRecord(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileRecord(b) + } +} + +// testBatchRecord validates a batch record +func testBatchRecord(t testing.TB) { companyName := "ACME Corporation" batch, _ := NewBatch(mockBatchPPDHeader()) @@ -30,7 +45,21 @@ func TestBatchRecord(t *testing.T) { } } -func TestEntryDetail(t *testing.T) { +// TestBatchRecord tests validating a batch record +func TestBatchRecord(t *testing.T) { + testBatchRecord(t) +} + +// BenchmarkBatchRecord benchmarks validating a batch record +func BenchmarkBatchRecord(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRecord(b) + } +} + +// testEntryDetail validates an entry detail record +func testEntryDetail(t testing.TB) { entry := mockEntryDetail() //override mockEntryDetail entry.TransactionCode = 27 @@ -40,19 +69,45 @@ func TestEntryDetail(t *testing.T) { } } -func TestEntryDetailPaymentType(t *testing.T) { +// TestEntryDetail tests validating an entry detail record +func TestEntryDetail(t *testing.T) { + testEntryDetail(t) +} + +// BenchmarkEntryDetail benchmarks validating an entry detail record +func BenchmarkEntryDetail(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEntryDetail(b) + } +} +// testEntryDetailPaymentType validates an entry detail record payment type +func testEntryDetailPaymentType(t testing.TB) { entry := mockEntryDetail() //override mockEntryDetail entry.TransactionCode = 27 entry.DiscretionaryData = "R" - if err := entry.Validate(); err != nil { t.Errorf("%T: %s", err, err) } } -func TestEntryDetailReceivingCompany(t *testing.T) { +// TestEntryDetailPaymentType tests validating an entry detail record payment type +func TestEntryDetailPaymentType(t *testing.T) { + testEntryDetailPaymentType(t) +} + +// BenchmarkEntryDetailPaymentType benchmarks validating an entry detail record payment type +func BenchmarkEntryDetailPaymentType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEntryDetailPaymentType(b) + } +} + +// testEntryDetailReceivingCompany validates an entry detail record receiving company +func testEntryDetailReceivingCompany(t testing.TB) { entry := mockEntryDetail() //override mockEntryDetail entry.TransactionCode = 27 @@ -64,7 +119,21 @@ func TestEntryDetailReceivingCompany(t *testing.T) { } } -func TestAddendaRecord(t *testing.T) { +// TestEntryDetailReceivingCompany tests validating an entry detail record receiving company +func TestEntryDetailReceivingCompany(t *testing.T) { + testEntryDetailReceivingCompany(t) +} + +// BenchmarkEntryDetailReceivingCompany benchmarks validating an entry detail record receiving company +func BenchmarkEntryDetailReceivingCompany(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEntryDetailReceivingCompany(b) + } +} + +// testAddendaRecord validates an addenda record +func testAddendaRecord(t testing.TB) { addenda05 := NewAddenda05() addenda05.PaymentRelatedInformation = "Currently string needs ASC X12 Interchange Control Structures" addenda05.SequenceNumber = 1 @@ -75,7 +144,21 @@ func TestAddendaRecord(t *testing.T) { } } -func TestBuildFile(t *testing.T) { +// TestAddendaRecord tests validating an addenda record +func TestAddendaRecord(t *testing.T) { + testAddendaRecord(t) +} + +// BenchmarkAddendaRecord benchmarks validating an addenda record +func BenchmarkAddendaRecord(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddendaRecord(b) + } +} + +// testBuildFile validates building a file +func testBuildFile(t testing.TB) { // To create a file file := NewFile() file.SetHeader(mockFileHeader()) @@ -140,3 +223,16 @@ func TestBuildFile(t *testing.T) { } w.Flush() } + +// TestBuildFile tests validating building a file +func TestBuildFile(t *testing.T) { + testBuildFile(t) +} + +// BenchmarkBuildFile benchmarks validating building a file +func BenchmarkBuildFile(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBuildFile(b) + } +} diff --git a/writer_test.go b/writer_test.go index b2e14fd79..efc2e2f47 100644 --- a/writer_test.go +++ b/writer_test.go @@ -6,7 +6,8 @@ import ( "testing" ) -func TestPPDWrite(t *testing.T) { +// testPPDWrite writes a PPD ACH file +func testPPDWrite(t testing.TB) { file := NewFile().SetHeader(mockFileHeader()) entry := mockEntryDetail() entry.AddAddenda(mockAddenda05()) @@ -38,3 +39,16 @@ func TestPPDWrite(t *testing.T) { t.Errorf("%T: %s", err, err) } } + +// TestPPDWrite tests writing a PPD ACH file +func TestPPDWrite(t *testing.T) { + testPPDWrite(t) +} + +// BenchmarkPPDWrite benchmarks writing a PPD ACH file +func BenchmarkPPDWrite(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testPPDWrite(b) + } +} From 05f5488c13eb950d255a7c4d86b8d683e51874a4 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 21 May 2018 13:26:55 -0400 Subject: [PATCH 0117/1694] #166 benchmarks added #166 benchmarks added --- reader_test.go | 445 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 407 insertions(+), 38 deletions(-) diff --git a/reader_test.go b/reader_test.go index 7eda2d8c2..b87b5ceef 100644 --- a/reader_test.go +++ b/reader_test.go @@ -10,7 +10,8 @@ import ( "testing" ) -func TestParseError(t *testing.T) { +// testParseError validates a a parsing error +func testParseError(t testing.TB) { e := &FieldError{FieldName: "testField", Value: "nil", Msg: "could not parse"} err := &ParseError{Line: 63, Err: e} if err.Error() != "line:63 *ach.FieldError testField nil could not parse" { @@ -22,8 +23,21 @@ func TestParseError(t *testing.T) { } } -// TestDecode is a complete file decoding test. A canary test -func TestPPDDebitRead(t *testing.T) { +// TestParseError tests validating a a parsing error +func TestParseError(t *testing.T) { + testParseError(t) +} + +// BenchmarkParseError benchmarks validating a a parsing error +func BenchmarkParseError(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseError(b) + } +} + +// testPPDDebitRead validates reading a PPD debit +func testPPDDebitRead(t testing.TB) { f, err := os.Open("./testdata/ppd-debit.ach") if err != nil { t.Errorf("%T: %s", err, err) @@ -39,7 +53,21 @@ func TestPPDDebitRead(t *testing.T) { } } -func TestWEBDebitRead(t *testing.T) { +// TestPPDDebitRead tests validating reading a PPD debit +func TestPPDDebitRead(t *testing.T) { + testPPDDebitRead(t) +} + +// BenchmarkPPDDebitRead benchmarks validating reading a PPD debit +func BenchmarkPPDDebitRead(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testPPDDebitRead(b) + } +} + +// testWEBDebitRead validates reading a WEB debit +func testWEBDebitRead(t testing.TB) { f, err := os.Open("./testdata/web-debit.ach") if err != nil { t.Errorf("%T: %s", err, err) @@ -55,8 +83,21 @@ func TestWEBDebitRead(t *testing.T) { } } -// TestDecode is a complete file decoding test. A canary test -func TestPPDDebitFixedLengthRead(t *testing.T) { +// TestWEBDebitRead tests validating reading a WEB debit +func TestWEBDebitRead (t *testing.T) { + testWEBDebitRead (t) +} + +// BenchmarkWEBDebitRead benchmarks validating reading a WEB debit +func BenchmarkWEBDebitRead (b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testWEBDebitRead (b) + } +} + +// testPPDDebitFixedLengthRead validates reading a PPD debit fixed width length +func testPPDDebitFixedLengthRead(t testing.TB) { f, err := os.Open("./testdata/ppd-debit-fixedLength.ach") if err != nil { t.Errorf("%T: %s", err, err) @@ -69,7 +110,21 @@ func TestPPDDebitFixedLengthRead(t *testing.T) { } } -func TestRecordTypeUnknown(t *testing.T) { +// TestPPDDebitFixedLengthRead test validates reading a PPD debit fixed width length +func TestPPDDebitFixedLengthRead(t *testing.T) { + testPPDDebitFixedLengthRead(t) +} + +// BenchmarkPPDDebitFixedLengthRead benchmark validates reading a PPD debit fixed width length +func BenchmarkPPDDebitFixedLengthRead(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testPPDDebitFixedLengthRead(b) + } +} + +// testRecordTypeUnknown validates record type unknown +func testRecordTypeUnknown(t testing.TB) { var line = "301 076401251 0764012510807291511A094101achdestname companyname " r := NewReader(strings.NewReader(line)) _, err := r.Read() @@ -84,7 +139,21 @@ func TestRecordTypeUnknown(t *testing.T) { } } -func TestTwoFileHeaders(t *testing.T) { +// TestRecordTypeUnknown tests validating record type unknown +func TestRecordTypeUnknown(t *testing.T) { + testRecordTypeUnknown(t) +} + +// BenchmarkRecordTypeUnknown benchmarks validating record type unknown +func BenchmarkRecordTypeUnknown(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRecordTypeUnknown(b) + } +} + +// testTwoFileHeaders validates one file header +func testTwoFileHeaders(t testing.TB) { var line = "101 076401251 0764012510807291511A094101achdestname companyname " var twoHeaders = line + "\n" + line r := NewReader(strings.NewReader(twoHeaders)) @@ -100,7 +169,21 @@ func TestTwoFileHeaders(t *testing.T) { } } -func TestTwoFileControls(t *testing.T) { +// TestTwoFileHeaders tests validating one file header +func TestTwoFileHeaders(t *testing.T) { + testTwoFileHeaders(t) +} + +// BenchmarkTwoFileHeaders benchmarks +func BenchmarkTwoFileHeaders(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testTwoFileHeaders(b) + } +} + +// testTwoFileControls validates one file control +func testTwoFileControls(t testing.TB) { var line = "9000001000001000000010005320001000000010500000000000000 " var twoControls = line + "\n" + line r := NewReader(strings.NewReader(twoControls)) @@ -124,7 +207,21 @@ func TestTwoFileControls(t *testing.T) { } } -func TestFileLineShort(t *testing.T) { +// TestTwoFileControls tests validating one file control +func TestTwoFileControls(t *testing.T) { + testTwoFileControls(t) +} + +// BenchmarkTwoFileControls benchmarks validating one file control +func BenchmarkTwoFileControls(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testTwoFileControls(b) + } +} + +// testFileLineShort validates file line is short +func testFileLineShort(t testing.TB) { var line = "1 line is only 70 characters ........................................!" r := NewReader(strings.NewReader(line)) _, err := r.Read() @@ -139,7 +236,21 @@ func TestFileLineShort(t *testing.T) { } } -func TestFileLineLong(t *testing.T) { +// TestFileLineShort tests validating file line is short +func TestFileLineShort(t *testing.T) { + testFileLineShort(t) +} + +// BenchmarkFileLineShort benchmarks validating file line is short +func BenchmarkFileLineShort(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileLineShort(b) + } +} + +// testFileLineLong validates file line is long +func testFileLineLong(t testing.TB) { var line = "1 line is 100 characters ..........................................................................!" r := NewReader(strings.NewReader(line)) _, err := r.Read() @@ -154,8 +265,21 @@ func TestFileLineLong(t *testing.T) { } } -// TestFileFileHeaderErr ensure a parse validation error flows back from the parser. -func TestFileFileHeaderErr(t *testing.T) { +// TestFileLineLong tests validating file line is long +func TestFileLineLong(t *testing.T) { + testFileLineLong(t) +} + +// BenchmarkFileLineLong benchmarks validating file line is long +func BenchmarkFileLineLong(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileLineLong(b) + } +} + +// testFileFileHeaderErr validates error flows back from the parser +func testFileFileHeaderErr(t testing.TB) { fh := mockFileHeader() //fh.ImmediateOrigin = "0" fh.ImmediateOrigin = "" @@ -174,8 +298,21 @@ func TestFileFileHeaderErr(t *testing.T) { } } -// TestFileBatchHeaderErr ensure a parse validation error flows back from the parser. -func TestFileBatchHeaderErr(t *testing.T) { +// TestFileFileHeaderErr tests validating error flows back from the parser +func TestFileFileHeaderErr(t *testing.T) { + testFileFileHeaderErr(t) +} + +// BenchmarkFileFileHeaderErr benchmarks validating error flows back from the parse +func BenchmarkFileFileHeaderErr(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileFileHeaderErr(b) + } +} + +// testFileBatchHeaderErr validates error flows back from the parser +func testFileBatchHeaderErr(t testing.TB) { bh := mockBatchHeader() //bh.ODFIIdentification = 0 bh.ODFIIdentification = "" @@ -192,8 +329,21 @@ func TestFileBatchHeaderErr(t *testing.T) { } } -// TestFileBatchHeaderErr Error when two batch headers exists in a current batch -func TestFileBatchHeaderDuplicate(t *testing.T) { +// TestFileBatchHeaderErr tests validating error flows back from the parser +func TestFileBatchHeaderErr(t *testing.T) { + testFileBatchHeaderErr(t) +} + +// BenchmarkFileBatchHeaderErr benchmarks validating error flows back from the parser +func BenchmarkFileBatchHeaderErr(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBatchHeaderErr(b) + } +} + +// testFileBatchHeaderDuplicate validates when two batch headers exists in a current batch +func testFileBatchHeaderDuplicate(t testing.TB) { // create a new Batch header string bh := mockBatchPPDHeader() r := NewReader(strings.NewReader(bh.String())) @@ -212,8 +362,21 @@ func TestFileBatchHeaderDuplicate(t *testing.T) { } } -// TestFileEntryDetailOutsideBatch ensure EntryDetail does not exist outside of Batch -func TestFileEntryDetailOutsideBatch(t *testing.T) { +// TestFileBatchHeaderDuplicate tests validating when two batch headers exists in a current batch +func TestFileBatchHeaderDuplicate(t *testing.T) { + testFileBatchHeaderDuplicate(t) +} + +// BenchmarkFileBatchHeaderDuplicate benchmarks validating when two batch headers exists in a current batch +func BenchmarkFileBatchHeaderDuplicate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBatchHeaderDuplicate(b) + } +} + +// testFileEntryDetailOutsideBatch validates entry detail does not exist outside of batch +func testFileEntryDetailOutsideBatch(t testing.TB) { ed := mockEntryDetail() r := NewReader(strings.NewReader(ed.String())) _, err := r.Read() @@ -228,8 +391,21 @@ func TestFileEntryDetailOutsideBatch(t *testing.T) { } } -// TestFileEntryDetail validation error populates through the reader -func TestFileEntryDetail(t *testing.T) { +// TestFileEntryDetailOutsideBatch tests validating entry detail does not exist outside of batch +func TestFileEntryDetailOutsideBatch(t *testing.T) { + testFileEntryDetailOutsideBatch(t) +} + +// BenchmarkFileEntryDetailOutsideBatch benchmarks validating entry detail does not exist outside of batch +func BenchmarkFileEntryDetailOutsideBatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileEntryDetailOutsideBatch(b) + } +} + +// testFileEntryDetail validates error populates through the reader +func testFileEntryDetail(t testing.TB) { ed := mockEntryDetail() ed.TransactionCode = 0 line := ed.String() @@ -248,8 +424,21 @@ func TestFileEntryDetail(t *testing.T) { } } -// TestFileAddenda05 validation error populates through the reader -func TestFileAddenda05(t *testing.T) { +// TestFileEntryDetail tests validating error populates through the reader +func TestFileEntryDetail(t *testing.T) { + testFileEntryDetail(t) +} + +// BenchmarkFileEntryDetail benchmarks validating error populates through the reader +func BenchmarkFileEntryDetail(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileEntryDetail(b) + } +} + +// testFileAddenda05 validates addenda 05 +func testFileAddenda05(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() addenda := mockAddenda05() @@ -271,7 +460,21 @@ func TestFileAddenda05(t *testing.T) { } } -func TestFileAddenda98(t *testing.T) { +// TestFileAddenda05 tests validating addenda 05 +func TestFileAddenda05(t *testing.T) { + testFileAddenda05(t) +} + +// BenchmarkFileAddenda05 benchmarks validating addenda 05 +func BenchmarkFileAddenda05(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddenda05(b) + } +} + +// testFileAddenda98 validates addenda 98 +func testFileAddenda98(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() addenda := mockAddenda98() @@ -296,7 +499,22 @@ func TestFileAddenda98(t *testing.T) { } } -func TestFileAddenda99(t *testing.T) { +// TestFileAddenda98 tests validating addenda 98 +func TestFileAddenda98(t *testing.T) { + testFileAddenda98(t) +} + +// BenchmarkFileAddenda98 benchmarks validating addenda 98 +func BenchmarkFileAddenda98(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddenda98(b) + } +} + + +// testFileAddenda99 validates addenda 99 +func testFileAddenda99(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() addenda := mockAddenda99() @@ -319,8 +537,21 @@ func TestFileAddenda99(t *testing.T) { } } -// TestFileAddendaOutsideBatch validation error populates through the reader -func TestFileAddendaOutsideBatch(t *testing.T) { +// TestFileAddenda99 tests validating addenda 99 +func TestFileAddenda99(t *testing.T) { + testFileAddenda99(t) +} + +// BenchmarkFileAddenda99 benchmarks validating addenda 99 +func BenchmarkFileAddenda99(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddenda99(b) + } +} + +// testFileAddendaOutsideBatch validates error populates through the reader +func testFileAddendaOutsideBatch(t testing.TB) { addenda := mockAddenda05() r := NewReader(strings.NewReader(addenda.String())) _, err := r.Read() @@ -337,8 +568,21 @@ func TestFileAddendaOutsideBatch(t *testing.T) { } } -// TestFileAddendaNoIndicator -func TestFileAddendaNoIndicator(t *testing.T) { +// TestFileAddendaOutsideBatch tests validating error populates through the reader +func TestFileAddendaOutsideBatch(t *testing.T) { + testFileAddendaOutsideBatch(t) +} + +// BenchmarkFileAddendaOutsideBatch benchmarks validating error populates through the reader +func BenchmarkFileAddendaOutsideBatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddendaOutsideBatch(b) + } +} + +// testFileAddendaNoIndicator validates no addenda indicator +func testFileAddendaNoIndicator(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() addenda := mockAddenda05() @@ -356,7 +600,21 @@ func TestFileAddendaNoIndicator(t *testing.T) { } } -func TestFileFileControlErr(t *testing.T) { +// TestFileAddendaNoIndicator tests validating no addenda indicator +func TestFileAddendaNoIndicator(t *testing.T) { + testFileAddendaNoIndicator(t) +} + +// BenchmarkFileAddendaNoIndicator benchmarks validating no addenda indicator +func BenchmarkFileAddendaNoIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddendaNoIndicator(b) + } +} + +// testFileFileControlErr validates a file control error +func testFileFileControlErr(t testing.TB) { fc := mockFileControl() fc.BatchCount = 0 r := NewReader(strings.NewReader(fc.String())) @@ -371,7 +629,22 @@ func TestFileFileControlErr(t *testing.T) { t.Errorf("%T: %s", err, err) } } -func TestFileBatchHeaderSEC(t *testing.T) { + +// TestFileFileControlErr tests validating a file control error +func TestFileFileControlErr(t *testing.T) { + testFileFileControlErr(t) +} + +// BenchmarkFileFileControlErr benchmarks validating a file control error +func BenchmarkFileFileControlErr(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileFileControlErr(b) + } +} + +// testFileBatchHeaderSEC validates batch header SEC +func testFileBatchHeaderSEC(t testing.TB) { bh := mockBatchHeader() bh.StandardEntryClassCode = "ABC" r := NewReader(strings.NewReader(bh.String())) @@ -387,7 +660,21 @@ func TestFileBatchHeaderSEC(t *testing.T) { } } -func TestFileFileControlNoCurrentBatch(t *testing.T) { +// TestFileBatchHeaderSEC tests validating batch header SEC +func TestFileBatchHeaderSEC(t *testing.T) { + testFileBatchHeaderSEC(t) +} + +// BenchmarkFileBatchHeaderSEC benchmarks validating batch header SEC +func BenchmarkFileBatchHeaderSEC(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBatchHeaderSEC(b) + } +} + +// testFileFileControlNoCurrentBatch validates no current batch +func testFileFileControlNoCurrentBatch(t testing.TB) { bc := mockBatchControl() r := NewReader(strings.NewReader(bc.String())) _, err := r.Read() @@ -400,7 +687,20 @@ func TestFileFileControlNoCurrentBatch(t *testing.T) { } } -func TestFileBatchControlValidate(t *testing.T) { +// TestFileFileControlNoCurrentBatch tests validating no current batch +func TestFileFileControlNoCurrentBatch(t *testing.T) { + testFileFileControlNoCurrentBatch(t) +} + +// BenchmarkFileFileControlNoCurrentBatch benchmarks validating no current batch +func BenchmarkFileFileControlNoCurrentBatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileFileControlNoCurrentBatch(b) + } +} +// testFileBatchControlValidate validates a batch control +func testFileBatchControlValidate(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() bc := mockBatchControl() @@ -419,7 +719,21 @@ func TestFileBatchControlValidate(t *testing.T) { } } -func TestFileAddBatchValidation(t *testing.T) { +// TestFileBatchControlValidate tests validating a batch control +func TestFileBatchControlValidate(t *testing.T) { + testFileBatchControlValidate(t) +} + +// BenchmarkFileBatchControlValidate benchmarks validating a batch control +func BenchmarkFileBatchControlValidate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileBatchControlValidate(b) + } +} + +// testFileAddBatchValidation validates a batch +func testFileAddBatchValidation(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() bc := mockBatchControl() @@ -437,8 +751,21 @@ func TestFileAddBatchValidation(t *testing.T) { } } -// TestFileLongErr Batch Header Service Class is 000 which does not validate -func TestFileLongErr(t *testing.T) { +// TestFileAddBatchValidation tests validating a batch +func TestFileAddBatchValidation(t *testing.T) { + testFileAddBatchValidation(t) +} + +// BenchmarkFileAddBatchValidation benchmarks validating a batch +func BenchmarkFileAddBatchValidation(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddBatchValidation(b) + } +} + +// testFileLongErr Batch Header Service Class is 000 which does not validate +func testFileLongErr(t testing.TB) { line := "101 076401251 0764012510807291511A094101achdestname companyname 5000companyname origid PPDCHECKPAYMT000002080730 1076401250000001" r := NewReader(strings.NewReader(line)) _, err := r.Read() @@ -451,7 +778,21 @@ func TestFileLongErr(t *testing.T) { } } -func TestFileAddendaOutsideEntry(t *testing.T) { +// TestFileLongErr tests Batch Header Service Class is 000 which does not validate +func TestFileLongErr(t *testing.T) { + testFileLongErr(t) +} + +// BenchmarkFileLongErr benchmarks Batch Header Service Class is 000 which does not validate +func BenchmarkFileLongErr(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileLongErr(b) + } +} + +// testFileAddendaOutsideEntry validates an addenda is within an entry detail +func testFileAddendaOutsideEntry(t testing.TB) { bh := mockBatchHeader() addenda := mockAddenda05() line := bh.String() + "\n" + addenda.String() @@ -468,7 +809,21 @@ func TestFileAddendaOutsideEntry(t *testing.T) { } } -func TestFileFHImmediateOrigin(t *testing.T) { +// TestFileAddendaOutsideEntry tests validating an addenda is within an entry detail +func TestFileAddendaOutsideEntry(t *testing.T) { + testFileAddendaOutsideEntry(t) +} + +// BenchmarkFileAddendaOutsideEntry benchmarks validating an addenda is within an entry detail +func BenchmarkFileAddendaOutsideEntry(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddendaOutsideEntry(b) + } +} + +// testFileFHImmediateOrigin validates file header immediate origin +func testFileFHImmediateOrigin(t testing.TB) { fh := mockFileHeader() fh.ImmediateDestination = "" r := NewReader(strings.NewReader(fh.String())) @@ -485,3 +840,17 @@ func TestFileFHImmediateOrigin(t *testing.T) { t.Errorf("%T: %s", err, err) } } + + +// TestFileFHImmediateOrigin tests validating file header immediate origin +func TestFileFHImmediateOrigin(t *testing.T) { + testFileFHImmediateOrigin(t) +} + +// BenchmarkFileFHImmediateOrigin benchmarks validating file header immediate origin +func BenchmarkFileFHImmediateOrigin(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileFHImmediateOrigin(b) + } +} \ No newline at end of file From 398bebccde89562ecc30ef64b2d69ef92b337b3c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 21 May 2018 14:11:17 -0400 Subject: [PATCH 0118/1694] #166 benchmarks added #166 benchmarks added --- fileHeader_internal_test.go | 329 ++++++++++++++++++++++++++++++++---- 1 file changed, 299 insertions(+), 30 deletions(-) diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index cb649cfb5..c0ac349e1 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -21,7 +21,8 @@ func mockFileHeader() FileHeader { return fh } -func TestMockFileHeader(t *testing.T) { +// testMockFileHeader validates a file header +func testMockFileHeader(t testing.TB) { fh := mockFileHeader() if err := fh.Validate(); err != nil { t.Error("mockFileHeader does not validate and will break other tests") @@ -40,18 +41,21 @@ func TestMockFileHeader(t *testing.T) { } } -// TestParseFileHeader parses a known File Header Record string. -func TestParseFileHeader(t *testing.T) { - parseFileHeader(t) +// TestMockFileHeader tests validating a file header +func TestMockFileHeader(t *testing.T) { + testMockFileHeader(t) } -func BenchmarkParseFileHeader(b *testing.B) { +// BenchmarkMockFileHeader benchmarks validating a file header +func BenchmarkMockFileHeader(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - parseFileHeader(b) + testMockFileHeader(b) } } + +// parseFileHeader validates parsing a file header func parseFileHeader(t testing.TB) { var line = "101 076401251 0764012510807291511A094101achdestname companyname " r := NewReader(strings.NewReader(line)) @@ -104,8 +108,21 @@ func parseFileHeader(t testing.TB) { } } -// TestString validats that a known parsed file can be return to a string of the same value -func TestFHString(t *testing.T) { +// TestParseFileHeader test validates parsing a file header +func TestParseFileHeader(t *testing.T) { + parseFileHeader(t) +} + +// BenchmarkParseFileHeader benchmark validates parsing a file header +func BenchmarkParseFileHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + parseFileHeader(b) + } +} + +// testFHString validates that a known parsed file can return to a string of the same value +func testFHString(t testing.TB) { var line = "101 076401251 0764012510807291511A094101achdestname companyname " r := NewReader(strings.NewReader(line)) r.line = line @@ -119,8 +136,22 @@ func TestFHString(t *testing.T) { } } -// TestValidateFHRecordType ensure error if recordType is not 1 -func TestValidateFHRecordType(t *testing.T) { +// TestFHString tests validating that a known parsed file can return to a string of the same value +func TestFHString(t *testing.T) { + testFHString(t) +} + +// BenchmarkFHString benchmarks validating that a known parsed file +// can return to a string of the same value +func BenchmarkFHString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHString(b) + } +} + +// testValidateFHRecordType validates error if record type is not 1 +func testValidateFHRecordType(t testing.TB) { fh := mockFileHeader() fh.recordType = "2" if err := fh.Validate(); err != nil { @@ -132,8 +163,21 @@ func TestValidateFHRecordType(t *testing.T) { } } -// TestValidateIDModifier ensure ID Modiier is upper alphanumeric -func TestValidateIDModifier(t *testing.T) { +// TestValidateFHRecordType tests validating error if record type is not 1 +func TestValidateFHRecordType(t *testing.T) { + testValidateFHRecordType(t) +} + +// BenchmarkValidateFHRecordType benchmarks validating error if record type is not 1 +func BenchmarkValidateFHRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateFHRecordType(b) + } +} + +// testValidateIDModifier validates ID modifier is upper alphanumeric +func testValidateIDModifier(t testing.TB) { fh := mockFileHeader() fh.FileIDModifier = "®" if err := fh.Validate(); err != nil { @@ -145,8 +189,22 @@ func TestValidateIDModifier(t *testing.T) { } } -// TestValidateRecordSize ensure record size is "094" -func TestValidateRecordSize(t *testing.T) { + +// TestValidateIDModifier tests validating ID modifier is upper alphanumeric +func TestValidateIDModifier(t *testing.T) { + testValidateIDModifier(t) +} + +// BenchmarkValidateIDModifier benchmarks validating ID modifier is upper alphanumeric +func BenchmarkValidateIDModifier(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIDModifier(b) + } +} + +// testValidateRecordSize validates record size is "094" +func testValidateRecordSize(t testing.TB) { fh := mockFileHeader() fh.recordSize = "666" if err := fh.Validate(); err != nil { @@ -158,8 +216,21 @@ func TestValidateRecordSize(t *testing.T) { } } -// TestBlockingFactor ensure blocking factor is "10" -func TestBlockingFactor(t *testing.T) { +// TestValidateRecordSize tests validating record size is "094" +func TestValidateRecordSize(t *testing.T) { + testValidateRecordSize(t) +} + +// BenchmarkValidateRecordSize benchmarks validating record size is "094" +func BenchmarkValidateRecordSize(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateRecordSize(b) + } +} + +// testBlockingFactor validates blocking factor is "10" +func testBlockingFactor(t testing.TB) { fh := mockFileHeader() fh.blockingFactor = "99" if err := fh.Validate(); err != nil { @@ -171,8 +242,21 @@ func TestBlockingFactor(t *testing.T) { } } -// TestFormatCode ensure format code is "1" -func TestFormatCode(t *testing.T) { +// TestBlockingFactor tests validating blocking factor is "10" +func TestBlockingFactor(t *testing.T) { + testBlockingFactor(t) +} + +// BenchmarkBlockingFactor benchmarks validating blocking factor is "10" +func BenchmarkBlockingFactor(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBlockingFactor(b) + } +} + +// testFormatCode validates format code is "1" +func testFormatCode(t testing.TB) { fh := mockFileHeader() fh.formatCode = "2" if err := fh.Validate(); err != nil { @@ -184,7 +268,21 @@ func TestFormatCode(t *testing.T) { } } -func TestFHFieldInclusion(t *testing.T) { +// TestFormatCode tests validating format code is "1" +func TestFormatCode(t *testing.T) { + testFormatCode(t) +} + +// BenchmarkFormatCode benchmarks validating format code is "1" +func BenchmarkFormatCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFormatCode(b) + } +} + +// testFHFieldInclusion validates file header field inclusion +func testFHFieldInclusion(t testing.TB) { fh := mockFileHeader() fh.ImmediateOrigin = "" if err := fh.Validate(); err != nil { @@ -196,7 +294,21 @@ func TestFHFieldInclusion(t *testing.T) { } } -func TestUpperLengthFileID(t *testing.T) { +// TestFHFieldInclusion tests validating file header field inclusion +func TestFHFieldInclusion(t *testing.T) { + testFHFieldInclusion(t) +} + +// BenchmarkFHFieldInclusion benchmarks validating file header field inclusion +func BenchmarkFHFieldInclusion(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusion(b) + } +} + +// testUpperLengthFileID validates file ID +func testUpperLengthFileID(t testing.TB) { fh := mockFileHeader() fh.FileIDModifier = "a" if err := fh.Validate(); err != nil { @@ -217,7 +329,21 @@ func TestUpperLengthFileID(t *testing.T) { } } -func TestImmediateDestinationNameAlphaNumeric(t *testing.T) { +// TestUpperLengthFileID tests validating file ID +func TestUpperLengthFileID (t *testing.T) { + testUpperLengthFileID (t) +} + +// BenchmarkUpperLengthFileID benchmarks validating file ID +func BenchmarkUpperLengthFileID (b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testUpperLengthFileID (b) + } +} + +// testImmediateDestinationNameAlphaNumeric validates immediate destination name is alphanumeric +func testImmediateDestinationNameAlphaNumeric(t testing.TB) { fh := mockFileHeader() fh.ImmediateDestinationName = "Super Big Bank" fh.ImmediateDestinationName = "Big ®$$ Bank" @@ -230,7 +356,23 @@ func TestImmediateDestinationNameAlphaNumeric(t *testing.T) { } } -func TestImmediateOriginNameAlphaNumeric(t *testing.T) { +// TestImmediateDestinationNameAlphaNumeric tests validating +// immediate destination name is alphanumeric +func TestImmediateDestinationNameAlphaNumeric(t *testing.T) { + testImmediateDestinationNameAlphaNumeric(t) +} + +// BenchmarkImmediateDestinationNameAlphaNumeric benchmarks validating +// immediate destination name is alphanumeric +func BenchmarkImmediateDestinationNameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testImmediateDestinationNameAlphaNumeric(b) + } +} + +// testImmediateOriginNameAlphaNumeric validates immediate origin name is alphanumeric +func testImmediateOriginNameAlphaNumeric(t testing.TB) { fh := mockFileHeader() fh.ImmediateOriginName = "Super Big Bank" fh.ImmediateOriginName = "Bigger ®$$ Bank" @@ -243,7 +385,22 @@ func TestImmediateOriginNameAlphaNumeric(t *testing.T) { } } -func TestImmediateReferenceCodeAlphaNumeric(t *testing.T) { +// TestImmediateOriginNameAlphaNumeric tests validating immediate origin name is alphanumeric +func TestImmediateOriginNameAlphaNumeric(t *testing.T) { + testImmediateOriginNameAlphaNumeric(t) +} + +// BenchmarkImmediateOriginNameAlphaNumeric benchmarks validating +// immediate origin name is alphanumeric +func BenchmarkImmediateOriginNameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testImmediateOriginNameAlphaNumeric(b) + } +} + +// testImmediateReferenceCodeAlphaNumeric validates immediate reference is alphanumeric +func testImmediateReferenceCodeAlphaNumeric(t testing.TB) { fh := mockFileHeader() fh.ReferenceCode = " " fh.ReferenceCode = "®" @@ -256,7 +413,21 @@ func TestImmediateReferenceCodeAlphaNumeric(t *testing.T) { } } -func TestFHFieldInclusionRecordType(t *testing.T) { +// TestImmediateReferenceCodeAlphaNumeric tests validating immediate reference is alphanumeric +func TestImmediateReferenceCodeAlphaNumeric(t *testing.T) { + testImmediateReferenceCodeAlphaNumeric(t) +} + +// BenchmarkImmediateReferenceCodeAlphaNumeric benchmarks validating immediate reference is alphanumeric +func BenchmarkImmediateReferenceCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testImmediateReferenceCodeAlphaNumeric(b) + } +} + +// testFHFieldInclusionRecordType validates field inclusion +func testFHFieldInclusionRecordType(t testing.TB) { fh := mockFileHeader() fh.recordType = "" if err := fh.Validate(); err != nil { @@ -268,7 +439,21 @@ func TestFHFieldInclusionRecordType(t *testing.T) { } } -func TestFHFieldInclusionImmediatDestination(t *testing.T) { +// TestFHFieldInclusionRecordType tests validating field inclusion +func TestFHFieldInclusionRecordType(t *testing.T) { + testFHFieldInclusionRecordType(t) +} + +// BenchmarkFHFieldInclusionRecordType benchmarks validating field inclusion +func BenchmarkFHFieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusionRecordType(b) + } +} + +// testFHFieldInclusionImmediateDestination validates immediate destination field inclusion +func testFHFieldInclusionImmediateDestination(t testing.TB) { fh := mockFileHeader() fh.ImmediateDestination = "" if err := fh.Validate(); err != nil { @@ -280,7 +465,21 @@ func TestFHFieldInclusionImmediatDestination(t *testing.T) { } } -func TestFHFieldInclusionFileIDModifier(t *testing.T) { +// TestFHFieldInclusionImmediateDestination tests validates immediate destination field inclusion +func TestFHFieldInclusionImmediateDestination(t *testing.T) { + testFHFieldInclusionImmediateDestination(t) +} + +// BenchmarkFHFieldInclusionImmediateDestination benchmarks validates immediate destination field inclusion +func BenchmarkFHFieldInclusionImmediateDestination(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusionImmediateDestination(b) + } +} + +// testFHFieldInclusionFileIDModifier validates file ID modifier field inclusion +func testFHFieldInclusionFileIDModifier(t testing.TB) { fh := mockFileHeader() fh.FileIDModifier = "" if err := fh.Validate(); err != nil { @@ -292,7 +491,21 @@ func TestFHFieldInclusionFileIDModifier(t *testing.T) { } } -func TestFHFieldInclusionRecordSize(t *testing.T) { +// TestFHFieldInclusionFileIDModifier tests validating file ID modifier field inclusion +func TestFHFieldInclusionFileIDModifier(t *testing.T) { + testFHFieldInclusionFileIDModifier(t) +} + +// BenchmarkFHFieldInclusionFileIDModifier benchmarks validating file ID modifier field inclusion +func BenchmarkFHFieldInclusionFileIDModifier(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusionFileIDModifier(b) + } +} + +// testFHFieldInclusionRecordSize validates record size field inclusion +func testFHFieldInclusionRecordSize(t testing.TB) { fh := mockFileHeader() fh.recordSize = "" if err := fh.Validate(); err != nil { @@ -304,7 +517,21 @@ func TestFHFieldInclusionRecordSize(t *testing.T) { } } -func TestFHFieldInclusionBlockingFactor(t *testing.T) { +// TestFHFieldInclusionRecordSize tests validating record size field inclusion +func TestFHFieldInclusionRecordSize(t *testing.T) { + testFHFieldInclusionRecordSize(t) +} + +// BenchmarkFHFieldInclusionRecordSize benchmarks validating record size field inclusion +func BenchmarkFHFieldInclusionRecordSize(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusionRecordSize(b) + } +} + +// testFHFieldInclusionBlockingFactor validates blocking factor field inclusion +func testFHFieldInclusionBlockingFactor(t testing.TB) { fh := mockFileHeader() fh.blockingFactor = "" if err := fh.Validate(); err != nil { @@ -316,7 +543,22 @@ func TestFHFieldInclusionBlockingFactor(t *testing.T) { } } -func TestFHFieldInclusionFormatCode(t *testing.T) { +// TestFHFieldInclusionBlockingFactor tests validating blocking factor field inclusion +func TestFHFieldInclusionBlockingFactor(t *testing.T) { + testFHFieldInclusionBlockingFactor(t) +} + +// BenchmarkFHFieldInclusionBlockingFactor benchmarks +// validating blocking factor field inclusion +func BenchmarkFHFieldInclusionBlockingFactor(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusionBlockingFactor(b) + } +} + +// testFHFieldInclusionFormatCode validates format code field inclusion +func testFHFieldInclusionFormatCode(t testing.TB) { fh := mockFileHeader() fh.formatCode = "" if err := fh.Validate(); err != nil { @@ -328,7 +570,21 @@ func TestFHFieldInclusionFormatCode(t *testing.T) { } } -func TestFHFieldInclusionCreationDate(t *testing.T) { +// TestFHFieldInclusionFormatCode tests validating format code field inclusion +func TestFHFieldInclusionFormatCode(t *testing.T) { + testFHFieldInclusionFormatCode(t) +} + +// BenchmarkFHFieldInclusionFormatCode benchmarks validating format code field inclusion +func BenchmarkFHFieldInclusionFormatCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusionFormatCode(b) + } +} + +// testFHFieldInclusionCreationDate validates creation date field inclusion +func testFHFieldInclusionCreationDate(t testing.TB) { fh := mockFileHeader() fh.FileCreationDate = time.Time{} if err := fh.Validate(); err != nil { @@ -339,3 +595,16 @@ func TestFHFieldInclusionCreationDate(t *testing.T) { } } } + +// TestFHFieldInclusionCreationDate tests validating creation date field inclusion +func TestFHFieldInclusionCreationDate(t *testing.T) { + testFHFieldInclusionCreationDate(t) +} + +// BenchmarkFHFieldInclusionCreationDate benchmarks validating creation date field inclusion +func BenchmarkFHFieldInclusionCreationDate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFHFieldInclusionCreationDate(b) + } +} \ No newline at end of file From 207ae0d688fdd2407f7eb92aac7ad7b50e462c7a Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 16:58:52 -0600 Subject: [PATCH 0119/1694] Add coverall's encrypted token --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fd02cc0e2..2a1e45314 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,4 +25,6 @@ script: # output code coverage - go test -v -covermode=count -coverprofile=coverage.out . - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN - \ No newline at end of file + env: + global: + secure: otwALdLyiyV4ZEjqC7/QXdkpr8VXPqYdjBQRuDrYlu5jVmtNpm/ZxwCT2+0WfnSW4ZBNgKs3HXbw2L41prOn6miJ5vC+jzzC5IFTOErZj7dtv8M1j5LmlmyznpDaxMWhVv/t7ZN2V0dcLF8WeMIl4DHSDQIPGUW7cV3ENvf6ht2a+6A02SadR0KM1ce7rZiGRA/Asfm/XUI87sI8L/ZrH60Dc31RuxNmluVJH4tv/GCRmWQiEMX67vOe/04RpsVexXzex20Ft1/eh8nDe0qggZJ+QLcYl8h2gOXjq87O2gBFOxazOyrCPu4silSD4mNy03Zmc8QPdxQRnYn1UKLRy2UALwWFnB+O9py5N/2s4yMy14nuFe4iMAqkyv8mNmMdSsb/g4hc7NYWJmQahGRQi5dTPKrIwRlFc5VPRiKYLzeBVXNL1iewUjGv+c8AtchtRZZJKflqq/fu6tZGtj0fUCCAzvBFPDzduiO6y0M1S7bJ/H6d0zTxWQvoBTiFxmuZSD693Qe7CLnEZUiw8HJnTnNpJikTVDZYb53E+kv3vt7Psz+G0165ZU+SRn9HgQ4Mq5B0FhYr4+IUq1zXxX9/w0+PyNsOzluNtfYsc4NJ1GyfvIeoMTjuat08fNBnCyr+mdYZnrSnuz1B3ZZeFHsnIC81BUdHeqGha8K7tLwJdaw= From 8142e4efa984a52ff8621739e70ee82139a79a16 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 17:01:51 -0600 Subject: [PATCH 0120/1694] Gofmt -w --- batchCOR_test.go | 2 +- batchHeader_internal_test.go | 5 +---- batchPPD_test.go | 3 +-- entryDetail_internal_test.go | 2 +- fileControl_internal_test.go | 2 +- fileHeader_internal_test.go | 12 +++++------- reader_test.go | 13 ++++++------- 7 files changed, 16 insertions(+), 23 deletions(-) diff --git a/batchCOR_test.go b/batchCOR_test.go index 20da9d8c3..87a615e4b 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -263,4 +263,4 @@ func BenchmarkBatchCORCreate(b *testing.B) { for i := 0; i < b.N; i++ { testBatchCORCreate(b) } -} \ No newline at end of file +} diff --git a/batchHeader_internal_test.go b/batchHeader_internal_test.go index 86bc47f4d..a01229dcd 100644 --- a/batchHeader_internal_test.go +++ b/batchHeader_internal_test.go @@ -204,7 +204,6 @@ func BenchmarkInvalidServiceCode(b *testing.B) { } } - // testValidateInvalidSECCode validates error if service class is not valid func testInvalidSECCode(t testing.TB) { bh := mockBatchHeader() @@ -517,7 +516,6 @@ func BenchmarkBHFieldInclusionCompanyEntryDescription(b *testing.B) { } } - // testBHFieldInclusionOriginatorStatusCode validates Originator Status Code field inclusion func testBHFieldInclusionOriginatorStatusCode(t testing.TB) { bh := mockBatchHeader() @@ -544,7 +542,6 @@ func BenchmarkBHFieldInclusionOriginatorStatusCode(b *testing.B) { } } - // testBHFieldInclusionODFIIdentification validates ODFIIdentification field inclusion func testBHFieldInclusionODFIIdentification(t testing.TB) { bh := mockBatchHeader() @@ -569,4 +566,4 @@ func BenchmarkBHFieldInclusionODFIIdentification(b *testing.B) { for i := 0; i < b.N; i++ { testBHFieldInclusionODFIIdentification(b) } -} \ No newline at end of file +} diff --git a/batchPPD_test.go b/batchPPD_test.go index 0af1d0f0e..ee9ab57fd 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -212,7 +212,6 @@ func BenchmarkBatchCompanyIdentification(b *testing.B) { } } - // testBatchODFIIDMismatch validates ODFIIdentification mismatch func testBatchODFIIDMismatch(t testing.TB) { mockBatch := mockBatchPPD() @@ -264,4 +263,4 @@ func BenchmarkBatchBuild(b *testing.B) { for i := 0; i < b.N; i++ { testBatchBuild(b) } -} \ No newline at end of file +} diff --git a/entryDetail_internal_test.go b/entryDetail_internal_test.go index ec9e04168..e362508af 100644 --- a/entryDetail_internal_test.go +++ b/entryDetail_internal_test.go @@ -619,4 +619,4 @@ func BenchmarkEDCreditOrDebit(b *testing.B) { for i := 0; i < b.N; i++ { testEDCreditOrDebit(b) } -} \ No newline at end of file +} diff --git a/fileControl_internal_test.go b/fileControl_internal_test.go index baef7c892..1a3e7c1b6 100644 --- a/fileControl_internal_test.go +++ b/fileControl_internal_test.go @@ -285,4 +285,4 @@ func BenchmarkFCFieldInclusionEntryHash(b *testing.B) { for i := 0; i < b.N; i++ { testFCFieldInclusionEntryHash(b) } -} \ No newline at end of file +} diff --git a/fileHeader_internal_test.go b/fileHeader_internal_test.go index c0ac349e1..9c9610bbf 100644 --- a/fileHeader_internal_test.go +++ b/fileHeader_internal_test.go @@ -54,7 +54,6 @@ func BenchmarkMockFileHeader(b *testing.B) { } } - // parseFileHeader validates parsing a file header func parseFileHeader(t testing.TB) { var line = "101 076401251 0764012510807291511A094101achdestname companyname " @@ -189,7 +188,6 @@ func testValidateIDModifier(t testing.TB) { } } - // TestValidateIDModifier tests validating ID modifier is upper alphanumeric func TestValidateIDModifier(t *testing.T) { testValidateIDModifier(t) @@ -330,15 +328,15 @@ func testUpperLengthFileID(t testing.TB) { } // TestUpperLengthFileID tests validating file ID -func TestUpperLengthFileID (t *testing.T) { - testUpperLengthFileID (t) +func TestUpperLengthFileID(t *testing.T) { + testUpperLengthFileID(t) } // BenchmarkUpperLengthFileID benchmarks validating file ID -func BenchmarkUpperLengthFileID (b *testing.B) { +func BenchmarkUpperLengthFileID(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testUpperLengthFileID (b) + testUpperLengthFileID(b) } } @@ -607,4 +605,4 @@ func BenchmarkFHFieldInclusionCreationDate(b *testing.B) { for i := 0; i < b.N; i++ { testFHFieldInclusionCreationDate(b) } -} \ No newline at end of file +} diff --git a/reader_test.go b/reader_test.go index b87b5ceef..5b37d9123 100644 --- a/reader_test.go +++ b/reader_test.go @@ -84,15 +84,15 @@ func testWEBDebitRead(t testing.TB) { } // TestWEBDebitRead tests validating reading a WEB debit -func TestWEBDebitRead (t *testing.T) { - testWEBDebitRead (t) +func TestWEBDebitRead(t *testing.T) { + testWEBDebitRead(t) } // BenchmarkWEBDebitRead benchmarks validating reading a WEB debit -func BenchmarkWEBDebitRead (b *testing.B) { +func BenchmarkWEBDebitRead(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testWEBDebitRead (b) + testWEBDebitRead(b) } } @@ -512,7 +512,6 @@ func BenchmarkFileAddenda98(b *testing.B) { } } - // testFileAddenda99 validates addenda 99 func testFileAddenda99(t testing.TB) { bh := mockBatchHeader() @@ -699,6 +698,7 @@ func BenchmarkFileFileControlNoCurrentBatch(b *testing.B) { testFileFileControlNoCurrentBatch(b) } } + // testFileBatchControlValidate validates a batch control func testFileBatchControlValidate(t testing.TB) { bh := mockBatchHeader() @@ -841,7 +841,6 @@ func testFileFHImmediateOrigin(t testing.TB) { } } - // TestFileFHImmediateOrigin tests validating file header immediate origin func TestFileFHImmediateOrigin(t *testing.T) { testFileFHImmediateOrigin(t) @@ -853,4 +852,4 @@ func BenchmarkFileFHImmediateOrigin(b *testing.B) { for i := 0; i < b.N; i++ { testFileFHImmediateOrigin(b) } -} \ No newline at end of file +} From cca2782b7b379d95f463e718db974e9035f6bda7 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 17:07:44 -0600 Subject: [PATCH 0121/1694] Travis-ci.org -> travis-ci.com Updating build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ccec6919b..1df470fa1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ moov-io/ach === [![GoDoc](https://godoc.org/github.com/moov-io/ach?status.svg)](https://godoc.org/github.com/moov-io/ach) -[![Build Status](https://travis-ci.org/moov-io/ach.svg?branch=master)](https://travis-ci.org/moov-io/ach) +[![Build Status](https://travis-ci.com/moov-io/ach.svg?branch=master)](https://travis-ci.com/moov-io/ach) [![Coverage Status](https://coveralls.io/repos/github/moov-io/ach/badge.svg?branch=master)](https://coveralls.io/github/moov-io/ach?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/moov-io/ach)](https://goreportcard.com/report/github.com/moov-io/ach) [![Apache 2 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/moov-io/ach/master/LICENSE) From dbd766232e16ad353e129537e4661d6c4a677bed Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 17:34:06 -0600 Subject: [PATCH 0122/1694] Remove internal from file names --- batchControl_internal_test.go => batchControl_test.go | 0 batchHeader_internal_test.go => batchHeader_test.go | 0 entryDetail_internal_test.go => entryDetail_test.go | 0 fileControl_internal_test.go => fileControl_test.go | 0 fileHeader_internal_test.go => fileHeader_test.go | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename batchControl_internal_test.go => batchControl_test.go (100%) rename batchHeader_internal_test.go => batchHeader_test.go (100%) rename entryDetail_internal_test.go => entryDetail_test.go (100%) rename fileControl_internal_test.go => fileControl_test.go (100%) rename fileHeader_internal_test.go => fileHeader_test.go (100%) diff --git a/batchControl_internal_test.go b/batchControl_test.go similarity index 100% rename from batchControl_internal_test.go rename to batchControl_test.go diff --git a/batchHeader_internal_test.go b/batchHeader_test.go similarity index 100% rename from batchHeader_internal_test.go rename to batchHeader_test.go diff --git a/entryDetail_internal_test.go b/entryDetail_test.go similarity index 100% rename from entryDetail_internal_test.go rename to entryDetail_test.go diff --git a/fileControl_internal_test.go b/fileControl_test.go similarity index 100% rename from fileControl_internal_test.go rename to fileControl_test.go diff --git a/fileHeader_internal_test.go b/fileHeader_test.go similarity index 100% rename from fileHeader_internal_test.go rename to fileHeader_test.go From 74fe9f4366053223c42ba7fd87a1dda43b26250a Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 16 May 2018 10:54:45 -0600 Subject: [PATCH 0123/1694] JSON encoding keys Adding JSON encoding keys to the structs. --- addenda05.go | 6 +++--- addenda98.go | 10 +++++----- addenda99.go | 12 ++++++------ batch.go | 8 ++++---- batchControl.go | 35 ++++++++++++++++++----------------- batchHeader.go | 22 +++++++++++----------- entryDetail.go | 26 +++++++++++++------------- file.go | 6 +++--- fileControl.go | 12 ++++++------ fileHeader.go | 16 ++++++++-------- 10 files changed, 77 insertions(+), 76 deletions(-) diff --git a/addenda05.go b/addenda05.go index ef614ebed..d4989089d 100644 --- a/addenda05.go +++ b/addenda05.go @@ -13,16 +13,16 @@ type Addenda05 struct { // TypeCode Addenda05 types code '05' typeCode string // PaymentRelatedInformation - PaymentRelatedInformation string + PaymentRelatedInformation string `json:"paymentRelatedInformation"` // SequenceNumber is consecutively assigned to each Addenda05 Record following // an Entry Detail Record. The first addenda05 sequence number must always // be a "1". - SequenceNumber int + SequenceNumber int `json:"sequenceNumber,omitempty"` // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry // Detail or Corporate Entry Detail Record's trace number This number is // the same as the last seven digits of the trace number of the related // Entry Detail Record or Corporate Entry Detail Record. - EntryDetailSequenceNumber int + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to GoLang Converters diff --git a/addenda98.go b/addenda98.go index da86a09ca..5b850bbe9 100644 --- a/addenda98.go +++ b/addenda98.go @@ -14,17 +14,17 @@ type Addenda98 struct { typeCode string // ChangeCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for a change Entry. // Must exist in changeCodeDict - ChangeCode string + ChangeCode string `json:"changeCode"` // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. - OriginalTrace int + OriginalTrace int `json:"originalTrace"` // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI string + OriginalDFI string `json:"originalDFI"` // CorrectedData - CorrectedData string + CorrectedData string `json:"correctedData"` // TraceNumber matches the Entry Detail Trace Number of the entry being returned. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // validator is composed for data validation validator diff --git a/addenda99.go b/addenda99.go index 250238742..88075261f 100644 --- a/addenda99.go +++ b/addenda99.go @@ -34,19 +34,19 @@ type Addenda99 struct { typeCode string // ReturnCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for returning an Entry. // Must exist in returnCodeDict - ReturnCode string + ReturnCode string `json:"returnCode"` // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. - OriginalTrace int + OriginalTrace int `json:"originalTrace"` // DateOfDeath The field date of death is to be supplied on Entries being returned for reason of death (return reason codes R14 and R15). - DateOfDeath time.Time + DateOfDeath time.Time `json:"dateOfDeath"` // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI string + OriginalDFI string `json:"originalDFI"` // AddendaInformation - AddendaInformation string + AddendaInformation string `json:"addendaInformation,omitempty"` // TraceNumber matches the Entry Detail Trace Number of the entry being returned. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // validator is composed for data validation validator diff --git a/batch.go b/batch.go index c89323daa..2e4dede8e 100644 --- a/batch.go +++ b/batch.go @@ -8,12 +8,12 @@ import ( // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries type batch struct { - header *BatchHeader - entries []*EntryDetail - control *BatchControl + header *BatchHeader `json:"batchHeader,omitempty"` + entries []*EntryDetail `json:"entryDetails,omitempty"` + control *BatchControl `json:"batchControl,omitempty"` // category defines if the entry is a Forward, Return, or NOC - category string + category string `json:"category,omitempty"` // Converters is composed for ACH to GoLang Converters converters } diff --git a/batchControl.go b/batchControl.go index a5003e232..11de9739c 100644 --- a/batchControl.go +++ b/batchControl.go @@ -13,55 +13,56 @@ import ( // BatchControl contains entry counts, dollar total and has totals for all // entries contained in the preceding batch type BatchControl struct { - // RecordType defines the type of record in the block. batchControlPos 8 + // RecordType defines the type of record in the block. + // batchControlPos 8 recordType string // ServiceClassCode ACH Mixed Debits and Credits ‘200’ // ACH Credits Only ‘220’ // ACH Debits Only ‘225' // Same as 'ServiceClassCode' in BatchHeaderRecord - ServiceClassCode int + ServiceClassCode int `json:"serviceClassCode"` // EntryAddendaCount is a tally of each Entry Detail Record and each Addenda // Record processed, within either the batch or file as appropriate. - EntryAddendaCount int + EntryAddendaCount int `json:"entryAddendaÇount"` // validate the Receiving DFI Identification in each Entry Detail Record is hashed // to provide a check against inadvertent alteration of data contents due - // to hardware failure or program erro + // to hardware failure or program error // // In this context the Entry Hash is the sum of the corresponding fields in the // Entry Detail Records on the file. - EntryHash int + EntryHash int `json:"entryHash"` // TotalDebitEntryDollarAmount Contains accumulated Entry debit totals within the batch. - TotalDebitEntryDollarAmount int + TotalDebitEntryDollarAmount int `json:"totalDebit"` // TotalCreditEntryDollarAmount Contains accumulated Entry credit totals within the batch. - TotalCreditEntryDollarAmount int - // CompanyIdentification is an alphameric code used to identify an Originato + TotalCreditEntryDollarAmount int `json:"totalCredit"` + // CompanyIdentification is an alphameric code used to identify an Originator // The Company Identification Field must be included on all - // prenotification records and on each entry initiated puruant to such + // prenotification records and on each entry initiated pursuant to such // prenotification. The Company ID may begin with the ANSI one-digit - // Identification Code Designators (ICD), followed by the identification - // numbe The ANSI Identification Numbers and related Identification Code - // Designators (ICD) are: + // Identification Code Designator (ICD), followed by the identification + // number The ANSI Identification Numbers and related Identification Code + // Designator (ICD) are: // // IRS Employer Identification Number (EIN) "1" // Data Universal Numbering Systems (DUNS) "3" // User Assigned Number "9" - CompanyIdentification string + CompanyIdentification string `json:"companyIdentification"` // MessageAuthenticationCode the MAC is an eight character code derived from a special key used in // conjunction with the DES algorithm. The purpose of the MAC is to // validate the authenticity of ACH entries. The DES algorithm and key // message standards must be in accordance with standards adopted by the // American National Standards Institute. The remaining eleven characters // of this field are blank. - MessageAuthenticationCode string + MessageAuthenticationCode string `json:"messageAuthentication,omitempty"` // Reserved for the future - Blank, 6 characters long reserved string - // OdfiIdentification the routing number is used to identify the DFI originating entries within a given branch. - ODFIIdentification string + // ODFIIdentification the routing number is used to identify the DFI originating entries within a given branch. + ODFIIdentification string `json:"ODFIIdentification"` // BatchNumber this number is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number // in the Batch Header Record and the Batch Control Record is the same, // the ascending sequence number should be assigned by batch and not by record. - BatchNumber int + BatchNumber int `json:"batchNumber"` // validator is composed for data validation validator // converters is composed for ACH to golang Converters diff --git a/batchHeader.go b/batchHeader.go index 9c475c8f8..6baee7f0e 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -25,21 +25,21 @@ type BatchHeader struct { // ServiceClassCode ACH Mixed Debits and Credits ‘200’ // ACH Credits Only ‘220’ // ACH Debits Only ‘225' - ServiceClassCode int + ServiceClassCode int `json:"serviceClassCode"` // CompanyName the company originating the entries in the batch - CompanyName string + CompanyName string `json:"companyName"` // CompanyDiscretionaryData allows Originators and/or ODFIs to include codes (one or more), // of significance only to them, to enable specialized handling of all // subsequent entries in that batch. There will be no standardized // interpretation for the value of the field. This field must be returned // intact on any return entry. - CompanyDiscretionaryData string + CompanyDiscretionaryData string `json:"companyDiscretionaryData,omitempty"` // CompanyIdentification The 9 digit FEIN number (proceeded by a predetermined // alpha or numeric character) of the entity in the company name field - CompanyIdentification string + CompanyIdentification string `json:"companyIdentification"` // StandardEntryClassCode // Identifies the payment type (product) found within an ACH batch-using a 3-character code. @@ -48,7 +48,7 @@ type BatchHeader struct { // Determines addenda records (required or optional PLUS one or up to 9,999 records). // Determines rules to follow (return time frames). // Some SEC codes require specific data in predetermined fields within the ACH record - StandardEntryClassCode string + StandardEntryClassCode string `json:"standardEntryClassCode,omitempty"` // CompanyEntryDescription A description of the entries contained in the batch // @@ -65,7 +65,7 @@ type BatchHeader struct { // // This field must contain the word "NONSETTLED" (left justified) when the // batch contains entries which could not settle. - CompanyEntryDescription string + CompanyEntryDescription string `json:"companyEntryDescription,omitempty"` // CompanyDescriptiveDate except as otherwise noted below, the Originator establishes this field // as the date it would like to see displayed to the receiver for @@ -73,10 +73,10 @@ type BatchHeader struct { // computer or manual operation. It is solely for descriptive purposes. // The RDFI should not assume any specific format. Examples of possible // entries in this field are "011392,", "01 92," "JAN 13," "JAN 92," etc. - CompanyDescriptiveDate string + CompanyDescriptiveDate string `json:"companyDescriptiveDate,omitempty"` // EffectiveEntryDate the date on which the entries are to settle - EffectiveEntryDate time.Time + EffectiveEntryDate time.Time `json:"effectiveEntryDate,omitempty"` // SettlementDate Leave blank, this field is inserted by the ACH operator settlementDate string @@ -85,17 +85,17 @@ type BatchHeader struct { // 0 ADV File prepared by an ACH Operator. // 1 This code identifies the Originator as a depository financial institution. // 2 This code identifies the Originator as a Federal Government entity or agency. - OriginatorStatusCode int + OriginatorStatusCode int `json:"originatorStatusCode,omitempty"` //ODFIIdentification First 8 digits of the originating DFI transit routing number - ODFIIdentification string + ODFIIdentification string `json:"ODFIIdentification"` // BatchNumber is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number // in the Batch Header Record and the Batch Control Record is the same, // the ascending sequence number should be assigned by batch and not by // record. - BatchNumber int + BatchNumber int `json:"batchNumber,omitempty"` // validator is composed for data validation validator diff --git a/entryDetail.go b/entryDetail.go index c9a773e39..1e21122b7 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -26,28 +26,28 @@ type EntryDetail struct { // Prenote for credit to savings account ‘33’ // Debit to savings account ‘37’ // Prenote for debit to savings account ‘38’ - TransactionCode int + TransactionCode int `json:"transactionCode"` // RDFIIdentification is the RDFI's routing number without the last digit. // Receiving Depository Financial Institution - RDFIIdentification string + RDFIIdentification string `json:"RDFIIdentification"` // CheckDigit the last digit of the RDFI's routing number - CheckDigit string + CheckDigit string `json:"checkDigit"` // DFIAccountNumber is the receiver's bank account number you are crediting/debiting. // It important to note that this is an alphanumeric field, so its space padded, no zero padded - DFIAccountNumber string + DFIAccountNumber string `json:"DFIAccountNumber"` // Amount Number of cents you are debiting/crediting this account - Amount int + Amount int `json:"amount"` - // IdentificationNumber n internal identification (alphanumeric) that + // IdentificationNumber an internal identification (alphanumeric) that // you use to uniquely identify this Entry Detail Record - IdentificationNumber string + IdentificationNumber string `json:"identificationNumber,omitempty"` // IndividualName The name of the receiver, usually the name on the bank account - IndividualName string + IndividualName string `json:"individualName"` // DiscretionaryData allows ODFIs to include codes, of significance only to them, // to enable specialized handling of the entry. There will be no @@ -57,12 +57,12 @@ type EntryDetail struct { // field must be returned intact for any returned entry. // // WEB uses the Discretionary Data Field as the Payment Type Code - DiscretionaryData string + DiscretionaryData string `json:"discretionaryData,omitempty"` // AddendaRecordIndicator indicates the existence of an Addenda Record. // A value of "1" indicates that one ore more addenda records follow, // and "0" means no such record is present. - AddendaRecordIndicator int + AddendaRecordIndicator int `json:"addendaRecordIndicator,omitempty"` // TraceNumber assigned by the ODFI in ascending sequence, is included in each // Entry Detail Record, Corporate Entry Detail Record, and addenda Record. @@ -72,12 +72,12 @@ type EntryDetail struct { // For addenda Records, the Trace Number will be identical to the Trace Number // in the associated Entry Detail Record, since the Trace Number is associated // with an entry or item rather than a physical record. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // Addendum a list of Addenda for the Entry Detail - Addendum []Addendumer + Addendum []Addendumer `json:"addendum,omitempty"` // Category defines if the entry is a Forward, Return, or NOC - Category string + Category string `json:"category,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to golang Converters diff --git a/file.go b/file.go index e248372e8..ad16502b9 100644 --- a/file.go +++ b/file.go @@ -53,9 +53,9 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { - Header FileHeader - Batches []Batcher - Control FileControl + Header FileHeader `json:"fileHeader"` + Batches []Batcher `json:"batches"` + Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches NotificationOfChange []*BatchCOR diff --git a/fileControl.go b/fileControl.go index 4c96fc130..a70cfb6cc 100644 --- a/fileControl.go +++ b/fileControl.go @@ -13,23 +13,23 @@ type FileControl struct { recordType string // BatchCount total number of batches (i.e., ‘5’ records) in the file - BatchCount int + BatchCount int `json:"batchCount"` // BlockCount total number of records in the file (include all headers and trailer) divided // by 10 (This number must be evenly divisible by 10. If not, additional records consisting of all 9’s are added to the file after the initial ‘9’ record to fill out the block 10.) - BlockCount int + BlockCount int `json:"blockCount,omitempty"` // EntryAddendaCount total detail and addenda records in the file - EntryAddendaCount int + EntryAddendaCount int `json:"entryAddendaCount"` // EntryHash calculated in the same manner as the batch has total but includes total from entire file - EntryHash int + EntryHash int `json:"entryHash"` // TotalDebitEntryDollarAmountInFile contains accumulated Batch debit totals within the file. - TotalDebitEntryDollarAmountInFile int + TotalDebitEntryDollarAmountInFile int `json:"totalDebit"` // TotalCreditEntryDollarAmountInFile contains accumulated Batch credit totals within the file. - TotalCreditEntryDollarAmountInFile int + TotalCreditEntryDollarAmountInFile int `json:"totalCredit"` // Reserved should be blank. reserved string // validator is composed for data validation diff --git a/fileHeader.go b/fileHeader.go index 4f3a10bff..d9b7b2a8a 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -35,7 +35,7 @@ type FileHeader struct { // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). ImmediateDestinationField() will append the blank space to the // routing number. - ImmediateDestination string + ImmediateDestination string `json:"immediateDestination"` // ImmediateOrigin contains the Routing Number of the ACH Operator or sending // point that is sending the file. The ach file format specifies a 10 character field @@ -43,23 +43,23 @@ type FileHeader struct { // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). ImmediateOriginField() will append the blank space to the routing // number. - ImmediateOrigin string + ImmediateOrigin string `json:"immediateOrigin"` // FileCreationDate is expressed in a "YYMMDD" format. The File Creation // Date is the date on which the file is prepared by an ODFI (ACH input files) // or the date (exchange date) on which a file is transmitted from ACH Operator // to ACH Operator, or from ACH Operator to RDFIs (ACH output files). - FileCreationDate time.Time + FileCreationDate time.Time `json:"fileCreationDate"` // FileCreationTime is expressed ina n "HHMM" (24 hour clock) format. // The system time when the ACH file was created - FileCreationTime time.Time + FileCreationTime time.Time `json:"fileCreationTime"` // This field should start at zero and increment by 1 (up to 9) and then go to // letters starting at A through Z for each subsequent file that is created for // a single system date. (34-34) 1 numeric 0-9 or uppercase alpha A-Z. // I have yet to see this ID not A - FileIDModifier string + FileIDModifier string `json:"fileIDModifier,omitempty"` // RecordSize indicates the number of characters contained in each // record. At this time, the value "094" must be used. @@ -78,14 +78,14 @@ type FileHeader struct { // ImmediateDestinationName us the name of the ACH or receiving point for which that // file is destined. Name corresponding to the ImmediateDestination - ImmediateDestinationName string + ImmediateDestinationName string `json:"immediateDestinationName"` // ImmediateOriginName is the name of the ACH operator or sending point that is // sending the file. Name corresponding to the ImmediateOrigin - ImmediateOriginName string + ImmediateOriginName string `json:"immediateOriginName"` // ReferenceCode is reserved for information pertinent to the Originator. - ReferenceCode string + ReferenceCode string `json:"referenceCode,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to GoLang Converters From 13e29b820f49c1e634b98eabd71b5b858928cebd Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 17:50:00 -0600 Subject: [PATCH 0124/1694] I love YAML and TABS --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2a1e45314..41bdbef8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,6 @@ script: # output code coverage - go test -v -covermode=count -coverprofile=coverage.out . - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN - env: +env: global: secure: otwALdLyiyV4ZEjqC7/QXdkpr8VXPqYdjBQRuDrYlu5jVmtNpm/ZxwCT2+0WfnSW4ZBNgKs3HXbw2L41prOn6miJ5vC+jzzC5IFTOErZj7dtv8M1j5LmlmyznpDaxMWhVv/t7ZN2V0dcLF8WeMIl4DHSDQIPGUW7cV3ENvf6ht2a+6A02SadR0KM1ce7rZiGRA/Asfm/XUI87sI8L/ZrH60Dc31RuxNmluVJH4tv/GCRmWQiEMX67vOe/04RpsVexXzex20Ft1/eh8nDe0qggZJ+QLcYl8h2gOXjq87O2gBFOxazOyrCPu4silSD4mNy03Zmc8QPdxQRnYn1UKLRy2UALwWFnB+O9py5N/2s4yMy14nuFe4iMAqkyv8mNmMdSsb/g4hc7NYWJmQahGRQi5dTPKrIwRlFc5VPRiKYLzeBVXNL1iewUjGv+c8AtchtRZZJKflqq/fu6tZGtj0fUCCAzvBFPDzduiO6y0M1S7bJ/H6d0zTxWQvoBTiFxmuZSD693Qe7CLnEZUiw8HJnTnNpJikTVDZYb53E+kv3vt7Psz+G0165ZU+SRn9HgQ4Mq5B0FhYr4+IUq1zXxX9/w0+PyNsOzluNtfYsc4NJ1GyfvIeoMTjuat08fNBnCyr+mdYZnrSnuz1B3ZZeFHsnIC81BUdHeqGha8K7tLwJdaw= From 8f75729e9ecfba1338f1078120641967d316525c Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 17:50:00 -0600 Subject: [PATCH 0125/1694] I love YAML and TABS --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2a1e45314..41bdbef8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,6 @@ script: # output code coverage - go test -v -covermode=count -coverprofile=coverage.out . - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN - env: +env: global: secure: otwALdLyiyV4ZEjqC7/QXdkpr8VXPqYdjBQRuDrYlu5jVmtNpm/ZxwCT2+0WfnSW4ZBNgKs3HXbw2L41prOn6miJ5vC+jzzC5IFTOErZj7dtv8M1j5LmlmyznpDaxMWhVv/t7ZN2V0dcLF8WeMIl4DHSDQIPGUW7cV3ENvf6ht2a+6A02SadR0KM1ce7rZiGRA/Asfm/XUI87sI8L/ZrH60Dc31RuxNmluVJH4tv/GCRmWQiEMX67vOe/04RpsVexXzex20Ft1/eh8nDe0qggZJ+QLcYl8h2gOXjq87O2gBFOxazOyrCPu4silSD4mNy03Zmc8QPdxQRnYn1UKLRy2UALwWFnB+O9py5N/2s4yMy14nuFe4iMAqkyv8mNmMdSsb/g4hc7NYWJmQahGRQi5dTPKrIwRlFc5VPRiKYLzeBVXNL1iewUjGv+c8AtchtRZZJKflqq/fu6tZGtj0fUCCAzvBFPDzduiO6y0M1S7bJ/H6d0zTxWQvoBTiFxmuZSD693Qe7CLnEZUiw8HJnTnNpJikTVDZYb53E+kv3vt7Psz+G0165ZU+SRn9HgQ4Mq5B0FhYr4+IUq1zXxX9/w0+PyNsOzluNtfYsc4NJ1GyfvIeoMTjuat08fNBnCyr+mdYZnrSnuz1B3ZZeFHsnIC81BUdHeqGha8K7tLwJdaw= From 1bfee0004fd055d0af8bc18a6cb14e6e173c2c9d Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 18:10:53 -0600 Subject: [PATCH 0126/1694] Change service to travis-ci --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 41bdbef8d..5cb9424a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,7 @@ script: - gocyclo -over 19 $GO_FILES # forbid code with huge functions - golint -set_exit_status $(go list $GO_FILES) # one last linter # output code coverage - - go test -v -covermode=count -coverprofile=coverage.out . - - goveralls -coverprofile=coverage.out -service travis-ci -repotoken $COVERALLS_TOKEN + - goveralls -service=travis-ci env: global: secure: otwALdLyiyV4ZEjqC7/QXdkpr8VXPqYdjBQRuDrYlu5jVmtNpm/ZxwCT2+0WfnSW4ZBNgKs3HXbw2L41prOn6miJ5vC+jzzC5IFTOErZj7dtv8M1j5LmlmyznpDaxMWhVv/t7ZN2V0dcLF8WeMIl4DHSDQIPGUW7cV3ENvf6ht2a+6A02SadR0KM1ce7rZiGRA/Asfm/XUI87sI8L/ZrH60Dc31RuxNmluVJH4tv/GCRmWQiEMX67vOe/04RpsVexXzex20Ft1/eh8nDe0qggZJ+QLcYl8h2gOXjq87O2gBFOxazOyrCPu4silSD4mNy03Zmc8QPdxQRnYn1UKLRy2UALwWFnB+O9py5N/2s4yMy14nuFe4iMAqkyv8mNmMdSsb/g4hc7NYWJmQahGRQi5dTPKrIwRlFc5VPRiKYLzeBVXNL1iewUjGv+c8AtchtRZZJKflqq/fu6tZGtj0fUCCAzvBFPDzduiO6y0M1S7bJ/H6d0zTxWQvoBTiFxmuZSD693Qe7CLnEZUiw8HJnTnNpJikTVDZYb53E+kv3vt7Psz+G0165ZU+SRn9HgQ4Mq5B0FhYr4+IUq1zXxX9/w0+PyNsOzluNtfYsc4NJ1GyfvIeoMTjuat08fNBnCyr+mdYZnrSnuz1B3ZZeFHsnIC81BUdHeqGha8K7tLwJdaw= From bf20b69cbb56eec729840ade69a0ba234bb7b0f1 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 18:24:46 -0600 Subject: [PATCH 0127/1694] Update Coveralls.io Token --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5cb9424a8..106790ff2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,4 +26,4 @@ script: - goveralls -service=travis-ci env: global: - secure: otwALdLyiyV4ZEjqC7/QXdkpr8VXPqYdjBQRuDrYlu5jVmtNpm/ZxwCT2+0WfnSW4ZBNgKs3HXbw2L41prOn6miJ5vC+jzzC5IFTOErZj7dtv8M1j5LmlmyznpDaxMWhVv/t7ZN2V0dcLF8WeMIl4DHSDQIPGUW7cV3ENvf6ht2a+6A02SadR0KM1ce7rZiGRA/Asfm/XUI87sI8L/ZrH60Dc31RuxNmluVJH4tv/GCRmWQiEMX67vOe/04RpsVexXzex20Ft1/eh8nDe0qggZJ+QLcYl8h2gOXjq87O2gBFOxazOyrCPu4silSD4mNy03Zmc8QPdxQRnYn1UKLRy2UALwWFnB+O9py5N/2s4yMy14nuFe4iMAqkyv8mNmMdSsb/g4hc7NYWJmQahGRQi5dTPKrIwRlFc5VPRiKYLzeBVXNL1iewUjGv+c8AtchtRZZJKflqq/fu6tZGtj0fUCCAzvBFPDzduiO6y0M1S7bJ/H6d0zTxWQvoBTiFxmuZSD693Qe7CLnEZUiw8HJnTnNpJikTVDZYb53E+kv3vt7Psz+G0165ZU+SRn9HgQ4Mq5B0FhYr4+IUq1zXxX9/w0+PyNsOzluNtfYsc4NJ1GyfvIeoMTjuat08fNBnCyr+mdYZnrSnuz1B3ZZeFHsnIC81BUdHeqGha8K7tLwJdaw= + secure: iQ3GSRYK0A+CBWNpFWRMxT8NaTTYDRa94LaS235qI56r0ggKj1jm5IXYnieOTSqT2kGq4sy+ztAOHkDmik0sfk33tzIPQUhbHpx6I7u5/JmKOS99hK4o3jDsaNY8lRUY/Cq/axN3tE2dKeqY3WWkVXp0HPM/Sy4z0eUNdks59lLphOQkYje6QE+FzaKxeqCV4k7xI0TVrG1cdSRQKp7MCplqifrHLB75QACrUK0nRQ8Xe/Zw/4kmodMfLhs59FVynsTzjS6Tp8WfslPqNOF8l+G7Bqy3NfDWUoMHLgvzlWttsDgBo0Q+o3syAn6HRRoZfwovTXwfaLV7dhtiKZ8+oVWF1+QNmfn9YW74HJylG8Od9bwxsWb2paAF7WmwY+a6xibk98qJWl3m6xsjFSqiV58Z4DiSU24r0KFmqbmc4SQmFf04bs+8rFT3kjnW0e6Ryg0Sgzxzeuls/zhm6DnJsb+D6odezzGqNYBo9kmiwDZptuSVx1Q5/tbcIVODYKSY6ouHNovper7d8BENS6v0NAApVtPs7yBSMrMp+VJqILOFgKqHQzHAMvq830+uJifMdKIp44WOHeRiZOwwt1Ks1jeXqOjsimMUzCCkUH/jbcJw897Vour94ovPgEoEbECORaykR9eUp43lXkC8CztQzTHCyoNGr/GbAfYixCkY2oo= From 0cc7b7c39944061a12b67e7d216ca88e1d79131b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 20:42:14 -0600 Subject: [PATCH 0128/1694] Remove coveralls security --- .travis.yml | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/.travis.yml b/.travis.yml index 106790ff2..b2455bcf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,20 @@ language: go sudo: false go: - - tip +- tip before_install: - - go get github.com/mattn/goveralls - - go get -u honnef.co/go/tools/cmd/megacheck - - go get -u github.com/client9/misspell/cmd/misspell - - go get -u golang.org/x/lint/golint - - go get github.com/fzipp/gocyclo - +- go get github.com/mattn/goveralls +- go get -u honnef.co/go/tools/cmd/megacheck +- go get -u github.com/client9/misspell/cmd/misspell +- go get -u golang.org/x/lint/golint +- go get github.com/fzipp/gocyclo before_script: - - GO_FILES=$(find . -iname '*.go' -type f | grep -v /example/ | grep -v /cmd/) # All the .go files, excluding example/ vendor/ - - # script always run to completion (set +e). All of these code checks are must haves -# in a modern Go project. +- GO_FILES=$(find . -iname '*.go' -type f | grep -v /example/ | grep -v /cmd/) script: - - test -z $(gofmt -s -l $GO_FILES) # Fail if a .go file hasn't been formatted with gofmt - # - go test -v -race $GO_FILES # Run all the tests with the race detector enabled - - go vet $GO_FILES # go vet is the official Go static analyzer - - megacheck $GO_FILES # "go vet on steroids" + linter - - misspell -error -locale US . # check for spelling errors - - gocyclo -over 19 $GO_FILES # forbid code with huge functions - - golint -set_exit_status $(go list $GO_FILES) # one last linter - # output code coverage - - goveralls -service=travis-ci -env: - global: - secure: iQ3GSRYK0A+CBWNpFWRMxT8NaTTYDRa94LaS235qI56r0ggKj1jm5IXYnieOTSqT2kGq4sy+ztAOHkDmik0sfk33tzIPQUhbHpx6I7u5/JmKOS99hK4o3jDsaNY8lRUY/Cq/axN3tE2dKeqY3WWkVXp0HPM/Sy4z0eUNdks59lLphOQkYje6QE+FzaKxeqCV4k7xI0TVrG1cdSRQKp7MCplqifrHLB75QACrUK0nRQ8Xe/Zw/4kmodMfLhs59FVynsTzjS6Tp8WfslPqNOF8l+G7Bqy3NfDWUoMHLgvzlWttsDgBo0Q+o3syAn6HRRoZfwovTXwfaLV7dhtiKZ8+oVWF1+QNmfn9YW74HJylG8Od9bwxsWb2paAF7WmwY+a6xibk98qJWl3m6xsjFSqiV58Z4DiSU24r0KFmqbmc4SQmFf04bs+8rFT3kjnW0e6Ryg0Sgzxzeuls/zhm6DnJsb+D6odezzGqNYBo9kmiwDZptuSVx1Q5/tbcIVODYKSY6ouHNovper7d8BENS6v0NAApVtPs7yBSMrMp+VJqILOFgKqHQzHAMvq830+uJifMdKIp44WOHeRiZOwwt1Ks1jeXqOjsimMUzCCkUH/jbcJw897Vour94ovPgEoEbECORaykR9eUp43lXkC8CztQzTHCyoNGr/GbAfYixCkY2oo= +- test -z $(gofmt -s -l $GO_FILES) +- go vet $GO_FILES +- megacheck $GO_FILES +- misspell -error -locale US . +- gocyclo -over 19 $GO_FILES +- golint -set_exit_status $(go list $GO_FILES) +- goveralls -service=travis-ci \ No newline at end of file From a71f6074e3f51d84836257746e5961d0a0a8ae3a Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 21 May 2018 20:50:16 -0600 Subject: [PATCH 0129/1694] More travis and coveralls fun --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b2455bcf9..9e08c5e4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,9 @@ language: go sudo: false go: - tip +env: + global: + secure: QPkcX77j8QEqTwOYyLGItqvxYwE6Na5WaSZWjmhp48OlxYatWRHxJBwcFYSn1OWD5FMn+3oW39fHknReIxtrnhXMaNvI7x3/0gy4zujD/xZ2xAg7NsQ+l5buvEFO8/LEwwo0fp4knItFcBv8xH/ziJBJyXvgfMtj7Is4Q/pB1p6pWDdVy1vtAj3zH02bcqh1yXXS3HvcD8UhTszfU017gVNXDN1ow0rp1L3ainr3btrVK9izUxZfKvb7PlWJO1ogah7xNr/dIOJLsx2SfKgzKp+3H28L2WegtbzON74Op4jXvRywCwqjmUt/nwJ/Y9anunMNHT136h+ye4ziG1i/VdbWq0Q4PopQ8yYqinujG7SjfQio+wNCV2cwc2r/WjNBjbH0N9/Pflogq3RHvgy/9VtPif1tY+RrZCSntohoEZbYpVcFQFE1xDyf6xq/uLxVeEcCU33gqq7cKEfpcUgyCITa+yCPfBdtgkLBJ8h7Sew1j08D1kTKUW6g3D1epmwlCh/Z16oHG5VwSnCLGDjJy8wm/hQk1i/g7qeP7g24CfNzffzlFBCy88HhjzmrhUpcaTyfVVDf4h8wK6Zu/J3dHjHXQYwfiQRqpMa+2DYyjGgZhniccuh4GWolGZauDQdmO9SD4Ugyt9PEMk02i32ax3A4XE/Q6VNOam+qszviX3Q= before_install: - go get github.com/mattn/goveralls - go get -u honnef.co/go/tools/cmd/megacheck @@ -17,4 +20,5 @@ script: - misspell -error -locale US . - gocyclo -over 19 $GO_FILES - golint -set_exit_status $(go list $GO_FILES) -- goveralls -service=travis-ci \ No newline at end of file +after_success: +- goveralls -repotoken $COVERALLS_TOKEN From 6cc4d35e8e916ac49c5f712942d49c3aab527f1d Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Wed, 16 May 2018 10:54:45 -0600 Subject: [PATCH 0130/1694] JSON encoding keys Adding JSON encoding keys to the structs. --- addenda05.go | 6 +++--- addenda98.go | 10 +++++----- addenda99.go | 12 ++++++------ batch.go | 8 ++++---- batchControl.go | 35 ++++++++++++++++++----------------- batchHeader.go | 22 +++++++++++----------- entryDetail.go | 26 +++++++++++++------------- file.go | 6 +++--- fileControl.go | 12 ++++++------ fileHeader.go | 16 ++++++++-------- 10 files changed, 77 insertions(+), 76 deletions(-) diff --git a/addenda05.go b/addenda05.go index ef614ebed..d4989089d 100644 --- a/addenda05.go +++ b/addenda05.go @@ -13,16 +13,16 @@ type Addenda05 struct { // TypeCode Addenda05 types code '05' typeCode string // PaymentRelatedInformation - PaymentRelatedInformation string + PaymentRelatedInformation string `json:"paymentRelatedInformation"` // SequenceNumber is consecutively assigned to each Addenda05 Record following // an Entry Detail Record. The first addenda05 sequence number must always // be a "1". - SequenceNumber int + SequenceNumber int `json:"sequenceNumber,omitempty"` // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry // Detail or Corporate Entry Detail Record's trace number This number is // the same as the last seven digits of the trace number of the related // Entry Detail Record or Corporate Entry Detail Record. - EntryDetailSequenceNumber int + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to GoLang Converters diff --git a/addenda98.go b/addenda98.go index da86a09ca..5b850bbe9 100644 --- a/addenda98.go +++ b/addenda98.go @@ -14,17 +14,17 @@ type Addenda98 struct { typeCode string // ChangeCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for a change Entry. // Must exist in changeCodeDict - ChangeCode string + ChangeCode string `json:"changeCode"` // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. - OriginalTrace int + OriginalTrace int `json:"originalTrace"` // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI string + OriginalDFI string `json:"originalDFI"` // CorrectedData - CorrectedData string + CorrectedData string `json:"correctedData"` // TraceNumber matches the Entry Detail Trace Number of the entry being returned. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // validator is composed for data validation validator diff --git a/addenda99.go b/addenda99.go index 250238742..88075261f 100644 --- a/addenda99.go +++ b/addenda99.go @@ -34,19 +34,19 @@ type Addenda99 struct { typeCode string // ReturnCode field contains a standard code used by an ACH Operator or RDFI to describe the reason for returning an Entry. // Must exist in returnCodeDict - ReturnCode string + ReturnCode string `json:"returnCode"` // OriginalTrace This field contains the Trace Number as originally included on the forward Entry or Prenotification. // The RDFI must include the Original Entry Trace Number in the Addenda Record of an Entry being returned to an ODFI, // in the Addenda Record of an 98, within an Acknowledgment Entry, or with an RDFI request for a copy of an authorization. - OriginalTrace int + OriginalTrace int `json:"originalTrace"` // DateOfDeath The field date of death is to be supplied on Entries being returned for reason of death (return reason codes R14 and R15). - DateOfDeath time.Time + DateOfDeath time.Time `json:"dateOfDeath"` // OriginalDFI field contains the Receiving DFI Identification (addenda.RDFIIdentification) as originally included on the forward Entry or Prenotification that the RDFI is returning or correcting. - OriginalDFI string + OriginalDFI string `json:"originalDFI"` // AddendaInformation - AddendaInformation string + AddendaInformation string `json:"addendaInformation,omitempty"` // TraceNumber matches the Entry Detail Trace Number of the entry being returned. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // validator is composed for data validation validator diff --git a/batch.go b/batch.go index c89323daa..2e4dede8e 100644 --- a/batch.go +++ b/batch.go @@ -8,12 +8,12 @@ import ( // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries type batch struct { - header *BatchHeader - entries []*EntryDetail - control *BatchControl + header *BatchHeader `json:"batchHeader,omitempty"` + entries []*EntryDetail `json:"entryDetails,omitempty"` + control *BatchControl `json:"batchControl,omitempty"` // category defines if the entry is a Forward, Return, or NOC - category string + category string `json:"category,omitempty"` // Converters is composed for ACH to GoLang Converters converters } diff --git a/batchControl.go b/batchControl.go index a5003e232..11de9739c 100644 --- a/batchControl.go +++ b/batchControl.go @@ -13,55 +13,56 @@ import ( // BatchControl contains entry counts, dollar total and has totals for all // entries contained in the preceding batch type BatchControl struct { - // RecordType defines the type of record in the block. batchControlPos 8 + // RecordType defines the type of record in the block. + // batchControlPos 8 recordType string // ServiceClassCode ACH Mixed Debits and Credits ‘200’ // ACH Credits Only ‘220’ // ACH Debits Only ‘225' // Same as 'ServiceClassCode' in BatchHeaderRecord - ServiceClassCode int + ServiceClassCode int `json:"serviceClassCode"` // EntryAddendaCount is a tally of each Entry Detail Record and each Addenda // Record processed, within either the batch or file as appropriate. - EntryAddendaCount int + EntryAddendaCount int `json:"entryAddendaÇount"` // validate the Receiving DFI Identification in each Entry Detail Record is hashed // to provide a check against inadvertent alteration of data contents due - // to hardware failure or program erro + // to hardware failure or program error // // In this context the Entry Hash is the sum of the corresponding fields in the // Entry Detail Records on the file. - EntryHash int + EntryHash int `json:"entryHash"` // TotalDebitEntryDollarAmount Contains accumulated Entry debit totals within the batch. - TotalDebitEntryDollarAmount int + TotalDebitEntryDollarAmount int `json:"totalDebit"` // TotalCreditEntryDollarAmount Contains accumulated Entry credit totals within the batch. - TotalCreditEntryDollarAmount int - // CompanyIdentification is an alphameric code used to identify an Originato + TotalCreditEntryDollarAmount int `json:"totalCredit"` + // CompanyIdentification is an alphameric code used to identify an Originator // The Company Identification Field must be included on all - // prenotification records and on each entry initiated puruant to such + // prenotification records and on each entry initiated pursuant to such // prenotification. The Company ID may begin with the ANSI one-digit - // Identification Code Designators (ICD), followed by the identification - // numbe The ANSI Identification Numbers and related Identification Code - // Designators (ICD) are: + // Identification Code Designator (ICD), followed by the identification + // number The ANSI Identification Numbers and related Identification Code + // Designator (ICD) are: // // IRS Employer Identification Number (EIN) "1" // Data Universal Numbering Systems (DUNS) "3" // User Assigned Number "9" - CompanyIdentification string + CompanyIdentification string `json:"companyIdentification"` // MessageAuthenticationCode the MAC is an eight character code derived from a special key used in // conjunction with the DES algorithm. The purpose of the MAC is to // validate the authenticity of ACH entries. The DES algorithm and key // message standards must be in accordance with standards adopted by the // American National Standards Institute. The remaining eleven characters // of this field are blank. - MessageAuthenticationCode string + MessageAuthenticationCode string `json:"messageAuthentication,omitempty"` // Reserved for the future - Blank, 6 characters long reserved string - // OdfiIdentification the routing number is used to identify the DFI originating entries within a given branch. - ODFIIdentification string + // ODFIIdentification the routing number is used to identify the DFI originating entries within a given branch. + ODFIIdentification string `json:"ODFIIdentification"` // BatchNumber this number is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number // in the Batch Header Record and the Batch Control Record is the same, // the ascending sequence number should be assigned by batch and not by record. - BatchNumber int + BatchNumber int `json:"batchNumber"` // validator is composed for data validation validator // converters is composed for ACH to golang Converters diff --git a/batchHeader.go b/batchHeader.go index 9c475c8f8..6baee7f0e 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -25,21 +25,21 @@ type BatchHeader struct { // ServiceClassCode ACH Mixed Debits and Credits ‘200’ // ACH Credits Only ‘220’ // ACH Debits Only ‘225' - ServiceClassCode int + ServiceClassCode int `json:"serviceClassCode"` // CompanyName the company originating the entries in the batch - CompanyName string + CompanyName string `json:"companyName"` // CompanyDiscretionaryData allows Originators and/or ODFIs to include codes (one or more), // of significance only to them, to enable specialized handling of all // subsequent entries in that batch. There will be no standardized // interpretation for the value of the field. This field must be returned // intact on any return entry. - CompanyDiscretionaryData string + CompanyDiscretionaryData string `json:"companyDiscretionaryData,omitempty"` // CompanyIdentification The 9 digit FEIN number (proceeded by a predetermined // alpha or numeric character) of the entity in the company name field - CompanyIdentification string + CompanyIdentification string `json:"companyIdentification"` // StandardEntryClassCode // Identifies the payment type (product) found within an ACH batch-using a 3-character code. @@ -48,7 +48,7 @@ type BatchHeader struct { // Determines addenda records (required or optional PLUS one or up to 9,999 records). // Determines rules to follow (return time frames). // Some SEC codes require specific data in predetermined fields within the ACH record - StandardEntryClassCode string + StandardEntryClassCode string `json:"standardEntryClassCode,omitempty"` // CompanyEntryDescription A description of the entries contained in the batch // @@ -65,7 +65,7 @@ type BatchHeader struct { // // This field must contain the word "NONSETTLED" (left justified) when the // batch contains entries which could not settle. - CompanyEntryDescription string + CompanyEntryDescription string `json:"companyEntryDescription,omitempty"` // CompanyDescriptiveDate except as otherwise noted below, the Originator establishes this field // as the date it would like to see displayed to the receiver for @@ -73,10 +73,10 @@ type BatchHeader struct { // computer or manual operation. It is solely for descriptive purposes. // The RDFI should not assume any specific format. Examples of possible // entries in this field are "011392,", "01 92," "JAN 13," "JAN 92," etc. - CompanyDescriptiveDate string + CompanyDescriptiveDate string `json:"companyDescriptiveDate,omitempty"` // EffectiveEntryDate the date on which the entries are to settle - EffectiveEntryDate time.Time + EffectiveEntryDate time.Time `json:"effectiveEntryDate,omitempty"` // SettlementDate Leave blank, this field is inserted by the ACH operator settlementDate string @@ -85,17 +85,17 @@ type BatchHeader struct { // 0 ADV File prepared by an ACH Operator. // 1 This code identifies the Originator as a depository financial institution. // 2 This code identifies the Originator as a Federal Government entity or agency. - OriginatorStatusCode int + OriginatorStatusCode int `json:"originatorStatusCode,omitempty"` //ODFIIdentification First 8 digits of the originating DFI transit routing number - ODFIIdentification string + ODFIIdentification string `json:"ODFIIdentification"` // BatchNumber is assigned in ascending sequence to each batch by the ODFI // or its Sending Point in a given file of entries. Since the batch number // in the Batch Header Record and the Batch Control Record is the same, // the ascending sequence number should be assigned by batch and not by // record. - BatchNumber int + BatchNumber int `json:"batchNumber,omitempty"` // validator is composed for data validation validator diff --git a/entryDetail.go b/entryDetail.go index c9a773e39..1e21122b7 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -26,28 +26,28 @@ type EntryDetail struct { // Prenote for credit to savings account ‘33’ // Debit to savings account ‘37’ // Prenote for debit to savings account ‘38’ - TransactionCode int + TransactionCode int `json:"transactionCode"` // RDFIIdentification is the RDFI's routing number without the last digit. // Receiving Depository Financial Institution - RDFIIdentification string + RDFIIdentification string `json:"RDFIIdentification"` // CheckDigit the last digit of the RDFI's routing number - CheckDigit string + CheckDigit string `json:"checkDigit"` // DFIAccountNumber is the receiver's bank account number you are crediting/debiting. // It important to note that this is an alphanumeric field, so its space padded, no zero padded - DFIAccountNumber string + DFIAccountNumber string `json:"DFIAccountNumber"` // Amount Number of cents you are debiting/crediting this account - Amount int + Amount int `json:"amount"` - // IdentificationNumber n internal identification (alphanumeric) that + // IdentificationNumber an internal identification (alphanumeric) that // you use to uniquely identify this Entry Detail Record - IdentificationNumber string + IdentificationNumber string `json:"identificationNumber,omitempty"` // IndividualName The name of the receiver, usually the name on the bank account - IndividualName string + IndividualName string `json:"individualName"` // DiscretionaryData allows ODFIs to include codes, of significance only to them, // to enable specialized handling of the entry. There will be no @@ -57,12 +57,12 @@ type EntryDetail struct { // field must be returned intact for any returned entry. // // WEB uses the Discretionary Data Field as the Payment Type Code - DiscretionaryData string + DiscretionaryData string `json:"discretionaryData,omitempty"` // AddendaRecordIndicator indicates the existence of an Addenda Record. // A value of "1" indicates that one ore more addenda records follow, // and "0" means no such record is present. - AddendaRecordIndicator int + AddendaRecordIndicator int `json:"addendaRecordIndicator,omitempty"` // TraceNumber assigned by the ODFI in ascending sequence, is included in each // Entry Detail Record, Corporate Entry Detail Record, and addenda Record. @@ -72,12 +72,12 @@ type EntryDetail struct { // For addenda Records, the Trace Number will be identical to the Trace Number // in the associated Entry Detail Record, since the Trace Number is associated // with an entry or item rather than a physical record. - TraceNumber int + TraceNumber int `json:"traceNumber,omitempty"` // Addendum a list of Addenda for the Entry Detail - Addendum []Addendumer + Addendum []Addendumer `json:"addendum,omitempty"` // Category defines if the entry is a Forward, Return, or NOC - Category string + Category string `json:"category,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to golang Converters diff --git a/file.go b/file.go index e248372e8..ad16502b9 100644 --- a/file.go +++ b/file.go @@ -53,9 +53,9 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { - Header FileHeader - Batches []Batcher - Control FileControl + Header FileHeader `json:"fileHeader"` + Batches []Batcher `json:"batches"` + Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches NotificationOfChange []*BatchCOR diff --git a/fileControl.go b/fileControl.go index 4c96fc130..a70cfb6cc 100644 --- a/fileControl.go +++ b/fileControl.go @@ -13,23 +13,23 @@ type FileControl struct { recordType string // BatchCount total number of batches (i.e., ‘5’ records) in the file - BatchCount int + BatchCount int `json:"batchCount"` // BlockCount total number of records in the file (include all headers and trailer) divided // by 10 (This number must be evenly divisible by 10. If not, additional records consisting of all 9’s are added to the file after the initial ‘9’ record to fill out the block 10.) - BlockCount int + BlockCount int `json:"blockCount,omitempty"` // EntryAddendaCount total detail and addenda records in the file - EntryAddendaCount int + EntryAddendaCount int `json:"entryAddendaCount"` // EntryHash calculated in the same manner as the batch has total but includes total from entire file - EntryHash int + EntryHash int `json:"entryHash"` // TotalDebitEntryDollarAmountInFile contains accumulated Batch debit totals within the file. - TotalDebitEntryDollarAmountInFile int + TotalDebitEntryDollarAmountInFile int `json:"totalDebit"` // TotalCreditEntryDollarAmountInFile contains accumulated Batch credit totals within the file. - TotalCreditEntryDollarAmountInFile int + TotalCreditEntryDollarAmountInFile int `json:"totalCredit"` // Reserved should be blank. reserved string // validator is composed for data validation diff --git a/fileHeader.go b/fileHeader.go index 4f3a10bff..d9b7b2a8a 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -35,7 +35,7 @@ type FileHeader struct { // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). ImmediateDestinationField() will append the blank space to the // routing number. - ImmediateDestination string + ImmediateDestination string `json:"immediateDestination"` // ImmediateOrigin contains the Routing Number of the ACH Operator or sending // point that is sending the file. The ach file format specifies a 10 character field @@ -43,23 +43,23 @@ type FileHeader struct { // Federal Reserve Routing Symbol, the four digit ABA Institution Identifier, and the Check // Digit (bTTTTAAAAC). ImmediateOriginField() will append the blank space to the routing // number. - ImmediateOrigin string + ImmediateOrigin string `json:"immediateOrigin"` // FileCreationDate is expressed in a "YYMMDD" format. The File Creation // Date is the date on which the file is prepared by an ODFI (ACH input files) // or the date (exchange date) on which a file is transmitted from ACH Operator // to ACH Operator, or from ACH Operator to RDFIs (ACH output files). - FileCreationDate time.Time + FileCreationDate time.Time `json:"fileCreationDate"` // FileCreationTime is expressed ina n "HHMM" (24 hour clock) format. // The system time when the ACH file was created - FileCreationTime time.Time + FileCreationTime time.Time `json:"fileCreationTime"` // This field should start at zero and increment by 1 (up to 9) and then go to // letters starting at A through Z for each subsequent file that is created for // a single system date. (34-34) 1 numeric 0-9 or uppercase alpha A-Z. // I have yet to see this ID not A - FileIDModifier string + FileIDModifier string `json:"fileIDModifier,omitempty"` // RecordSize indicates the number of characters contained in each // record. At this time, the value "094" must be used. @@ -78,14 +78,14 @@ type FileHeader struct { // ImmediateDestinationName us the name of the ACH or receiving point for which that // file is destined. Name corresponding to the ImmediateDestination - ImmediateDestinationName string + ImmediateDestinationName string `json:"immediateDestinationName"` // ImmediateOriginName is the name of the ACH operator or sending point that is // sending the file. Name corresponding to the ImmediateOrigin - ImmediateOriginName string + ImmediateOriginName string `json:"immediateOriginName"` // ReferenceCode is reserved for information pertinent to the Originator. - ReferenceCode string + ReferenceCode string `json:"referenceCode,omitempty"` // validator is composed for data validation validator // converters is composed for ACH to GoLang Converters From caf18492c82e6f6980aa1555d5bb8e6e4d39cfa2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 22 May 2018 09:46:29 -0400 Subject: [PATCH 0131/1694] Copyright Copyright --- addenda05.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addenda05.go b/addenda05.go index ef614ebed..e70f6a632 100644 --- a/addenda05.go +++ b/addenda05.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( From d8ae274ea9634c38db7bbc81194ba3f70e249063 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 22 May 2018 09:50:57 -0400 Subject: [PATCH 0132/1694] Copyright Copyright --- addenda05_test.go | 2 +- addenda98.go | 4 ++++ addenda98_test.go | 4 ++++ addenda99.go | 4 ++++ addenda99_test.go | 2 +- addendumer.go | 4 ++++ batch.go | 4 ++++ batchCCD.go | 4 ++++ batchCCD_test.go | 4 ++++ batchCOR.go | 4 ++++ batchCOR_test.go | 4 ++++ batchControl.go | 2 +- batchControl_test.go | 2 +- batchHeader.go | 2 +- batchHeader_test.go | 2 +- batchPPD.go | 2 +- batchPPD_test.go | 2 +- batchTEL.go | 4 ++++ batch_test.go | 4 ++++ batcher.go | 4 ++++ fileControl.go | 2 +- fileHeader.go | 2 +- reader.go | 2 +- reader_test.go | 2 +- record_test.go | 4 ++++ validators.go | 2 +- writer.go | 2 +- writer_test.go | 4 ++++ 28 files changed, 70 insertions(+), 14 deletions(-) diff --git a/addenda05_test.go b/addenda05_test.go index cdedd0e43..16624677b 100644 --- a/addenda05_test.go +++ b/addenda05_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/addenda98.go b/addenda98.go index da86a09ca..81b8a8611 100644 --- a/addenda98.go +++ b/addenda98.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/addenda98_test.go b/addenda98_test.go index 95756cc88..86c5d6310 100644 --- a/addenda98_test.go +++ b/addenda98_test.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/addenda99.go b/addenda99.go index 250238742..87db7accf 100644 --- a/addenda99.go +++ b/addenda99.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/addenda99_test.go b/addenda99_test.go index 7e569d7cc..961ab4744 100644 --- a/addenda99_test.go +++ b/addenda99_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/addendumer.go b/addendumer.go index 47752a148..c3d5d3412 100644 --- a/addendumer.go +++ b/addendumer.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach // Addendumer abstracts the different ACH addendum types that can be added to an EntryDetail record diff --git a/batch.go b/batch.go index c89323daa..e97ef5686 100644 --- a/batch.go +++ b/batch.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/batchCCD.go b/batchCCD.go index d3c00a7e8..0ff08f3ad 100644 --- a/batchCCD.go +++ b/batchCCD.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/batchCCD_test.go b/batchCCD_test.go index 4bd33d328..e3d756e7c 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/batchCOR.go b/batchCOR.go index 94ff94743..97949eb65 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/batchCOR_test.go b/batchCOR_test.go index 87a615e4b..bfab389df 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/batchControl.go b/batchControl.go index a5003e232..916c7456e 100644 --- a/batchControl.go +++ b/batchControl.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/batchControl_test.go b/batchControl_test.go index 390b4c3e1..30e221544 100644 --- a/batchControl_test.go +++ b/batchControl_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/batchHeader.go b/batchHeader.go index 9c475c8f8..033abd924 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/batchHeader_test.go b/batchHeader_test.go index a01229dcd..05fe67a23 100644 --- a/batchHeader_test.go +++ b/batchHeader_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/batchPPD.go b/batchPPD.go index f87904003..d1b2bc5ce 100644 --- a/batchPPD.go +++ b/batchPPD.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/batchPPD_test.go b/batchPPD_test.go index ee9ab57fd..1c27dd1a6 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/batchTEL.go b/batchTEL.go index c4bc73e67..d7c77e722 100644 --- a/batchTEL.go +++ b/batchTEL.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import "fmt" diff --git a/batch_test.go b/batch_test.go index 665366307..254f3c097 100644 --- a/batch_test.go +++ b/batch_test.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/batcher.go b/batcher.go index e68799d88..992b41aa7 100644 --- a/batcher.go +++ b/batcher.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/fileControl.go b/fileControl.go index 4c96fc130..2002b7024 100644 --- a/fileControl.go +++ b/fileControl.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/fileHeader.go b/fileHeader.go index 4f3a10bff..05a3d6db8 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -1,4 +1,4 @@ -// Copyright 2016 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/reader.go b/reader.go index 73ed0f47f..be88b70c3 100644 --- a/reader.go +++ b/reader.go @@ -1,4 +1,4 @@ -// Copyright 2016 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/reader_test.go b/reader_test.go index 5b37d9123..125b2b24d 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/record_test.go b/record_test.go index a05454bb0..bdb7f98b1 100644 --- a/record_test.go +++ b/record_test.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( diff --git a/validators.go b/validators.go index 87894235a..487f25a6b 100644 --- a/validators.go +++ b/validators.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/writer.go b/writer.go index e359b3cdb..d83469611 100644 --- a/writer.go +++ b/writer.go @@ -1,4 +1,4 @@ -// Copyright 2016 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/writer_test.go b/writer_test.go index efc2e2f47..d41beb22e 100644 --- a/writer_test.go +++ b/writer_test.go @@ -1,3 +1,7 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + package ach import ( From b28580993655086bd1168ec6b2e9eb5f22dbc688 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 22 May 2018 11:44:54 -0600 Subject: [PATCH 0133/1694] Exported fields for JSON mappings & ID on all Records --- addenda05.go | 2 + addenda98.go | 2 + addenda99.go | 2 + batch.go | 156 +++++------ batchCCD.go | 6 +- batchCCD_test.go | 2 +- batchCOR.go | 20 +- batchCOR_test.go | 2 +- batchControl.go | 3 +- batchHeader.go | 2 + batchTEL.go | 10 +- batchTEL_test.go | 2 +- batchWEB_test.go | 2 +- batchWeb.go | 6 +- batch_test.go | 4 +- coverage.out | 688 ----------------------------------------------- entryDetail.go | 2 + file.go | 1 + fileControl.go | 2 + fileHeader.go | 4 +- record_test.go | 2 +- 21 files changed, 124 insertions(+), 796 deletions(-) delete mode 100644 coverage.out diff --git a/addenda05.go b/addenda05.go index 55ecea525..aeb03bae2 100644 --- a/addenda05.go +++ b/addenda05.go @@ -12,6 +12,8 @@ import ( // Addenda05 is a Addendumer addenda which provides business transaction information for Addenda Type // Code 05 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. type Addenda05 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. entryAddenda05 Pos 7 recordType string // TypeCode Addenda05 types code '05' diff --git a/addenda98.go b/addenda98.go index 3786d192c..6577e8ff6 100644 --- a/addenda98.go +++ b/addenda98.go @@ -12,6 +12,8 @@ import ( // Addenda98 is a Addendumer addenda record format for Notification OF Change(98) // The field contents for Notification of Change Entries must match the field contents of the original Entries type Addenda98 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string // TypeCode Addenda types code '98' diff --git a/addenda99.go b/addenda99.go index fa31dee3f..87e4b402d 100644 --- a/addenda99.go +++ b/addenda99.go @@ -32,6 +32,8 @@ func init() { // Addenda99 utilized for Notification of Change Entry (COR) and Return types. type Addenda99 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. entryAddendaPos 7 recordType string // TypeCode Addenda types code '99' diff --git a/batch.go b/batch.go index 16829d9e3..fdf23514d 100644 --- a/batch.go +++ b/batch.go @@ -12,12 +12,14 @@ import ( // Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries type batch struct { - header *BatchHeader `json:"batchHeader,omitempty"` - entries []*EntryDetail `json:"entryDetails,omitempty"` - control *BatchControl `json:"batchControl,omitempty"` + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + Header *BatchHeader `json:"batchHeader,omitempty"` + Entries []*EntryDetail `json:"entryDetails,omitempty"` + Control *BatchControl `json:"batchControl,omitempty"` // category defines if the entry is a Forward, Return, or NOC - category string `json:"category,omitempty"` + category string // Converters is composed for ACH to GoLang Converters converters } @@ -43,7 +45,7 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { // verify checks basic valid NACHA batch rules. Assumes properly parsed records. This does not mean it is a valid batch as validity is tied to each batch type func (batch *batch) verify() error { - batchNumber := batch.header.BatchNumber + batchNumber := batch.Header.BatchNumber // verify field inclusion in all the records of the batch. if err := batch.isFieldInclusion(); err != nil { @@ -54,23 +56,23 @@ func (batch *batch) verify() error { return &BatchError{BatchNumber: batchNumber, FieldName: "FieldError", Msg: err.Error()} } // validate batch header and control codes are the same - if batch.header.ServiceClassCode != batch.control.ServiceClassCode { - msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.header.ServiceClassCode, batch.control.ServiceClassCode) + if batch.Header.ServiceClassCode != batch.Control.ServiceClassCode { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ServiceClassCode, batch.Control.ServiceClassCode) return &BatchError{BatchNumber: batchNumber, FieldName: "ServiceClassCode", Msg: msg} } // Company Identification must match the Company ID from the batch header record - if batch.header.CompanyIdentification != batch.control.CompanyIdentification { - msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.header.CompanyIdentification, batch.control.CompanyIdentification) + if batch.Header.CompanyIdentification != batch.Control.CompanyIdentification { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.CompanyIdentification, batch.Control.CompanyIdentification) return &BatchError{BatchNumber: batchNumber, FieldName: "CompanyIdentification", Msg: msg} } // Control ODFI Identification must be the same as batch header - if batch.header.ODFIIdentification != batch.control.ODFIIdentification { - msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.header.ODFIIdentification, batch.control.ODFIIdentification) + if batch.Header.ODFIIdentification != batch.Control.ODFIIdentification { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) return &BatchError{BatchNumber: batchNumber, FieldName: "ODFIIdentification", Msg: msg} } // batch number header and control must match - if batch.header.BatchNumber != batch.control.BatchNumber { - msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.header.ODFIIdentification, batch.control.ODFIIdentification) + if batch.Header.BatchNumber != batch.Control.BatchNumber { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) return &BatchError{BatchNumber: batchNumber, FieldName: "BatchNumber", Msg: msg} } @@ -108,38 +110,38 @@ func (batch *batch) verify() error { // the batch being built has invalid records. func (batch *batch) build() error { // Requires a valid BatchHeader - if err := batch.header.Validate(); err != nil { + if err := batch.Header.Validate(); err != nil { return err } - if len(batch.entries) <= 0 { - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} + if len(batch.Entries) <= 0 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} } // Create record sequence numbers entryCount := 0 seq := 1 - for i, entry := range batch.entries { + for i, entry := range batch.Entries { entryCount = entryCount + 1 + len(entry.Addendum) currentTraceNumberODFI, err := strconv.Atoi(entry.TraceNumberField()[:8]) if err != nil { return err } - batchHeaderODFI, err := strconv.Atoi(batch.header.ODFIIdentificationField()[:8]) + batchHeaderODFI, err := strconv.Atoi(batch.Header.ODFIIdentificationField()[:8]) if err != nil { return err } // Add a sequenced TraceNumber if one is not already set. Have to keep original trance number Return and NOC entries if currentTraceNumberODFI != batchHeaderODFI { - batch.entries[i].SetTraceNumber(batch.header.ODFIIdentification, seq) + batch.Entries[i].SetTraceNumber(batch.Header.ODFIIdentification, seq) } seq++ addendaSeq := 1 for x := range entry.Addendum { // sequences don't exist in NOC or Return addenda - if a, ok := batch.entries[i].Addendum[x].(*Addenda05); ok { + if a, ok := batch.Entries[i].Addendum[x].(*Addenda05); ok { a.SequenceNumber = addendaSeq - a.EntryDetailSequenceNumber = batch.parseNumField(batch.entries[i].TraceNumberField()[8:]) + a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) } addendaSeq++ } @@ -147,47 +149,47 @@ func (batch *batch) build() error { // build a BatchControl record bc := NewBatchControl() - bc.ServiceClassCode = batch.header.ServiceClassCode - bc.CompanyIdentification = batch.header.CompanyIdentification - bc.ODFIIdentification = batch.header.ODFIIdentification - bc.BatchNumber = batch.header.BatchNumber + bc.ServiceClassCode = batch.Header.ServiceClassCode + bc.CompanyIdentification = batch.Header.CompanyIdentification + bc.ODFIIdentification = batch.Header.ODFIIdentification + bc.BatchNumber = batch.Header.BatchNumber bc.EntryAddendaCount = entryCount bc.EntryHash = batch.parseNumField(batch.calculateEntryHash()) bc.TotalCreditEntryDollarAmount, bc.TotalDebitEntryDollarAmount = batch.calculateBatchAmounts() - batch.control = bc + batch.Control = bc return nil } // SetHeader appends an BatchHeader to the Batch func (batch *batch) SetHeader(batchHeader *BatchHeader) { - batch.header = batchHeader + batch.Header = batchHeader } // GetHeader returns the current Batch header func (batch *batch) GetHeader() *BatchHeader { - return batch.header + return batch.Header } // SetControl appends an BatchControl to the Batch func (batch *batch) SetControl(batchControl *BatchControl) { - batch.control = batchControl + batch.Control = batchControl } // GetControl returns the current Batch Control func (batch *batch) GetControl() *BatchControl { - return batch.control + return batch.Control } // GetEntries returns a slice of entry details for the batch func (batch *batch) GetEntries() []*EntryDetail { - return batch.entries + return batch.Entries } // AddEntry appends an EntryDetail to the Batch func (batch *batch) AddEntry(entry *EntryDetail) { batch.category = entry.Category - batch.entries = append(batch.entries, entry) + batch.Entries = append(batch.Entries, entry) } // IsReturn is true if the batch contains an Entry Return @@ -197,10 +199,10 @@ func (batch *batch) Category() string { // isFieldInclusion iterates through all the records in the batch and verifies against default fields func (batch *batch) isFieldInclusion() error { - if err := batch.header.Validate(); err != nil { + if err := batch.Header.Validate(); err != nil { return err } - for _, entry := range batch.entries { + for _, entry := range batch.Entries { if err := entry.Validate(); err != nil { return err } @@ -210,7 +212,7 @@ func (batch *batch) isFieldInclusion() error { } } } - return batch.control.Validate() + return batch.Control.Validate() } // isBatchEntryCount validate Entry count is accurate @@ -218,12 +220,12 @@ func (batch *batch) isFieldInclusion() error { // Record processed within the batch func (batch *batch) isBatchEntryCount() error { entryCount := 0 - for _, entry := range batch.entries { + for _, entry := range batch.Entries { entryCount = entryCount + 1 + len(entry.Addendum) } - if entryCount != batch.control.EntryAddendaCount { - msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.control.EntryAddendaCount) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "EntryAddendaCount", Msg: msg} + if entryCount != batch.Control.EntryAddendaCount { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.Control.EntryAddendaCount) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "EntryAddendaCount", Msg: msg} } return nil } @@ -233,20 +235,20 @@ func (batch *batch) isBatchEntryCount() error { // Entry Detail debit and credit totals within a given batch func (batch *batch) isBatchAmount() error { credit, debit := batch.calculateBatchAmounts() - if debit != batch.control.TotalDebitEntryDollarAmount { - msg := fmt.Sprintf(msgBatchCalculatedControlEquality, debit, batch.control.TotalDebitEntryDollarAmount) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TotalDebitEntryDollarAmount", Msg: msg} + if debit != batch.Control.TotalDebitEntryDollarAmount { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, debit, batch.Control.TotalDebitEntryDollarAmount) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TotalDebitEntryDollarAmount", Msg: msg} } - if credit != batch.control.TotalCreditEntryDollarAmount { - msg := fmt.Sprintf(msgBatchCalculatedControlEquality, credit, batch.control.TotalCreditEntryDollarAmount) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TotalCreditEntryDollarAmount", Msg: msg} + if credit != batch.Control.TotalCreditEntryDollarAmount { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, credit, batch.Control.TotalCreditEntryDollarAmount) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TotalCreditEntryDollarAmount", Msg: msg} } return nil } func (batch *batch) calculateBatchAmounts() (credit int, debit int) { - for _, entry := range batch.entries { + for _, entry := range batch.Entries { if entry.TransactionCode == 21 || entry.TransactionCode == 22 || entry.TransactionCode == 23 || entry.TransactionCode == 32 || entry.TransactionCode == 33 { credit = credit + entry.Amount } @@ -261,10 +263,10 @@ func (batch *batch) calculateBatchAmounts() (credit int, debit int) { // be in ascending Trace Number order (although Trace Numbers need not necessarily be consecutive). func (batch *batch) isSequenceAscending() error { lastSeq := -1 - for _, entry := range batch.entries { + for _, entry := range batch.Entries { if entry.TraceNumber <= lastSeq { msg := fmt.Sprintf(msgBatchAscending, entry.TraceNumber, lastSeq) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} } lastSeq = entry.TraceNumber } @@ -274,9 +276,9 @@ func (batch *batch) isSequenceAscending() error { // isEntryHash validates the hash by recalculating the result func (batch *batch) isEntryHash() error { hashField := batch.calculateEntryHash() - if hashField != batch.control.EntryHashField() { - msg := fmt.Sprintf(msgBatchCalculatedControlEquality, hashField, batch.control.EntryHashField()) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "EntryHash", Msg: msg} + if hashField != batch.Control.EntryHashField() { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, hashField, batch.Control.EntryHashField()) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "EntryHash", Msg: msg} } return nil } @@ -285,7 +287,7 @@ func (batch *batch) isEntryHash() error { // The Entry Hash provides a check against inadvertent alteration of data func (batch *batch) calculateEntryHash() string { hash := 0 - for _, entry := range batch.entries { + for _, entry := range batch.Entries { entryRDFI, _ := strconv.Atoi(entry.RDFIIdentification) @@ -296,11 +298,11 @@ func (batch *batch) calculateEntryHash() string { // The Originator Status Code is not equal to “2” for DNE if the Transaction Code is 23 or 33 func (batch *batch) isOriginatorDNE() error { - if batch.header.OriginatorStatusCode != 2 { - for _, entry := range batch.entries { + if batch.Header.OriginatorStatusCode != 2 { + for _, entry := range batch.Entries { if entry.TransactionCode == 23 || entry.TransactionCode == 33 { - msg := fmt.Sprintf(msgBatchOriginatorDNE, batch.header.OriginatorStatusCode) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "OriginatorStatusCode", Msg: msg} + msg := fmt.Sprintf(msgBatchOriginatorDNE, batch.Header.OriginatorStatusCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "OriginatorStatusCode", Msg: msg} } } } @@ -310,10 +312,10 @@ func (batch *batch) isOriginatorDNE() error { // isTraceNumberODFI checks if the first 8 positions of the entry detail trace number // match the batch header odfi func (batch *batch) isTraceNumberODFI() error { - for _, entry := range batch.entries { - if batch.header.ODFIIdentificationField() != entry.TraceNumberField()[:8] { - msg := fmt.Sprintf(msgBatchTraceNumberNotODFI, batch.header.ODFIIdentificationField(), entry.TraceNumberField()[:8]) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "ODFIIdentificationField", Msg: msg} + for _, entry := range batch.Entries { + if batch.Header.ODFIIdentificationField() != entry.TraceNumberField()[:8] { + msg := fmt.Sprintf(msgBatchTraceNumberNotODFI, batch.Header.ODFIIdentificationField(), entry.TraceNumberField()[:8]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ODFIIdentificationField", Msg: msg} } } @@ -322,11 +324,11 @@ func (batch *batch) isTraceNumberODFI() error { // isAddendaSequence check multiple errors on addenda records in the batch entries func (batch *batch) isAddendaSequence() error { - for _, entry := range batch.entries { + for _, entry := range batch.Entries { if len(entry.Addendum) > 0 { // addenda without indicator flag of 1 if entry.AddendaRecordIndicator != 1 { - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} } lastSeq := -1 // check if sequence is assending @@ -336,13 +338,13 @@ func (batch *batch) isAddendaSequence() error { if a.SequenceNumber < lastSeq { msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastSeq) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} } lastSeq = a.SequenceNumber // check that we are in the correct Entry Detail if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) { msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} } } } @@ -355,10 +357,10 @@ func (batch *batch) isAddendaSequence() error { // Following SEC codes allow for none or one Addendum // "PPD", "WEB", "CCD", "CIE", "DNE", "MTE", "POS", "SHR" func (batch *batch) isAddendaCount(count int) error { - for _, entry := range batch.entries { + for _, entry := range batch.Entries { if len(entry.Addendum) > count { - msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "AddendaCount", Msg: msg} + msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.Header.StandardEntryClassCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaCount", Msg: msg} } } @@ -367,11 +369,11 @@ func (batch *batch) isAddendaCount(count int) error { // isTypeCode takes a typecode string and verifies addenda records match func (batch *batch) isTypeCode(typeCode string) error { - for _, entry := range batch.entries { + for _, entry := range batch.Entries { for _, addenda := range entry.Addendum { if addenda.TypeCode() != typeCode { - msg := fmt.Sprintf(msgBatchTypeCode, addenda.TypeCode(), typeCode, batch.header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TypeCode", Msg: msg} + msg := fmt.Sprintf(msgBatchTypeCode, addenda.TypeCode(), typeCode, batch.Header.StandardEntryClassCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TypeCode", Msg: msg} } } } @@ -381,13 +383,13 @@ func (batch *batch) isTypeCode(typeCode string) error { // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *batch) isCategory() error { category := batch.GetEntries()[0].Category - if len(batch.entries) > 1 { - for i := 1; i < len(batch.entries); i++ { - if batch.entries[i].Category == CategoryNOC { + if len(batch.Entries) > 1 { + for i := 1; i < len(batch.Entries); i++ { + if batch.Entries[i].Category == CategoryNOC { continue } - if batch.entries[i].Category != category { - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Category", Msg: msgBatchForwardReturn} + if batch.Entries[i].Category != category { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Category", Msg: msgBatchForwardReturn} } } } @@ -398,11 +400,11 @@ func (batch *batch) isCategory() error { // "R" For a recurring WEB Entry // "S" For a Single-Entry WEB Entry func (batch *batch) isPaymentTypeCode() error { - for _, entry := range batch.entries { + for _, entry := range batch.Entries { if !strings.Contains(strings.ToUpper(entry.PaymentTypeField()), "S") && !strings.Contains(strings.ToUpper(entry.PaymentTypeField()), "R") { // TODO dead code because PaymentTypeField always returns S regardless of Discretionary Data value msg := fmt.Sprintf(msgBatchWebPaymentType, entry.PaymentTypeField()) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "PaymentType", Msg: msg} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "PaymentType", Msg: msg} } } return nil diff --git a/batchCCD.go b/batchCCD.go index 0ff08f3ad..025857af7 100644 --- a/batchCCD.go +++ b/batchCCD.go @@ -39,9 +39,9 @@ func (batch *BatchCCD) Validate() error { } // Add type specific validation. - if batch.header.StandardEntryClassCode != "CCD" { - msg := fmt.Sprintf(msgBatchSECType, batch.header.StandardEntryClassCode, "CCD") - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + if batch.Header.StandardEntryClassCode != "CCD" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "CCD") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } return nil diff --git a/batchCCD_test.go b/batchCCD_test.go index e3d756e7c..cd778aa26 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -156,7 +156,7 @@ func BenchmarkBatchCCDAddendaTypeCode(b *testing.B) { // testBatchCCDSEC validates that the standard entry class code is CCD for batchCCD func testBatchCCDSEC(t testing.TB) { mockBatch := mockBatchCCD() - mockBatch.header.StandardEntryClassCode = "RCK" + mockBatch.Header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "StandardEntryClassCode" { diff --git a/batchCOR.go b/batchCOR.go index 97949eb65..e3be0db77 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -40,16 +40,16 @@ func (batch *BatchCOR) Validate() error { } // Add type specific validation. - if batch.header.StandardEntryClassCode != "COR" { - msg := fmt.Sprintf(msgBatchSECType, batch.header.StandardEntryClassCode, "COR") - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + if batch.Header.StandardEntryClassCode != "COR" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "COR") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } // The Amount field must be zero // batch.verify calls batch.isBatchAmount which ensures the batch.Control values are accurate. - if batch.control.TotalCreditEntryDollarAmount != 0 || batch.control.TotalDebitEntryDollarAmount != 0 { - msg := fmt.Sprintf(msgBatchCORAmount, batch.control.TotalCreditEntryDollarAmount, batch.control.TotalDebitEntryDollarAmount) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Amount", Msg: msg} + if batch.Control.TotalCreditEntryDollarAmount != 0 || batch.Control.TotalDebitEntryDollarAmount != 0 { + msg := fmt.Sprintf(msgBatchCORAmount, batch.Control.TotalCreditEntryDollarAmount, batch.Control.TotalDebitEntryDollarAmount) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} } return nil @@ -67,22 +67,22 @@ func (batch *BatchCOR) Create() error { // isAddenda98 verifies that a Addenda98 exists for each EntryDetail and is Validated func (batch *BatchCOR) isAddenda98() error { - for _, entry := range batch.entries { + for _, entry := range batch.Entries { // Addenda type must be equal to 1 if len(entry.Addendum) != 1 { - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Addendum", Msg: msgBatchCORAddenda} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchCORAddenda} } // Addenda type assertion must be Addenda98 addenda98, ok := entry.Addendum[0].(*Addenda98) if !ok { msg := fmt.Sprintf(msgBatchCORAddendaType, entry.Addendum[0]) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "Addendum", Msg: msg} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} } // Addenda98 must be Validated if err := addenda98.Validate(); err != nil { // convert the field error in to a batch error for a consistent api if e, ok := err.(*FieldError); ok { - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} } } } diff --git a/batchCOR_test.go b/batchCOR_test.go index bfab389df..3ed5e68a8 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -73,7 +73,7 @@ func BenchmarkBatchCORHeader(b *testing.B) { // testBatchCORSEC validates COR SEC code func testBatchCORSEC(t testing.TB) { mockBatch := mockBatchCOR() - mockBatch.header.StandardEntryClassCode = "RCK" + mockBatch.Header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "StandardEntryClassCode" { diff --git a/batchControl.go b/batchControl.go index 1df07a0c5..16e0ead06 100644 --- a/batchControl.go +++ b/batchControl.go @@ -13,8 +13,9 @@ import ( // BatchControl contains entry counts, dollar total and has totals for all // entries contained in the preceding batch type BatchControl struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. - // batchControlPos 8 recordType string // ServiceClassCode ACH Mixed Debits and Credits ‘200’ // ACH Credits Only ‘220’ diff --git a/batchHeader.go b/batchHeader.go index 962fe849a..58d48330a 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -19,6 +19,8 @@ import ( // settlement date, for all entries contained in this batch. The settlement date // field is not entered as it is determined by the ACH operator type BatchHeader struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. 5 recordType string diff --git a/batchTEL.go b/batchTEL.go index d7c77e722..a95fcada3 100644 --- a/batchTEL.go +++ b/batchTEL.go @@ -34,15 +34,15 @@ func (batch *BatchTEL) Validate() error { } // Add type specific validation. - if batch.header.StandardEntryClassCode != "TEL" { - msg := fmt.Sprintf(msgBatchSECType, batch.header.StandardEntryClassCode, "TEL") - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + if batch.Header.StandardEntryClassCode != "TEL" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "TEL") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } // can not have credits in TEL batches - for _, entry := range batch.entries { + for _, entry := range batch.Entries { if entry.CreditOrDebit() != "D" { msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.IndividualName) - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} } } diff --git a/batchTEL_test.go b/batchTEL_test.go index 2c9293c66..b556edbfd 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -125,7 +125,7 @@ func BenchmarkBatchTELAddendaCount(b *testing.B) { // testBatchTELSEC validates SEC code for batch TEL func testBatchTELSEC(t testing.TB) { mockBatch := mockBatchTEL() - mockBatch.header.StandardEntryClassCode = "RCK" + mockBatch.Header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "StandardEntryClassCode" { diff --git a/batchWEB_test.go b/batchWEB_test.go index ba716e247..1bb6c3552 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -130,7 +130,7 @@ func BenchmarkBatchWEBAddendaTypeCode(b *testing.B) { // testBatchWebSEC validates that the standard entry class code is WEB for batch Web func testBatchWebSEC(t testing.TB) { mockBatch := mockBatchWEB() - mockBatch.header.StandardEntryClassCode = "RCK" + mockBatch.Header.StandardEntryClassCode = "RCK" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "StandardEntryClassCode" { diff --git a/batchWeb.go b/batchWeb.go index c88c946c9..00d987fbb 100644 --- a/batchWeb.go +++ b/batchWeb.go @@ -39,9 +39,9 @@ func (batch *BatchWEB) Validate() error { } // Add type specific validation. - if batch.header.StandardEntryClassCode != "WEB" { - msg := fmt.Sprintf(msgBatchSECType, batch.header.StandardEntryClassCode, "WEB") - return &BatchError{BatchNumber: batch.header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + if batch.Header.StandardEntryClassCode != "WEB" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "WEB") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } return batch.isPaymentTypeCode() diff --git a/batch_test.go b/batch_test.go index 254f3c097..18be510d6 100644 --- a/batch_test.go +++ b/batch_test.go @@ -501,7 +501,7 @@ func BenchmarkBatchTraceNumberExists(b *testing.B) { func testBatchFieldInclusion(t testing.TB) { mockBatch := mockBatch() - mockBatch.header.ODFIIdentification = "" + mockBatch.Header.ODFIIdentification = "" if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "ODFIIdentification" { @@ -568,7 +568,7 @@ func BenchmarkBatchNoEntry(b *testing.B) { func testBatchControl(t testing.TB) { mockBatch := mockBatch() - mockBatch.control.ODFIIdentification = "" + mockBatch.Control.ODFIIdentification = "" if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "ODFIIdentification" { diff --git a/coverage.out b/coverage.out deleted file mode 100644 index 55316af20..000000000 --- a/coverage.out +++ /dev/null @@ -1,688 +0,0 @@ -mode: count -github.com/wadearnold/ach/batchCOR.go:19.45,24.2 4 9 -github.com/wadearnold/ach/batchCOR.go:27.41,29.39 1 12 -github.com/wadearnold/ach/batchCOR.go:34.2,34.44 1 12 -github.com/wadearnold/ach/batchCOR.go:39.2,39.50 1 9 -github.com/wadearnold/ach/batchCOR.go:46.2,46.103 1 8 -github.com/wadearnold/ach/batchCOR.go:51.2,51.12 1 7 -github.com/wadearnold/ach/batchCOR.go:29.39,31.3 1 0 -github.com/wadearnold/ach/batchCOR.go:34.44,36.3 1 3 -github.com/wadearnold/ach/batchCOR.go:39.50,42.3 2 1 -github.com/wadearnold/ach/batchCOR.go:46.103,49.3 2 1 -github.com/wadearnold/ach/batchCOR.go:55.39,57.38 1 11 -github.com/wadearnold/ach/batchCOR.go:61.2,61.25 1 10 -github.com/wadearnold/ach/batchCOR.go:57.38,59.3 1 1 -github.com/wadearnold/ach/batchCOR.go:65.44,66.38 1 12 -github.com/wadearnold/ach/batchCOR.go:85.2,85.12 1 9 -github.com/wadearnold/ach/batchCOR.go:66.38,68.31 1 12 -github.com/wadearnold/ach/batchCOR.go:72.3,73.10 2 11 -github.com/wadearnold/ach/batchCOR.go:78.3,78.46 1 10 -github.com/wadearnold/ach/batchCOR.go:68.31,70.4 1 1 -github.com/wadearnold/ach/batchCOR.go:73.10,76.4 2 1 -github.com/wadearnold/ach/batchCOR.go:78.46,80.38 1 1 -github.com/wadearnold/ach/batchCOR.go:80.38,82.5 1 1 -github.com/wadearnold/ach/batchWeb.go:19.45,24.2 4 9 -github.com/wadearnold/ach/batchWeb.go:27.41,29.39 1 13 -github.com/wadearnold/ach/batchWeb.go:34.2,34.48 1 12 -github.com/wadearnold/ach/batchWeb.go:37.2,37.47 1 11 -github.com/wadearnold/ach/batchWeb.go:42.2,42.50 1 10 -github.com/wadearnold/ach/batchWeb.go:47.2,47.34 1 9 -github.com/wadearnold/ach/batchWeb.go:29.39,31.3 1 1 -github.com/wadearnold/ach/batchWeb.go:34.48,36.3 1 1 -github.com/wadearnold/ach/batchWeb.go:37.47,39.3 1 1 -github.com/wadearnold/ach/batchWeb.go:42.50,45.3 2 1 -github.com/wadearnold/ach/batchWeb.go:51.39,53.38 1 8 -github.com/wadearnold/ach/batchWeb.go:57.2,57.25 1 7 -github.com/wadearnold/ach/batchWeb.go:53.38,55.3 1 1 -github.com/wadearnold/ach/fileHeader.go:96.33,106.2 2 45 -github.com/wadearnold/ach/fileHeader.go:109.44,137.2 13 11 -github.com/wadearnold/ach/fileHeader.go:140.39,157.2 1 5 -github.com/wadearnold/ach/fileHeader.go:161.40,163.44 1 45 -github.com/wadearnold/ach/fileHeader.go:166.2,166.26 1 36 -github.com/wadearnold/ach/fileHeader.go:170.2,170.66 1 35 -github.com/wadearnold/ach/fileHeader.go:173.2,173.33 1 33 -github.com/wadearnold/ach/fileHeader.go:177.2,177.28 1 32 -github.com/wadearnold/ach/fileHeader.go:180.2,180.31 1 31 -github.com/wadearnold/ach/fileHeader.go:183.2,183.26 1 30 -github.com/wadearnold/ach/fileHeader.go:186.2,186.71 1 29 -github.com/wadearnold/ach/fileHeader.go:189.2,189.39 1 28 -github.com/wadearnold/ach/fileHeader.go:192.2,192.44 1 27 -github.com/wadearnold/ach/fileHeader.go:195.2,195.66 1 26 -github.com/wadearnold/ach/fileHeader.go:198.2,198.60 1 25 -github.com/wadearnold/ach/fileHeader.go:208.2,208.12 1 24 -github.com/wadearnold/ach/fileHeader.go:163.44,165.3 1 9 -github.com/wadearnold/ach/fileHeader.go:166.26,169.3 2 1 -github.com/wadearnold/ach/fileHeader.go:170.66,172.3 1 2 -github.com/wadearnold/ach/fileHeader.go:173.33,176.3 2 1 -github.com/wadearnold/ach/fileHeader.go:177.28,179.3 1 1 -github.com/wadearnold/ach/fileHeader.go:180.31,182.3 1 1 -github.com/wadearnold/ach/fileHeader.go:183.26,185.3 1 1 -github.com/wadearnold/ach/fileHeader.go:186.71,188.3 1 1 -github.com/wadearnold/ach/fileHeader.go:189.39,191.3 1 1 -github.com/wadearnold/ach/fileHeader.go:192.44,194.3 1 1 -github.com/wadearnold/ach/fileHeader.go:195.66,197.3 1 1 -github.com/wadearnold/ach/fileHeader.go:198.60,200.3 1 1 -github.com/wadearnold/ach/fileHeader.go:213.46,214.25 1 45 -github.com/wadearnold/ach/fileHeader.go:217.2,217.35 1 43 -github.com/wadearnold/ach/fileHeader.go:220.2,220.30 1 42 -github.com/wadearnold/ach/fileHeader.go:223.2,223.34 1 41 -github.com/wadearnold/ach/fileHeader.go:226.2,226.29 1 40 -github.com/wadearnold/ach/fileHeader.go:229.2,229.25 1 39 -github.com/wadearnold/ach/fileHeader.go:232.2,232.29 1 38 -github.com/wadearnold/ach/fileHeader.go:235.2,235.25 1 37 -github.com/wadearnold/ach/fileHeader.go:238.2,238.12 1 36 -github.com/wadearnold/ach/fileHeader.go:214.25,216.3 1 2 -github.com/wadearnold/ach/fileHeader.go:217.35,219.3 1 1 -github.com/wadearnold/ach/fileHeader.go:220.30,222.3 1 1 -github.com/wadearnold/ach/fileHeader.go:223.34,225.3 1 1 -github.com/wadearnold/ach/fileHeader.go:226.29,228.3 1 1 -github.com/wadearnold/ach/fileHeader.go:229.25,231.3 1 1 -github.com/wadearnold/ach/fileHeader.go:232.29,234.3 1 1 -github.com/wadearnold/ach/fileHeader.go:235.25,237.3 1 1 -github.com/wadearnold/ach/fileHeader.go:242.58,244.2 1 7 -github.com/wadearnold/ach/fileHeader.go:247.53,249.2 1 7 -github.com/wadearnold/ach/fileHeader.go:252.54,254.2 1 6 -github.com/wadearnold/ach/fileHeader.go:257.54,259.2 1 6 -github.com/wadearnold/ach/fileHeader.go:262.62,264.2 1 6 -github.com/wadearnold/ach/fileHeader.go:267.57,269.2 1 6 -github.com/wadearnold/ach/fileHeader.go:272.51,274.2 1 6 -github.com/wadearnold/ach/reader.go:22.37,23.20 1 2 -github.com/wadearnold/ach/reader.go:26.2,26.79 1 1 -github.com/wadearnold/ach/reader.go:23.20,25.3 1 1 -github.com/wadearnold/ach/reader.go:46.41,52.2 1 24 -github.com/wadearnold/ach/reader.go:56.49,58.2 1 24 -github.com/wadearnold/ach/reader.go:61.37,65.2 1 38 -github.com/wadearnold/ach/reader.go:70.39,73.23 2 27 -github.com/wadearnold/ach/reader.go:94.2,94.37 1 8 -github.com/wadearnold/ach/reader.go:99.2,99.39 1 5 -github.com/wadearnold/ach/reader.go:105.2,105.20 1 4 -github.com/wadearnold/ach/reader.go:73.23,78.10 4 73 -github.com/wadearnold/ach/reader.go:79.84,80.57 1 2 -github.com/wadearnold/ach/reader.go:83.35,86.31 3 3 -github.com/wadearnold/ach/reader.go:87.11,89.40 2 68 -github.com/wadearnold/ach/reader.go:80.57,82.5 1 1 -github.com/wadearnold/ach/reader.go:89.40,91.5 1 15 -github.com/wadearnold/ach/reader.go:94.37,98.3 2 3 -github.com/wadearnold/ach/reader.go:99.39,103.3 2 1 -github.com/wadearnold/ach/reader.go:108.60,111.26 2 2 -github.com/wadearnold/ach/reader.go:121.2,121.12 1 1 -github.com/wadearnold/ach/reader.go:111.26,113.39 2 658 -github.com/wadearnold/ach/reader.go:113.39,115.40 2 7 -github.com/wadearnold/ach/reader.go:118.4,118.15 1 6 -github.com/wadearnold/ach/reader.go:115.40,117.5 1 1 -github.com/wadearnold/ach/reader.go:124.36,125.20 1 75 -github.com/wadearnold/ach/reader.go:164.2,164.12 1 59 -github.com/wadearnold/ach/reader.go:126.21,127.45 1 9 -github.com/wadearnold/ach/reader.go:130.22,131.46 1 17 -github.com/wadearnold/ach/reader.go:134.22,135.46 1 16 -github.com/wadearnold/ach/reader.go:138.23,139.42 1 7 -github.com/wadearnold/ach/reader.go:142.23,143.47 1 9 -github.com/wadearnold/ach/reader.go:146.3,146.51 1 7 -github.com/wadearnold/ach/reader.go:150.3,151.23 2 6 -github.com/wadearnold/ach/reader.go:152.22,153.25 1 16 -github.com/wadearnold/ach/reader.go:157.3,157.46 1 6 -github.com/wadearnold/ach/reader.go:160.10,162.83 2 1 -github.com/wadearnold/ach/reader.go:127.45,129.4 1 2 -github.com/wadearnold/ach/reader.go:131.46,133.4 1 3 -github.com/wadearnold/ach/reader.go:135.46,137.4 1 1 -github.com/wadearnold/ach/reader.go:139.42,141.4 1 4 -github.com/wadearnold/ach/reader.go:143.47,145.4 1 2 -github.com/wadearnold/ach/reader.go:146.51,149.4 2 1 -github.com/wadearnold/ach/reader.go:153.25,155.9 1 10 -github.com/wadearnold/ach/reader.go:157.46,159.4 1 2 -github.com/wadearnold/ach/reader.go:168.42,170.37 2 11 -github.com/wadearnold/ach/reader.go:174.2,176.49 2 11 -github.com/wadearnold/ach/reader.go:179.2,179.12 1 9 -github.com/wadearnold/ach/reader.go:170.37,173.3 1 1 -github.com/wadearnold/ach/reader.go:176.49,178.3 1 2 -github.com/wadearnold/ach/reader.go:183.43,185.27 2 19 -github.com/wadearnold/ach/reader.go:191.2,193.38 3 18 -github.com/wadearnold/ach/reader.go:198.2,199.16 2 16 -github.com/wadearnold/ach/reader.go:203.2,204.12 2 16 -github.com/wadearnold/ach/reader.go:185.27,188.3 1 1 -github.com/wadearnold/ach/reader.go:193.38,195.3 1 2 -github.com/wadearnold/ach/reader.go:199.16,201.3 1 0 -github.com/wadearnold/ach/reader.go:208.43,210.27 2 18 -github.com/wadearnold/ach/reader.go:213.2,215.38 3 17 -github.com/wadearnold/ach/reader.go:218.2,219.12 2 17 -github.com/wadearnold/ach/reader.go:210.27,212.3 1 1 -github.com/wadearnold/ach/reader.go:215.38,217.3 1 0 -github.com/wadearnold/ach/reader.go:223.39,226.27 2 7 -github.com/wadearnold/ach/reader.go:230.2,230.43 1 6 -github.com/wadearnold/ach/reader.go:233.2,236.39 3 5 -github.com/wadearnold/ach/reader.go:265.2,265.12 1 3 -github.com/wadearnold/ach/reader.go:226.27,229.3 2 1 -github.com/wadearnold/ach/reader.go:230.43,232.3 1 1 -github.com/wadearnold/ach/reader.go:236.39,237.22 1 4 -github.com/wadearnold/ach/reader.go:238.13,241.47 3 2 -github.com/wadearnold/ach/reader.go:244.4,244.65 1 1 -github.com/wadearnold/ach/reader.go:245.13,248.47 3 1 -github.com/wadearnold/ach/reader.go:251.4,251.65 1 1 -github.com/wadearnold/ach/reader.go:252.13,255.47 3 1 -github.com/wadearnold/ach/reader.go:258.4,258.65 1 1 -github.com/wadearnold/ach/reader.go:241.47,243.5 1 1 -github.com/wadearnold/ach/reader.go:248.47,250.5 1 0 -github.com/wadearnold/ach/reader.go:255.47,257.5 1 0 -github.com/wadearnold/ach/reader.go:260.8,263.3 2 1 -github.com/wadearnold/ach/reader.go:269.44,271.27 2 11 -github.com/wadearnold/ach/reader.go:275.2,276.63 2 10 -github.com/wadearnold/ach/reader.go:279.2,279.12 1 9 -github.com/wadearnold/ach/reader.go:271.27,274.3 1 1 -github.com/wadearnold/ach/reader.go:276.63,278.3 1 1 -github.com/wadearnold/ach/reader.go:283.43,285.39 2 8 -github.com/wadearnold/ach/reader.go:289.2,290.50 2 7 -github.com/wadearnold/ach/reader.go:293.2,293.12 1 6 -github.com/wadearnold/ach/reader.go:285.39,288.3 1 1 -github.com/wadearnold/ach/reader.go:290.50,292.3 1 1 -github.com/wadearnold/ach/batchCCD.go:15.45,20.2 4 7 -github.com/wadearnold/ach/batchCCD.go:23.41,25.39 1 13 -github.com/wadearnold/ach/batchCCD.go:30.2,30.48 1 10 -github.com/wadearnold/ach/batchCCD.go:33.2,33.47 1 8 -github.com/wadearnold/ach/batchCCD.go:38.2,38.50 1 7 -github.com/wadearnold/ach/batchCCD.go:43.2,43.12 1 6 -github.com/wadearnold/ach/batchCCD.go:25.39,27.3 1 3 -github.com/wadearnold/ach/batchCCD.go:30.48,32.3 1 2 -github.com/wadearnold/ach/batchCCD.go:33.47,35.3 1 1 -github.com/wadearnold/ach/batchCCD.go:38.50,41.3 2 1 -github.com/wadearnold/ach/batchCCD.go:47.39,49.38 1 8 -github.com/wadearnold/ach/batchCCD.go:53.2,53.25 1 7 -github.com/wadearnold/ach/batchCCD.go:49.38,51.3 1 1 -github.com/wadearnold/ach/batchHeader.go:108.36,115.2 2 228 -github.com/wadearnold/ach/batchHeader.go:118.45,153.2 13 18 -github.com/wadearnold/ach/batchHeader.go:156.40,172.2 1 14 -github.com/wadearnold/ach/batchHeader.go:176.41,177.44 1 212 -github.com/wadearnold/ach/batchHeader.go:180.2,180.26 1 198 -github.com/wadearnold/ach/batchHeader.go:184.2,184.63 1 197 -github.com/wadearnold/ach/batchHeader.go:187.2,187.64 1 194 -github.com/wadearnold/ach/batchHeader.go:190.2,190.75 1 192 -github.com/wadearnold/ach/batchHeader.go:193.2,193.58 1 191 -github.com/wadearnold/ach/batchHeader.go:196.2,196.71 1 190 -github.com/wadearnold/ach/batchHeader.go:199.2,199.68 1 189 -github.com/wadearnold/ach/batchHeader.go:202.2,202.70 1 188 -github.com/wadearnold/ach/batchHeader.go:205.2,205.12 1 187 -github.com/wadearnold/ach/batchHeader.go:177.44,179.3 1 14 -github.com/wadearnold/ach/batchHeader.go:180.26,183.3 2 1 -github.com/wadearnold/ach/batchHeader.go:184.63,186.3 1 3 -github.com/wadearnold/ach/batchHeader.go:187.64,189.3 1 2 -github.com/wadearnold/ach/batchHeader.go:190.75,192.3 1 1 -github.com/wadearnold/ach/batchHeader.go:193.58,195.3 1 1 -github.com/wadearnold/ach/batchHeader.go:196.71,198.3 1 1 -github.com/wadearnold/ach/batchHeader.go:199.68,201.3 1 1 -github.com/wadearnold/ach/batchHeader.go:202.70,204.3 1 1 -github.com/wadearnold/ach/batchHeader.go:210.47,211.25 1 212 -github.com/wadearnold/ach/batchHeader.go:214.2,214.30 1 211 -github.com/wadearnold/ach/batchHeader.go:217.2,217.26 1 204 -github.com/wadearnold/ach/batchHeader.go:220.2,220.36 1 203 -github.com/wadearnold/ach/batchHeader.go:223.2,223.37 1 202 -github.com/wadearnold/ach/batchHeader.go:226.2,226.38 1 201 -github.com/wadearnold/ach/batchHeader.go:229.2,229.33 1 200 -github.com/wadearnold/ach/batchHeader.go:232.2,232.12 1 198 -github.com/wadearnold/ach/batchHeader.go:211.25,213.3 1 1 -github.com/wadearnold/ach/batchHeader.go:214.30,216.3 1 7 -github.com/wadearnold/ach/batchHeader.go:217.26,219.3 1 1 -github.com/wadearnold/ach/batchHeader.go:220.36,222.3 1 1 -github.com/wadearnold/ach/batchHeader.go:223.37,225.3 1 1 -github.com/wadearnold/ach/batchHeader.go:226.38,228.3 1 1 -github.com/wadearnold/ach/batchHeader.go:229.33,231.3 1 2 -github.com/wadearnold/ach/batchHeader.go:236.50,238.2 1 15 -github.com/wadearnold/ach/batchHeader.go:241.63,243.2 1 15 -github.com/wadearnold/ach/batchHeader.go:246.60,248.2 1 15 -github.com/wadearnold/ach/batchHeader.go:251.62,253.2 1 15 -github.com/wadearnold/ach/batchHeader.go:256.61,258.2 1 14 -github.com/wadearnold/ach/batchHeader.go:261.57,263.2 1 15 -github.com/wadearnold/ach/batchHeader.go:266.57,268.2 1 188 -github.com/wadearnold/ach/batchHeader.go:271.50,273.2 1 15 -github.com/wadearnold/ach/batchHeader.go:275.53,277.2 1 14 -github.com/wadearnold/ach/batchPPD.go:13.45,18.2 4 41 -github.com/wadearnold/ach/batchPPD.go:21.41,23.39 1 30 -github.com/wadearnold/ach/batchPPD.go:29.2,29.48 1 23 -github.com/wadearnold/ach/batchPPD.go:32.2,32.47 1 23 -github.com/wadearnold/ach/batchPPD.go:38.2,38.12 1 20 -github.com/wadearnold/ach/batchPPD.go:23.39,25.3 1 7 -github.com/wadearnold/ach/batchPPD.go:29.48,31.3 1 0 -github.com/wadearnold/ach/batchPPD.go:32.47,34.3 1 3 -github.com/wadearnold/ach/batchPPD.go:42.39,44.38 1 21 -github.com/wadearnold/ach/batchPPD.go:50.2,50.25 1 20 -github.com/wadearnold/ach/batchPPD.go:44.38,46.3 1 1 -github.com/wadearnold/ach/batcher.go:35.37,37.2 1 1 -github.com/wadearnold/ach/addenda05.go:36.32,41.2 4 45 -github.com/wadearnold/ach/addenda05.go:44.50,56.2 5 4 -github.com/wadearnold/ach/addenda05.go:59.45,66.2 1 7 -github.com/wadearnold/ach/addenda05.go:69.79,73.2 2 0 -github.com/wadearnold/ach/addenda05.go:77.46,78.51 1 51 -github.com/wadearnold/ach/addenda05.go:81.2,81.33 1 47 -github.com/wadearnold/ach/addenda05.go:85.2,85.65 1 46 -github.com/wadearnold/ach/addenda05.go:88.2,88.86 1 41 -github.com/wadearnold/ach/addenda05.go:92.2,92.12 1 40 -github.com/wadearnold/ach/addenda05.go:78.51,80.3 1 4 -github.com/wadearnold/ach/addenda05.go:81.33,84.3 2 1 -github.com/wadearnold/ach/addenda05.go:85.65,87.3 1 5 -github.com/wadearnold/ach/addenda05.go:88.86,90.3 1 1 -github.com/wadearnold/ach/addenda05.go:97.52,98.32 1 51 -github.com/wadearnold/ach/addenda05.go:101.2,101.30 1 50 -github.com/wadearnold/ach/addenda05.go:104.2,104.35 1 49 -github.com/wadearnold/ach/addenda05.go:107.2,107.46 1 48 -github.com/wadearnold/ach/addenda05.go:110.2,110.12 1 47 -github.com/wadearnold/ach/addenda05.go:98.32,100.3 1 1 -github.com/wadearnold/ach/addenda05.go:101.30,103.3 1 1 -github.com/wadearnold/ach/addenda05.go:104.35,106.3 1 1 -github.com/wadearnold/ach/addenda05.go:107.46,109.3 1 1 -github.com/wadearnold/ach/addenda05.go:114.69,116.2 1 8 -github.com/wadearnold/ach/addenda05.go:119.58,121.2 1 9 -github.com/wadearnold/ach/addenda05.go:124.69,126.2 1 44 -github.com/wadearnold/ach/addenda05.go:129.47,131.2 1 28 -github.com/wadearnold/ach/addenda98.go:43.13,46.2 1 1 -github.com/wadearnold/ach/addenda98.go:55.32,61.2 2 20 -github.com/wadearnold/ach/addenda98.go:64.50,79.2 7 3 -github.com/wadearnold/ach/addenda98.go:82.45,94.2 1 2 -github.com/wadearnold/ach/addenda98.go:97.46,98.33 1 26 -github.com/wadearnold/ach/addenda98.go:103.2,103.32 1 25 -github.com/wadearnold/ach/addenda98.go:108.2,109.9 2 22 -github.com/wadearnold/ach/addenda98.go:114.2,114.35 1 21 -github.com/wadearnold/ach/addenda98.go:118.2,118.12 1 20 -github.com/wadearnold/ach/addenda98.go:98.33,101.3 2 1 -github.com/wadearnold/ach/addenda98.go:103.32,105.3 1 3 -github.com/wadearnold/ach/addenda98.go:109.9,111.3 1 1 -github.com/wadearnold/ach/addenda98.go:114.35,116.3 1 1 -github.com/wadearnold/ach/addenda98.go:122.47,124.2 1 2 -github.com/wadearnold/ach/addenda98.go:127.57,129.2 1 3 -github.com/wadearnold/ach/addenda98.go:132.55,134.2 1 3 -github.com/wadearnold/ach/addenda98.go:137.57,139.2 1 3 -github.com/wadearnold/ach/addenda98.go:142.55,144.2 1 3 -github.com/wadearnold/ach/addenda98.go:146.50,163.29 3 1 -github.com/wadearnold/ach/addenda98.go:166.2,166.13 1 1 -github.com/wadearnold/ach/addenda98.go:163.29,165.3 1 11 -github.com/wadearnold/ach/batch.go:22.49,23.35 1 24 -github.com/wadearnold/ach/batch.go:36.2,37.71 2 1 -github.com/wadearnold/ach/batch.go:24.13,25.30 1 17 -github.com/wadearnold/ach/batch.go:26.13,27.30 1 3 -github.com/wadearnold/ach/batch.go:28.13,29.30 1 1 -github.com/wadearnold/ach/batch.go:30.13,31.30 1 1 -github.com/wadearnold/ach/batch.go:32.13,33.30 1 1 -github.com/wadearnold/ach/batch.go:34.10,34.10 0 1 -github.com/wadearnold/ach/batch.go:41.36,45.49 2 93 -github.com/wadearnold/ach/batch.go:53.2,53.69 1 87 -github.com/wadearnold/ach/batch.go:58.2,58.79 1 86 -github.com/wadearnold/ach/batch.go:63.2,63.73 1 85 -github.com/wadearnold/ach/batch.go:68.2,68.59 1 83 -github.com/wadearnold/ach/batch.go:73.2,73.50 1 82 -github.com/wadearnold/ach/batch.go:77.2,77.52 1 79 -github.com/wadearnold/ach/batch.go:81.2,81.46 1 76 -github.com/wadearnold/ach/batch.go:85.2,85.44 1 74 -github.com/wadearnold/ach/batch.go:89.2,89.48 1 73 -github.com/wadearnold/ach/batch.go:93.2,93.50 1 72 -github.com/wadearnold/ach/batch.go:97.2,97.50 1 71 -github.com/wadearnold/ach/batch.go:100.2,100.27 1 68 -github.com/wadearnold/ach/batch.go:45.49,47.37 1 6 -github.com/wadearnold/ach/batch.go:50.3,50.90 1 0 -github.com/wadearnold/ach/batch.go:47.37,49.4 1 6 -github.com/wadearnold/ach/batch.go:53.69,56.3 2 1 -github.com/wadearnold/ach/batch.go:58.79,61.3 2 1 -github.com/wadearnold/ach/batch.go:63.73,66.3 2 2 -github.com/wadearnold/ach/batch.go:68.59,71.3 2 1 -github.com/wadearnold/ach/batch.go:73.50,75.3 1 3 -github.com/wadearnold/ach/batch.go:77.52,79.3 1 3 -github.com/wadearnold/ach/batch.go:81.46,83.3 1 2 -github.com/wadearnold/ach/batch.go:85.44,87.3 1 1 -github.com/wadearnold/ach/batch.go:89.48,91.3 1 1 -github.com/wadearnold/ach/batch.go:93.50,95.3 1 1 -github.com/wadearnold/ach/batch.go:97.50,99.3 1 3 -github.com/wadearnold/ach/batch.go:105.35,107.48 1 83 -github.com/wadearnold/ach/batch.go:110.2,110.29 1 78 -github.com/wadearnold/ach/batch.go:114.2,116.38 3 77 -github.com/wadearnold/ach/batch.go:145.2,155.12 10 77 -github.com/wadearnold/ach/batch.go:107.48,109.3 1 5 -github.com/wadearnold/ach/batch.go:110.29,112.3 1 1 -github.com/wadearnold/ach/batch.go:116.38,119.17 3 94 -github.com/wadearnold/ach/batch.go:123.3,124.17 2 94 -github.com/wadearnold/ach/batch.go:129.3,129.48 1 94 -github.com/wadearnold/ach/batch.go:132.3,134.33 3 94 -github.com/wadearnold/ach/batch.go:119.17,121.4 1 0 -github.com/wadearnold/ach/batch.go:124.17,126.4 1 0 -github.com/wadearnold/ach/batch.go:129.48,131.4 1 1 -github.com/wadearnold/ach/batch.go:134.33,136.62 1 39 -github.com/wadearnold/ach/batch.go:140.4,140.16 1 39 -github.com/wadearnold/ach/batch.go:136.62,139.5 2 28 -github.com/wadearnold/ach/batch.go:159.57,161.2 1 98 -github.com/wadearnold/ach/batch.go:164.46,166.2 1 27 -github.com/wadearnold/ach/batch.go:169.60,171.2 1 73 -github.com/wadearnold/ach/batch.go:174.48,176.2 1 176 -github.com/wadearnold/ach/batch.go:179.49,181.2 1 140 -github.com/wadearnold/ach/batch.go:184.50,187.2 2 93 -github.com/wadearnold/ach/batch.go:190.39,192.2 1 22 -github.com/wadearnold/ach/batch.go:195.46,196.48 1 93 -github.com/wadearnold/ach/batch.go:199.2,199.38 1 89 -github.com/wadearnold/ach/batch.go:209.2,209.33 1 82 -github.com/wadearnold/ach/batch.go:196.48,198.3 1 4 -github.com/wadearnold/ach/batch.go:199.38,200.42 1 108 -github.com/wadearnold/ach/batch.go:203.3,203.42 1 106 -github.com/wadearnold/ach/batch.go:200.42,202.4 1 2 -github.com/wadearnold/ach/batch.go:203.42,204.45 1 53 -github.com/wadearnold/ach/batch.go:204.45,206.5 1 5 -github.com/wadearnold/ach/batch.go:215.47,217.38 2 82 -github.com/wadearnold/ach/batch.go:220.2,220.51 1 82 -github.com/wadearnold/ach/batch.go:224.2,224.12 1 79 -github.com/wadearnold/ach/batch.go:217.38,219.3 1 101 -github.com/wadearnold/ach/batch.go:220.51,223.3 2 3 -github.com/wadearnold/ach/batch.go:230.43,232.56 2 76 -github.com/wadearnold/ach/batch.go:237.2,237.58 1 75 -github.com/wadearnold/ach/batch.go:241.2,241.12 1 74 -github.com/wadearnold/ach/batch.go:232.56,235.3 2 1 -github.com/wadearnold/ach/batch.go:237.58,240.3 2 1 -github.com/wadearnold/ach/batch.go:244.69,245.38 1 153 -github.com/wadearnold/ach/batch.go:253.2,253.22 1 153 -github.com/wadearnold/ach/batch.go:245.38,246.158 1 176 -github.com/wadearnold/ach/batch.go:249.3,249.189 1 176 -github.com/wadearnold/ach/batch.go:246.158,248.4 1 117 -github.com/wadearnold/ach/batch.go:249.189,251.4 1 59 -github.com/wadearnold/ach/batch.go:258.49,260.38 2 79 -github.com/wadearnold/ach/batch.go:267.2,267.12 1 76 -github.com/wadearnold/ach/batch.go:260.38,261.35 1 88 -github.com/wadearnold/ach/batch.go:265.3,265.30 1 85 -github.com/wadearnold/ach/batch.go:261.35,264.4 2 3 -github.com/wadearnold/ach/batch.go:271.41,273.49 2 74 -github.com/wadearnold/ach/batch.go:277.2,277.12 1 73 -github.com/wadearnold/ach/batch.go:273.49,276.3 2 1 -github.com/wadearnold/ach/batch.go:282.49,284.38 2 151 -github.com/wadearnold/ach/batch.go:290.2,290.37 1 151 -github.com/wadearnold/ach/batch.go:284.38,289.3 2 172 -github.com/wadearnold/ach/batch.go:294.45,295.44 1 73 -github.com/wadearnold/ach/batch.go:303.2,303.12 1 72 -github.com/wadearnold/ach/batch.go:295.44,296.39 1 73 -github.com/wadearnold/ach/batch.go:296.39,297.66 1 77 -github.com/wadearnold/ach/batch.go:297.66,300.5 2 1 -github.com/wadearnold/ach/batch.go:308.47,309.38 1 72 -github.com/wadearnold/ach/batch.go:316.2,316.12 1 71 -github.com/wadearnold/ach/batch.go:309.38,310.77 1 76 -github.com/wadearnold/ach/batch.go:310.77,313.4 2 1 -github.com/wadearnold/ach/batch.go:320.47,321.38 1 71 -github.com/wadearnold/ach/batch.go:347.2,347.12 1 68 -github.com/wadearnold/ach/batch.go:321.38,322.30 1 75 -github.com/wadearnold/ach/batch.go:322.30,324.41 1 44 -github.com/wadearnold/ach/batch.go:327.4,329.43 2 43 -github.com/wadearnold/ach/batch.go:324.41,326.5 1 1 -github.com/wadearnold/ach/batch.go:329.43,331.42 1 47 -github.com/wadearnold/ach/batch.go:331.42,333.36 1 35 -github.com/wadearnold/ach/batch.go:337.6,339.79 2 34 -github.com/wadearnold/ach/batch.go:333.36,336.7 2 1 -github.com/wadearnold/ach/batch.go:339.79,342.7 2 1 -github.com/wadearnold/ach/batch.go:353.53,354.38 1 55 -github.com/wadearnold/ach/batch.go:361.2,361.12 1 50 -github.com/wadearnold/ach/batch.go:354.38,355.34 1 58 -github.com/wadearnold/ach/batch.go:355.34,358.4 2 5 -github.com/wadearnold/ach/batch.go:365.55,366.38 1 42 -github.com/wadearnold/ach/batch.go:374.2,374.12 1 37 -github.com/wadearnold/ach/batch.go:366.38,367.42 1 45 -github.com/wadearnold/ach/batch.go:367.42,368.38 1 24 -github.com/wadearnold/ach/batch.go:368.38,371.5 2 5 -github.com/wadearnold/ach/batch.go:378.40,380.28 2 68 -github.com/wadearnold/ach/batch.go:390.2,390.12 1 67 -github.com/wadearnold/ach/batch.go:380.28,381.43 1 2 -github.com/wadearnold/ach/batch.go:381.43,382.48 1 4 -github.com/wadearnold/ach/batch.go:385.4,385.45 1 4 -github.com/wadearnold/ach/batch.go:382.48,383.13 1 0 -github.com/wadearnold/ach/batch.go:385.45,387.5 1 1 -github.com/wadearnold/ach/batch.go:396.47,397.38 1 15 -github.com/wadearnold/ach/batch.go:404.2,404.12 1 15 -github.com/wadearnold/ach/batch.go:397.38,398.141 1 18 -github.com/wadearnold/ach/batch.go:398.141,402.4 2 0 -github.com/wadearnold/ach/converters.go:16.54,19.2 2 456 -github.com/wadearnold/ach/converters.go:21.60,24.2 2 301 -github.com/wadearnold/ach/converters.go:27.59,29.2 1 22 -github.com/wadearnold/ach/converters.go:32.58,35.2 2 32 -github.com/wadearnold/ach/converters.go:38.59,40.2 1 6 -github.com/wadearnold/ach/converters.go:43.58,46.2 2 11 -github.com/wadearnold/ach/converters.go:49.60,51.14 2 188 -github.com/wadearnold/ach/converters.go:54.2,55.10 2 184 -github.com/wadearnold/ach/converters.go:51.14,53.3 1 4 -github.com/wadearnold/ach/converters.go:59.59,62.14 3 774 -github.com/wadearnold/ach/converters.go:65.2,66.10 2 773 -github.com/wadearnold/ach/converters.go:62.14,64.3 1 1 -github.com/wadearnold/ach/converters.go:70.64,72.14 2 574 -github.com/wadearnold/ach/converters.go:75.2,76.10 2 513 -github.com/wadearnold/ach/converters.go:72.14,74.3 1 61 -github.com/wadearnold/ach/entryDetail.go:94.36,100.2 2 105 -github.com/wadearnold/ach/entryDetail.go:103.45,129.2 11 17 -github.com/wadearnold/ach/entryDetail.go:132.40,145.2 1 11 -github.com/wadearnold/ach/entryDetail.go:149.41,150.44 1 143 -github.com/wadearnold/ach/entryDetail.go:153.2,153.26 1 135 -github.com/wadearnold/ach/entryDetail.go:157.2,157.65 1 134 -github.com/wadearnold/ach/entryDetail.go:160.2,160.63 1 133 -github.com/wadearnold/ach/entryDetail.go:163.2,163.67 1 132 -github.com/wadearnold/ach/entryDetail.go:166.2,166.61 1 131 -github.com/wadearnold/ach/entryDetail.go:169.2,169.64 1 130 -github.com/wadearnold/ach/entryDetail.go:173.2,176.16 3 129 -github.com/wadearnold/ach/entryDetail.go:180.2,180.32 1 129 -github.com/wadearnold/ach/entryDetail.go:184.2,184.12 1 128 -github.com/wadearnold/ach/entryDetail.go:150.44,152.3 1 8 -github.com/wadearnold/ach/entryDetail.go:153.26,156.3 2 1 -github.com/wadearnold/ach/entryDetail.go:157.65,159.3 1 1 -github.com/wadearnold/ach/entryDetail.go:160.63,162.3 1 1 -github.com/wadearnold/ach/entryDetail.go:163.67,165.3 1 1 -github.com/wadearnold/ach/entryDetail.go:166.61,168.3 1 1 -github.com/wadearnold/ach/entryDetail.go:169.64,171.3 1 1 -github.com/wadearnold/ach/entryDetail.go:176.16,178.3 1 0 -github.com/wadearnold/ach/entryDetail.go:180.32,183.3 2 1 -github.com/wadearnold/ach/entryDetail.go:189.47,190.25 1 143 -github.com/wadearnold/ach/entryDetail.go:193.2,193.29 1 142 -github.com/wadearnold/ach/entryDetail.go:196.2,196.33 1 141 -github.com/wadearnold/ach/entryDetail.go:199.2,199.31 1 140 -github.com/wadearnold/ach/entryDetail.go:202.2,202.29 1 139 -github.com/wadearnold/ach/entryDetail.go:205.2,205.25 1 136 -github.com/wadearnold/ach/entryDetail.go:208.2,208.12 1 135 -github.com/wadearnold/ach/entryDetail.go:190.25,192.3 1 1 -github.com/wadearnold/ach/entryDetail.go:193.29,195.3 1 1 -github.com/wadearnold/ach/entryDetail.go:196.33,198.3 1 1 -github.com/wadearnold/ach/entryDetail.go:199.31,201.3 1 1 -github.com/wadearnold/ach/entryDetail.go:202.29,204.3 1 3 -github.com/wadearnold/ach/entryDetail.go:205.25,207.3 1 1 -github.com/wadearnold/ach/entryDetail.go:212.68,215.24 2 49 -github.com/wadearnold/ach/entryDetail.go:216.18,220.21 4 8 -github.com/wadearnold/ach/entryDetail.go:221.18,225.21 4 9 -github.com/wadearnold/ach/entryDetail.go:227.10,230.21 3 32 -github.com/wadearnold/ach/entryDetail.go:235.58,240.2 4 105 -github.com/wadearnold/ach/entryDetail.go:243.75,246.2 2 106 -github.com/wadearnold/ach/entryDetail.go:249.57,251.2 1 142 -github.com/wadearnold/ach/entryDetail.go:254.55,256.2 1 12 -github.com/wadearnold/ach/entryDetail.go:259.45,261.2 1 12 -github.com/wadearnold/ach/entryDetail.go:264.59,266.2 1 11 -github.com/wadearnold/ach/entryDetail.go:269.53,271.2 1 11 -github.com/wadearnold/ach/entryDetail.go:274.55,276.2 1 0 -github.com/wadearnold/ach/entryDetail.go:279.54,281.2 1 15 -github.com/wadearnold/ach/entryDetail.go:284.56,286.2 1 11 -github.com/wadearnold/ach/entryDetail.go:289.50,293.2 2 18 -github.com/wadearnold/ach/entryDetail.go:296.49,298.14 2 30 -github.com/wadearnold/ach/entryDetail.go:298.14,300.3 1 0 -github.com/wadearnold/ach/entryDetail.go:300.8,302.3 1 30 -github.com/wadearnold/ach/entryDetail.go:306.50,308.2 1 249 -github.com/wadearnold/ach/entryDetail.go:311.47,314.17 2 9 -github.com/wadearnold/ach/entryDetail.go:320.2,320.11 1 0 -github.com/wadearnold/ach/entryDetail.go:315.21,316.13 1 2 -github.com/wadearnold/ach/entryDetail.go:317.21,318.13 1 7 -github.com/wadearnold/ach/file.go:50.36,52.2 1 1 -github.com/wadearnold/ach/file.go:69.22,74.2 1 13 -github.com/wadearnold/ach/file.go:77.31,79.44 1 14 -github.com/wadearnold/ach/file.go:83.2,83.25 1 13 -github.com/wadearnold/ach/file.go:87.2,93.34 7 12 -github.com/wadearnold/ach/file.go:109.2,112.36 3 12 -github.com/wadearnold/ach/file.go:117.2,123.12 6 12 -github.com/wadearnold/ach/file.go:79.44,81.3 1 1 -github.com/wadearnold/ach/file.go:83.25,85.3 1 1 -github.com/wadearnold/ach/file.go:93.34,107.3 8 15 -github.com/wadearnold/ach/file.go:112.36,114.3 1 11 -github.com/wadearnold/ach/file.go:114.8,116.3 1 1 -github.com/wadearnold/ach/file.go:127.50,128.22 1 21 -github.com/wadearnold/ach/file.go:132.2,132.40 1 21 -github.com/wadearnold/ach/file.go:135.2,136.18 2 21 -github.com/wadearnold/ach/file.go:129.17,130.77 1 1 -github.com/wadearnold/ach/file.go:132.40,134.3 1 1 -github.com/wadearnold/ach/file.go:140.46,143.2 2 13 -github.com/wadearnold/ach/file.go:146.33,148.44 1 11 -github.com/wadearnold/ach/file.go:153.2,153.48 1 10 -github.com/wadearnold/ach/file.go:157.2,157.41 1 9 -github.com/wadearnold/ach/file.go:161.2,161.24 1 7 -github.com/wadearnold/ach/file.go:148.44,151.3 2 1 -github.com/wadearnold/ach/file.go:153.48,155.3 1 1 -github.com/wadearnold/ach/file.go:157.41,159.3 1 2 -github.com/wadearnold/ach/file.go:166.44,169.34 2 10 -github.com/wadearnold/ach/file.go:172.2,172.42 1 10 -github.com/wadearnold/ach/file.go:176.2,176.12 1 9 -github.com/wadearnold/ach/file.go:169.34,171.3 1 14 -github.com/wadearnold/ach/file.go:172.42,175.3 2 1 -github.com/wadearnold/ach/file.go:181.37,184.34 3 9 -github.com/wadearnold/ach/file.go:188.2,188.58 1 9 -github.com/wadearnold/ach/file.go:192.2,192.60 1 8 -github.com/wadearnold/ach/file.go:196.2,196.12 1 7 -github.com/wadearnold/ach/file.go:184.34,187.3 2 13 -github.com/wadearnold/ach/file.go:188.58,191.3 2 1 -github.com/wadearnold/ach/file.go:192.60,195.3 2 1 -github.com/wadearnold/ach/file.go:200.36,202.45 2 7 -github.com/wadearnold/ach/file.go:206.2,206.12 1 6 -github.com/wadearnold/ach/file.go:202.45,205.3 2 1 -github.com/wadearnold/ach/file.go:211.44,213.34 2 7 -github.com/wadearnold/ach/file.go:216.2,216.33 1 7 -github.com/wadearnold/ach/file.go:213.34,215.3 1 11 -github.com/wadearnold/ach/validators.go:34.37,36.2 1 2 -github.com/wadearnold/ach/validators.go:53.52,54.14 1 294 -github.com/wadearnold/ach/validators.go:66.2,66.36 1 4 -github.com/wadearnold/ach/validators.go:63.7,64.13 1 290 -github.com/wadearnold/ach/validators.go:70.50,71.14 1 194 -github.com/wadearnold/ach/validators.go:77.2,77.31 1 2 -github.com/wadearnold/ach/validators.go:74.86,75.13 1 192 -github.com/wadearnold/ach/validators.go:83.51,84.14 1 46 -github.com/wadearnold/ach/validators.go:100.2,100.39 1 5 -github.com/wadearnold/ach/validators.go:97.8,98.13 1 41 -github.com/wadearnold/ach/validators.go:121.55,122.14 1 134 -github.com/wadearnold/ach/validators.go:229.2,229.39 1 1 -github.com/wadearnold/ach/validators.go:226.6,227.13 1 133 -github.com/wadearnold/ach/validators.go:233.60,234.14 1 192 -github.com/wadearnold/ach/validators.go:244.2,244.38 1 1 -github.com/wadearnold/ach/validators.go:241.5,242.13 1 191 -github.com/wadearnold/ach/validators.go:248.57,249.43 1 35 -github.com/wadearnold/ach/validators.go:252.2,252.12 1 33 -github.com/wadearnold/ach/validators.go:249.43,251.3 1 2 -github.com/wadearnold/ach/validators.go:256.52,257.38 1 1595 -github.com/wadearnold/ach/validators.go:261.2,261.12 1 1580 -github.com/wadearnold/ach/validators.go:257.38,260.3 1 15 -github.com/wadearnold/ach/validators.go:271.67,273.25 2 129 -github.com/wadearnold/ach/validators.go:276.2,293.31 17 129 -github.com/wadearnold/ach/validators.go:273.25,275.3 1 1032 -github.com/wadearnold/ach/validators.go:297.42,299.2 1 129 -github.com/wadearnold/ach/writer.go:24.37,28.2 1 2 -github.com/wadearnold/ach/writer.go:31.42,32.40 1 2 -github.com/wadearnold/ach/writer.go:36.2,38.72 2 2 -github.com/wadearnold/ach/writer.go:41.2,43.37 2 2 -github.com/wadearnold/ach/writer.go:65.2,65.73 1 2 -github.com/wadearnold/ach/writer.go:68.2,71.64 2 2 -github.com/wadearnold/ach/writer.go:77.2,77.12 1 2 -github.com/wadearnold/ach/writer.go:32.40,34.3 1 0 -github.com/wadearnold/ach/writer.go:38.72,40.3 1 0 -github.com/wadearnold/ach/writer.go:43.37,44.79 1 3 -github.com/wadearnold/ach/writer.go:47.3,48.44 2 3 -github.com/wadearnold/ach/writer.go:60.3,60.80 1 3 -github.com/wadearnold/ach/writer.go:63.3,63.14 1 3 -github.com/wadearnold/ach/writer.go:44.79,46.4 1 0 -github.com/wadearnold/ach/writer.go:48.44,49.68 1 2 -github.com/wadearnold/ach/writer.go:52.4,53.43 2 2 -github.com/wadearnold/ach/writer.go:49.68,51.5 1 0 -github.com/wadearnold/ach/writer.go:53.43,54.71 1 2 -github.com/wadearnold/ach/writer.go:57.5,57.16 1 2 -github.com/wadearnold/ach/writer.go:54.71,56.6 1 0 -github.com/wadearnold/ach/writer.go:60.80,62.4 1 0 -github.com/wadearnold/ach/writer.go:65.73,67.3 1 0 -github.com/wadearnold/ach/writer.go:71.64,72.76 1 6 -github.com/wadearnold/ach/writer.go:72.76,74.4 1 0 -github.com/wadearnold/ach/writer.go:82.26,84.2 1 1 -github.com/wadearnold/ach/writer.go:87.32,90.2 2 0 -github.com/wadearnold/ach/writer.go:93.48,94.29 1 2 -github.com/wadearnold/ach/writer.go:102.2,102.20 1 2 -github.com/wadearnold/ach/writer.go:94.29,98.17 2 2 -github.com/wadearnold/ach/writer.go:98.17,100.4 1 0 -github.com/wadearnold/ach/addenda99.go:24.13,27.2 1 1 -github.com/wadearnold/ach/addenda99.go:65.32,71.2 2 17 -github.com/wadearnold/ach/addenda99.go:74.50,91.2 8 3 -github.com/wadearnold/ach/addenda99.go:94.45,105.2 1 2 -github.com/wadearnold/ach/addenda99.go:108.46,110.33 1 5 -github.com/wadearnold/ach/addenda99.go:116.2,117.9 2 5 -github.com/wadearnold/ach/addenda99.go:121.2,121.12 1 4 -github.com/wadearnold/ach/addenda99.go:110.33,113.3 2 0 -github.com/wadearnold/ach/addenda99.go:117.9,120.3 1 1 -github.com/wadearnold/ach/addenda99.go:125.47,127.2 1 4 -github.com/wadearnold/ach/addenda99.go:130.57,132.2 1 3 -github.com/wadearnold/ach/addenda99.go:135.55,137.36 1 4 -github.com/wadearnold/ach/addenda99.go:141.2,141.58 1 1 -github.com/wadearnold/ach/addenda99.go:137.36,139.3 1 3 -github.com/wadearnold/ach/addenda99.go:145.55,147.2 1 3 -github.com/wadearnold/ach/addenda99.go:150.62,152.2 1 3 -github.com/wadearnold/ach/addenda99.go:155.55,157.2 1 3 -github.com/wadearnold/ach/addenda99.go:159.50,202.29 3 2 -github.com/wadearnold/ach/addenda99.go:205.2,205.13 1 2 -github.com/wadearnold/ach/addenda99.go:202.29,204.3 1 70 -github.com/wadearnold/ach/batchControl.go:72.46,96.2 11 10 -github.com/wadearnold/ach/batchControl.go:99.38,106.2 1 162 -github.com/wadearnold/ach/batchControl.go:109.41,123.2 1 8 -github.com/wadearnold/ach/batchControl.go:127.42,128.44 1 101 -github.com/wadearnold/ach/batchControl.go:131.2,131.26 1 98 -github.com/wadearnold/ach/batchControl.go:135.2,135.63 1 97 -github.com/wadearnold/ach/batchControl.go:139.2,139.68 1 96 -github.com/wadearnold/ach/batchControl.go:143.2,143.72 1 94 -github.com/wadearnold/ach/batchControl.go:147.2,147.12 1 93 -github.com/wadearnold/ach/batchControl.go:128.44,130.3 1 3 -github.com/wadearnold/ach/batchControl.go:131.26,134.3 2 1 -github.com/wadearnold/ach/batchControl.go:135.63,137.3 1 1 -github.com/wadearnold/ach/batchControl.go:139.68,141.3 1 2 -github.com/wadearnold/ach/batchControl.go:143.72,145.3 1 1 -github.com/wadearnold/ach/batchControl.go:152.48,153.25 1 101 -github.com/wadearnold/ach/batchControl.go:156.2,156.30 1 100 -github.com/wadearnold/ach/batchControl.go:159.2,159.42 1 99 -github.com/wadearnold/ach/batchControl.go:162.2,162.12 1 98 -github.com/wadearnold/ach/batchControl.go:153.25,155.3 1 1 -github.com/wadearnold/ach/batchControl.go:156.30,158.3 1 1 -github.com/wadearnold/ach/batchControl.go:159.42,161.3 1 1 -github.com/wadearnold/ach/batchControl.go:166.57,168.2 1 9 -github.com/wadearnold/ach/batchControl.go:171.49,173.2 1 84 -github.com/wadearnold/ach/batchControl.go:176.67,178.2 1 9 -github.com/wadearnold/ach/batchControl.go:181.68,183.2 1 9 -github.com/wadearnold/ach/batchControl.go:186.61,188.2 1 9 -github.com/wadearnold/ach/batchControl.go:191.65,193.2 1 9 -github.com/wadearnold/ach/batchControl.go:196.58,198.2 1 10 -github.com/wadearnold/ach/batchControl.go:201.51,203.2 1 9 -github.com/wadearnold/ach/batchTEL.go:13.45,18.2 4 6 -github.com/wadearnold/ach/batchTEL.go:21.41,23.39 1 11 -github.com/wadearnold/ach/batchTEL.go:28.2,28.48 1 10 -github.com/wadearnold/ach/batchTEL.go:33.2,33.50 1 8 -github.com/wadearnold/ach/batchTEL.go:38.2,38.38 1 7 -github.com/wadearnold/ach/batchTEL.go:45.2,45.34 1 6 -github.com/wadearnold/ach/batchTEL.go:23.39,25.3 1 1 -github.com/wadearnold/ach/batchTEL.go:28.48,30.3 1 2 -github.com/wadearnold/ach/batchTEL.go:33.50,36.3 2 1 -github.com/wadearnold/ach/batchTEL.go:38.38,39.35 1 7 -github.com/wadearnold/ach/batchTEL.go:39.35,42.4 2 1 -github.com/wadearnold/ach/batchTEL.go:49.39,51.38 1 8 -github.com/wadearnold/ach/batchTEL.go:55.2,55.25 1 7 -github.com/wadearnold/ach/batchTEL.go:51.38,53.3 1 1 -github.com/wadearnold/ach/fileControl.go:42.45,60.2 8 7 -github.com/wadearnold/ach/fileControl.go:63.35,68.2 1 35 -github.com/wadearnold/ach/fileControl.go:71.40,82.2 1 4 -github.com/wadearnold/ach/fileControl.go:86.41,87.44 1 14 -github.com/wadearnold/ach/fileControl.go:90.2,90.26 1 8 -github.com/wadearnold/ach/fileControl.go:94.2,94.12 1 7 -github.com/wadearnold/ach/fileControl.go:87.44,89.3 1 6 -github.com/wadearnold/ach/fileControl.go:90.26,93.3 2 1 -github.com/wadearnold/ach/fileControl.go:99.47,100.25 1 14 -github.com/wadearnold/ach/fileControl.go:103.2,103.24 1 13 -github.com/wadearnold/ach/fileControl.go:106.2,106.24 1 11 -github.com/wadearnold/ach/fileControl.go:109.2,109.31 1 10 -github.com/wadearnold/ach/fileControl.go:112.2,112.23 1 9 -github.com/wadearnold/ach/fileControl.go:115.2,115.12 1 8 -github.com/wadearnold/ach/fileControl.go:100.25,102.3 1 1 -github.com/wadearnold/ach/fileControl.go:103.24,105.3 1 2 -github.com/wadearnold/ach/fileControl.go:106.24,108.3 1 1 -github.com/wadearnold/ach/fileControl.go:109.31,111.3 1 1 -github.com/wadearnold/ach/fileControl.go:112.23,114.3 1 1 -github.com/wadearnold/ach/fileControl.go:119.49,121.2 1 7 -github.com/wadearnold/ach/fileControl.go:124.49,126.2 1 6 -github.com/wadearnold/ach/fileControl.go:129.56,131.2 1 8 -github.com/wadearnold/ach/fileControl.go:134.48,136.2 1 14 -github.com/wadearnold/ach/fileControl.go:139.72,141.2 1 6 -github.com/wadearnold/ach/fileControl.go:144.73,146.2 1 6 diff --git a/entryDetail.go b/entryDetail.go index 1e21122b7..01e50555e 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -15,6 +15,8 @@ import ( // withdrawal (debit), the transit routing number for the entry recipient’s financial // institution, the account number (left justify,no zero fill), name, and dollar amount. type EntryDetail struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. 6 recordType string // TransactionCode if the receivers account is: diff --git a/file.go b/file.go index ad16502b9..23c4ad30c 100644 --- a/file.go +++ b/file.go @@ -53,6 +53,7 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { + ID string `json:"id"` Header FileHeader `json:"fileHeader"` Batches []Batcher `json:"batches"` Control FileControl `json:"fileControl"` diff --git a/fileControl.go b/fileControl.go index 05ed7ed91..d63dd7f70 100644 --- a/fileControl.go +++ b/fileControl.go @@ -9,6 +9,8 @@ import "fmt" // FileControl record contains entry counts, dollar totals and hash // totals accumulated from each batch control record in the file. type FileControl struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. fileControlPos 9 recordType string diff --git a/fileHeader.go b/fileHeader.go index 7d681ea36..cf01b2fde 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -23,12 +23,12 @@ var ( // contained in the file. The file header also includes creation date and time // fields which can be used to uniquely identify a file. type FileHeader struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` // RecordType defines the type of record in the block. headerPos recordType string - // PriorityCode consists of the numerals 01 priorityCode string - // ImmediateDestination contains the Routing Number of the ACH Operator or receiving // point to which the file is being sent. The ach file format specifies a 10 character // field begins with a blank space in the first position, followed by the four digit diff --git a/record_test.go b/record_test.go index bdb7f98b1..393a50f34 100644 --- a/record_test.go +++ b/record_test.go @@ -45,7 +45,7 @@ func testBatchRecord(t testing.TB) { t.Errorf("%T: %s", err, err) } if bh.CompanyName != companyName { - t.Errorf("BatchParam value was not copied to batch.header.CompanyName") + t.Errorf("BatchParam value was not copied to batch.Header.CompanyName") } } From bb059466c4890d2651383087f2e3f5b0f68a5677 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 24 May 2018 12:15:23 -0600 Subject: [PATCH 0134/1694] File CRUD functions and tests --- server/repositoryInMemory.go | 58 ++++++++++++++++++++++ server/service.go | 95 ++++++++++++++++++++++++++++++++++++ server/service_test.go | 79 ++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 server/repositoryInMemory.go create mode 100644 server/service.go create mode 100644 server/service_test.go diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go new file mode 100644 index 000000000..bb583c636 --- /dev/null +++ b/server/repositoryInMemory.go @@ -0,0 +1,58 @@ +package server + +// TODO: rename to InMemory and move into a repository directory when stable. +import ( + "sync" + + "github.com/moov-io/ach" +) + +type repositoryInMemory struct { + mtx sync.RWMutex + files map[string]*ach.File +} + +// NewRepositoryInMemory is an in memory ach storage repository for files +func NewRepositoryInMemory() Repository { + f := map[string]*ach.File{} + return &repositoryInMemory{ + files: f, + } +} +func (r *repositoryInMemory) StoreFile(f *ach.File) error { + r.mtx.Lock() + defer r.mtx.Unlock() + if _, ok := r.files[f.ID]; ok { + return ErrAlreadyExists + } + r.files[f.ID] = f + return nil +} + +// FindFile retrieves a ach.File based on the supplied ID +func (r *repositoryInMemory) FindFile(id string) (*ach.File, error) { + r.mtx.RLock() + defer r.mtx.RUnlock() + if val, ok := r.files[id]; ok { + return val, nil + } + return nil, ErrNotFound +} + +// FindAllFiles returns all files that have been saved in memory +func (r *repositoryInMemory) FindAllFiles() []*ach.File { + r.mtx.RLock() + defer r.mtx.RUnlock() + files := make([]*ach.File, 0, len(r.files)) + for _, val := range r.files { + files = append(files, val) + } + return files +} + +func (r *repositoryInMemory) DeleteFile(id string) error { + r.mtx.RLock() + defer r.mtx.RUnlock() + delete(r.files, id) + return nil +} diff --git a/server/service.go b/server/service.go new file mode 100644 index 000000000..c316d0375 --- /dev/null +++ b/server/service.go @@ -0,0 +1,95 @@ +package server + +import ( + "errors" + "strings" + + "github.com/moov-io/ach" + uuid "github.com/satori/go.uuid" +) + +var ( + ErrNotFound = errors.New("Not Found") + ErrAlreadyExists = errors.New("Already Exists") +) + +// Service is a REST interface for interacting with ACH file structures +// TODO: Add ctx to function paramaters to pass the client security token +type Service interface { + // CreateFile creates a new ach file record and returns a resource ID + CreateFile(f ach.File) (string, error) + // AddFile retrieves a file based on the File id + GetFile(id string) (ach.File, error) + // GetFiles retrieves all files accessable from the client. + GetFiles() []ach.File + // DeleteFile takes a file resource ID and deletes it from the repository + DeleteFile(id string) error + // UpdateFile updates the changes properties of a matching File ID + // UpdateFile(f ach.File) (string, error) +} + +// service a concrete implementation of the service. +type service struct { + repository Repository +} + +// NewService creates a new concrete service +func NewService(r Repository) Service { + return &service{ + repository: r, + } +} + +// CreateFile add a file to storage +func (s *service) CreateFile(f ach.File) (string, error) { + if f.ID == "" { + f.ID = NextID() + } + if err := s.repository.StoreFile(&f); err != nil { + return "", err + } + return f.ID, nil +} + +// GetFile returns a files based on the supplied id +func (s *service) GetFile(id string) (ach.File, error) { + f, err := s.repository.FindFile(id) + if err != nil { + return ach.File{}, ErrNotFound + } + return *f, nil +} + +func (s *service) GetFiles() []ach.File { + var result []ach.File + for _, f := range s.repository.FindAllFiles() { + result = append(result, *f) + } + return result +} + +func (s *service) DeleteFile(id string) error { + return s.repository.DeleteFile(id) +} + +// Repository concrete implementations +// ******** + +// Repository is the Service storage mechanism abstraction +type Repository interface { + StoreFile(file *ach.File) error + FindFile(id string) (*ach.File, error) + FindAllFiles() []*ach.File + DeleteFile(id string) error +} + +// Utility Functions +// ***** + +// NextID generates a new resource ID +func NextID() string { + id, _ := uuid.NewV4() + //return id.String() + // make it shorter for testing URL + return string(strings.Split(strings.ToUpper(id.String()), "-")[0]) +} diff --git a/server/service_test.go b/server/service_test.go new file mode 100644 index 000000000..23fb333dd --- /dev/null +++ b/server/service_test.go @@ -0,0 +1,79 @@ +package server + +import ( + "testing" + + "github.com/moov-io/ach" +) + +func mockServiceInMemory() Service { + repository := NewRepositoryInMemory() + repository.StoreFile(&ach.File{ID: "98765"}) + return NewService(repository) +} + +// CreateFile tests +func TestCreateFile(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateFile(ach.File{ID: "12345"}) + if id != "12345" { + t.Errorf("expected %s received %s w/ error %s", "12345", id, err) + } +} +func TestCreateFileIDExists(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateFile(ach.File{ID: "98765"}) + if err != ErrAlreadyExists { + t.Errorf("expected %s received %s w/ error %s", "ErrAlreadyExists", id, err) + } +} + +func TestCreateFileNoID(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateFile(*ach.NewFile()) + if len(id) < 3 { + t.Errorf("expected %s received %s w/ error %s", "NextID", id, err) + } +} + +// Service.GetFile tests + +func TestGetFile(t *testing.T) { + s := mockServiceInMemory() + f, err := s.GetFile("98765") + if err != nil { + t.Errorf("expected %s received %s w/ error %s", "98765", f.ID, err) + } +} + +func TestGetFileNotFound(t *testing.T) { + s := mockServiceInMemory() + f, err := s.GetFile("12345") + if err != ErrNotFound { + t.Errorf("expected %s received %s w/ error %s", "ErrNotFound", f.ID, err) + } +} + +// Service.GetFiles tests + +func TestGetFiles(t *testing.T) { + s := mockServiceInMemory() + files := s.GetFiles() + if len(files) != 1 { + t.Errorf("expected %s received %v", "1", len(files)) + } +} + +// Service.DeleteFile tests + +func TestDeleteFile(t *testing.T) { + s := mockServiceInMemory() + err := s.DeleteFile("98765") + if err != nil { + t.Errorf("expected %s received %s", "nil", err) + } + _, err = s.GetFile("98765") + if err != ErrNotFound { + t.Errorf("expected %s received %s", "ErrNotFound", err) + } +} From 2aaec39d0d58f2996a34522f68655cade6aaaab7 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 25 May 2018 12:30:55 -0600 Subject: [PATCH 0135/1694] Basic CRUD File endpoints --- cmd/server/main.go | 67 +++++++++++++++++ file.go | 1 + server/endpoints.go | 97 +++++++++++++++++++++++++ server/middlewear.go | 53 ++++++++++++++ server/service.go | 4 +- server/transport.go | 169 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 cmd/server/main.go create mode 100644 server/endpoints.go create mode 100644 server/middlewear.go create mode 100644 server/transport.go diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 000000000..0a6124f37 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "flag" + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/wadearnold/ach/server" + "github.com/wadearnold/kit/log" +) + +/** +CreateFile +curl -d '{"id":"1234"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/ + +GetFile +curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/1234 + +GetFiles +curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/ + +DeleteFile +curl -H "Content-Type: application/json" -X DELETE http://localhost:8080/files/1234 +**/ + +func main() { + var ( + httpAddr = flag.String("http.addr", ":8080", "HTTP listen address") + ) + flag.Parse() + + var logger log.Logger + { + logger = log.NewLogfmtLogger(os.Stderr) + logger = log.With(logger, "ts", log.DefaultTimestampUTC) + logger = log.With(logger, "caller", log.DefaultCaller) + } + + var s server.Service + { + s = server.NewService(server.NewRepositoryInMemory()) + s = server.LoggingMiddleware(logger)(s) + } + + var h http.Handler + { + h = server.MakeHTTPHandler(s, log.With(logger, "component", "HTTP")) + } + + // Listen for application termination. + errs := make(chan error) + go func() { + c := make(chan os.Signal) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errs <- fmt.Errorf("%s", <-c) + }() + + go func() { + logger.Log("transport", "HTTP", "addr", *httpAddr) + errs <- http.ListenAndServe(*httpAddr, h) + }() + + logger.Log("exit", <-errs) +} diff --git a/file.go b/file.go index 23c4ad30c..883be1d04 100644 --- a/file.go +++ b/file.go @@ -53,6 +53,7 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { + // ID is a client defined string used as a reference to this record. ID string `json:"id"` Header FileHeader `json:"fileHeader"` Batches []Batcher `json:"batches"` diff --git a/server/endpoints.go b/server/endpoints.go new file mode 100644 index 000000000..cbe80c5d9 --- /dev/null +++ b/server/endpoints.go @@ -0,0 +1,97 @@ +package server + +import ( + "context" + + "github.com/go-kit/kit/endpoint" + "github.com/moov-io/ach" +) + +type Endpoints struct { + CreateFileEndpoint endpoint.Endpoint + GetFileEndpoint endpoint.Endpoint + GetFilesEndpoint endpoint.Endpoint + DeleteFileEndpoint endpoint.Endpoint +} + +func MakeServerEndpoints(s Service) Endpoints { + return Endpoints{ + CreateFileEndpoint: MakeCreateFileEndpoint(s), + GetFileEndpoint: MakeGetFileEndpoint(s), + GetFilesEndpoint: MakeGetFilesEndpoint(s), + DeleteFileEndpoint: MakeDeleteFileEndpoint(s), + } +} + +// MakeCreateFileEndpoint returns an endpoint via the passed service. +func MakeCreateFileEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(createFileRequest) + id, e := s.CreateFile(req.File) + return createFileResponse{ID: id, Err: e}, nil + } +} + +type createFileRequest struct { + File ach.File +} + +type createFileResponse struct { + ID string `json:"id,omitempty"` + Err error `json:"err,omitempty"` +} + +func (r createFileResponse) error() error { return r.Err } + +func MakeGetFilesEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + _ = request.(getFilesRequest) + return getFilesResponse{Files: s.GetFiles(), Err: nil}, nil + } +} + +type getFilesRequest struct{} + +type getFilesResponse struct { + Files []ach.File `json:"files,omitempty"` + Err error `json:"error,omitempty"` +} + +// MakeGetFileEndpoint returns an endpoint via the passed service. +// Primarily useful in a server. +func MakeGetFileEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(getFileRequest) + f, e := s.GetFile(req.ID) + return getFileResponse{File: f, Err: e}, nil + } +} + +type getFileRequest struct { + ID string `json:"id,omitempty"` +} + +type getFileResponse struct { + File ach.File `json:"file,omitempty"` + Err error `json:"err,omitempty"` +} + +func (r getFileResponse) error() error { return r.Err } + +func MakeDeleteFileEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(deleteFileRequest) + e := s.DeleteFile(req.ID) + return deleteFileResponse{Err: e}, nil + } +} + +type deleteFileRequest struct { + ID string `json:"id,omitempty"` +} + +type deleteFileResponse struct { + Err error `json:"err,omitempty"` +} + +func (r deleteFileResponse) error() error { return r.Err } diff --git a/server/middlewear.go b/server/middlewear.go new file mode 100644 index 000000000..2d2e6ac14 --- /dev/null +++ b/server/middlewear.go @@ -0,0 +1,53 @@ +package server + +import ( + "time" + + "github.com/go-kit/kit/log" + "github.com/moov-io/ach" +) + +// Middleware describes a service (as opposed to endpoint) middleware. +type Middleware func(Service) Service + +func LoggingMiddleware(logger log.Logger) Middleware { + return func(next Service) Service { + return &loggingMiddleware{ + next: next, + logger: logger, + } + } +} + +type loggingMiddleware struct { + next Service + logger log.Logger +} + +func (mw loggingMiddleware) CreateFile(f ach.File) (id string, err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "CreateFile", "id", f.ID, "took", time.Since(begin), "err", err) + }(time.Now()) + return mw.next.CreateFile(f) +} + +func (mw loggingMiddleware) GetFile(id string) (f ach.File, err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "GetFile", "id", id, "took", time.Since(begin), "err", err) + }(time.Now()) + return mw.next.GetFile(id) +} + +func (mw loggingMiddleware) GetFiles() []ach.File { + defer func(begin time.Time) { + mw.logger.Log("method", "GetFiles", "took", time.Since(begin)) + }(time.Now()) + return mw.next.GetFiles() +} + +func (mw loggingMiddleware) DeleteFile(id string) (err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "DeleteFile", "id", id, "took", time.Since(begin)) + }(time.Now()) + return mw.next.DeleteFile(id) +} diff --git a/server/service.go b/server/service.go index c316d0375..39ba9f026 100644 --- a/server/service.go +++ b/server/service.go @@ -14,13 +14,13 @@ var ( ) // Service is a REST interface for interacting with ACH file structures -// TODO: Add ctx to function paramaters to pass the client security token +// TODO: Add ctx to function parameters to pass the client security token type Service interface { // CreateFile creates a new ach file record and returns a resource ID CreateFile(f ach.File) (string, error) // AddFile retrieves a file based on the File id GetFile(id string) (ach.File, error) - // GetFiles retrieves all files accessable from the client. + // GetFiles retrieves all files accessible from the client. GetFiles() []ach.File // DeleteFile takes a file resource ID and deletes it from the repository DeleteFile(id string) error diff --git a/server/transport.go b/server/transport.go new file mode 100644 index 000000000..501ed88d8 --- /dev/null +++ b/server/transport.go @@ -0,0 +1,169 @@ +package server + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "io/ioutil" + + "net/http" + "net/url" + + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" + "github.com/gorilla/mux" + + "github.com/moov-io/ach" +) + +var ( + // ErrBadRouting is returned when an expected path variable is missing. + // It always indicates programmer error. + ErrBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)") +) + +func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { + r := mux.NewRouter() + e := MakeServerEndpoints(s) + options := []httptransport.ServerOption{ + httptransport.ServerErrorLogger(logger), + httptransport.ServerErrorEncoder(encodeError), + } + + // POST /files/ Creates a file + // GET /files/ retrieves a list of all file's + // GET /files/:id retrieves the given file by id + // DELETE /files/:id delete a file based on supplied id + // *** + // GET /files/:id/validate validates the supplied file id for nacha compliance + // PATCH /files/:id/build build batch and file controls in ach file with supplied values + // PATCH /files/upload/ upload a ach file + + r.Methods("POST").Path("/files/").Handler(httptransport.NewServer( + e.CreateFileEndpoint, + decodeCreateFileRequest, + encodeResponse, + options..., + )) + r.Methods("GET").Path("/files/").Handler(httptransport.NewServer( + e.GetFilesEndpoint, + decodeGetFilesRequest, + encodeResponse, + options..., + )) + r.Methods("GET").Path("/files/{id}").Handler(httptransport.NewServer( + e.GetFileEndpoint, + decodeGetFileRequest, + encodeResponse, + options..., + )) + r.Methods("DELETE").Path("/files/{id}").Handler(httptransport.NewServer( + e.DeleteFileEndpoint, + decodeDeleteFileRequest, + encodeResponse, + options..., + )) + return r +} + +func decodeCreateFileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + var req createFileRequest + // Sets default values + req.File = *ach.NewFile() + if e := json.NewDecoder(r.Body).Decode(&req.File); e != nil { + return nil, e + } + return req, nil +} + +func decodeGetFileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + vars := mux.Vars(r) + id, ok := vars["id"] + if !ok { + return nil, ErrBadRouting + } + return getFileRequest{ID: id}, nil +} + +func decodeDeleteFileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + vars := mux.Vars(r) + id, ok := vars["id"] + if !ok { + return nil, ErrBadRouting + } + return deleteFileRequest{ID: id}, nil +} + +func encodeCreateFileRequest(ctx context.Context, req *http.Request, request interface{}) error { + req.Method, req.URL.Path = "POST", "/files/" + return encodeRequest(ctx, req, request) +} +func decodeGetFilesRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + return getFilesRequest{}, nil +} + +func encodeGetFileRequest(ctx context.Context, req *http.Request, request interface{}) error { + r := request.(getFileRequest) + fileID := url.QueryEscape(r.ID) + req.Method, req.URL.Path = "GET", "/files/"+fileID + return encodeRequest(ctx, req, request) +} + +// errorer is implemented by all concrete response types that may contain +// errors. It allows us to change the HTTP response code without needing to +// trigger an endpoint (transport-level) error. For more information, read the +// big comment in endpoints.go. +type errorer interface { + error() error +} + +// encodeResponse is the common method to encode all response types to the +// client. I chose to do it this way because, since we're using JSON, there's no +// reason to provide anything more specific. It's certainly possible to +// specialize on a per-response (per-method) basis. +func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { + if e, ok := response.(errorer); ok && e.error() != nil { + // Not a Go kit transport error, but a business-logic error. + // Provide those as HTTP errors. + encodeError(ctx, e.error(), w) + return nil + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + return json.NewEncoder(w).Encode(response) +} + +// encodeRequest likewise JSON-encodes the request to the HTTP request body. +// Don't use it directly as a transport/http.Client EncodeRequestFunc: +// Service endpoints require mutating the HTTP method and request path. +func encodeRequest(_ context.Context, req *http.Request, request interface{}) error { + var buf bytes.Buffer + err := json.NewEncoder(&buf).Encode(request) + if err != nil { + return err + } + req.Body = ioutil.NopCloser(&buf) + return nil +} + +func encodeError(_ context.Context, err error, w http.ResponseWriter) { + if err == nil { + panic("encodeError with nil error") + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(codeFrom(err)) + json.NewEncoder(w).Encode(map[string]interface{}{ + "error": err.Error(), + }) +} + +func codeFrom(err error) int { + switch err { + case ErrNotFound: + return http.StatusNotFound + case ErrAlreadyExists: + return http.StatusBadRequest + default: + return http.StatusInternalServerError + } +} From bdb4e97ec3e66f45e492f0b5fcf16ab8e8425eae Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 31 May 2018 20:18:43 -0400 Subject: [PATCH 0136/1694] SEC Codes ARC, BOC, RCK #188, RCK #190, BOC #191, ARC --- BatchARC_test.go | 353 +++++++++++++++++ README.md | 3 + addenda99.go | 9 + batch.go | 14 +- batchARC.go | 95 +++++ batchBOC.go | 99 +++++ batchBOC_test.go | 351 +++++++++++++++++ batchRCK.go | 89 +++++ batchRCK_test.go | 382 +++++++++++++++++++ batch_test.go | 6 +- batcher.go | 4 + cmd/readACH/main_test.go | 4 +- cmd/readACH/old | 42 +- cmd/writeACH/main_test.go | 4 +- cmd/writeACH/old | 40 +- entryDetail.go | 28 +- {example => test}/ach-ppd-read/main.go | 0 {example => test}/ach-ppd-read/main_test.go | 0 {example => test}/ach-ppd-read/ppd-debit.ach | 0 {example => test}/ach-ppd-write/main.go | 0 {example => test}/ach-ppd-write/main_test.go | 0 test/ach-rck-read/main.go | 32 ++ test/ach-rck-read/main_test.go | 8 + test/ach-rck-read/rck-debit.ach | 10 + test/ach-rck-write/main.go | 75 ++++ test/ach-rck-write/main_test.go | 7 + 26 files changed, 1574 insertions(+), 81 deletions(-) create mode 100644 BatchARC_test.go create mode 100644 batchARC.go create mode 100644 batchBOC.go create mode 100644 batchBOC_test.go create mode 100644 batchRCK.go create mode 100644 batchRCK_test.go rename {example => test}/ach-ppd-read/main.go (100%) rename {example => test}/ach-ppd-read/main_test.go (100%) rename {example => test}/ach-ppd-read/ppd-debit.ach (100%) rename {example => test}/ach-ppd-write/main.go (100%) rename {example => test}/ach-ppd-write/main_test.go (100%) create mode 100644 test/ach-rck-read/main.go create mode 100644 test/ach-rck-read/main_test.go create mode 100644 test/ach-rck-read/rck-debit.ach create mode 100644 test/ach-rck-write/main.go create mode 100644 test/ach-rck-write/main_test.go diff --git a/BatchARC_test.go b/BatchARC_test.go new file mode 100644 index 000000000..5b4a9831d --- /dev/null +++ b/BatchARC_test.go @@ -0,0 +1,353 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +// mockBatchARCHeader creates a BatchARC BatchHeader +func mockBatchARCHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "ARC" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ARC" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockARCEntryDetail creates a BatchARC EntryDetail +func mockARCEntryDetail() *EntryDetail{ + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetCheckSerialNumber("123456789") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchARCHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchARC creates a BatchARC +func mockBatchARC() *BatchARC { + mockBatch := NewBatchARC(mockBatchARCHeader()) + mockBatch.AddEntry(mockARCEntryDetail()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// mockBatchARCHeaderCredit creates a BatchARC BatchHeader +func mockBatchARCHeaderCredit() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "ARC" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ARC" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockARCEntryDetailCredit creates a ARC EntryDetail with a credit entry +func mockARCEntryDetailCredit() *EntryDetail{ + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetCheckSerialNumber("123456789") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchARCHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchARCCredit creates a BatchARC with a Credit entry +func mockBatchARCCredit() *BatchARC { + mockBatch := NewBatchARC(mockBatchARCHeaderCredit()) + mockBatch.AddEntry(mockARCEntryDetailCredit()) + return mockBatch +} + +// testBatchARCHeader creates a BatchARC BatchHeader +func testBatchARCHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchARCHeader()) + err, ok := batch.(*BatchARC) + if !ok { + t.Errorf("Expecting BatchARC got %T", err) + } +} + +// TestBatchARCHeader tests validating BatchARC BatchHeader +func TestBatchARCHeader(t *testing.T) { + testBatchARCHeader(t) +} + +// BenchmarkBatchARCHeader benchmarks validating BatchARC BatchHeader +func BenchmarkBatchARCHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCHeader(b) + } +} + + +// testBatchARCCreate validates BatchARC create +func testBatchARCCreate(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchARCCreate tests validating BatchARC create +func TestBatchARCCreate(t *testing.T) { + testBatchARCCreate(t) +} + +// BenchmarkBatchARCCreate benchmarks validating BatchARC create +func BenchmarkBatchARCCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCCreate(b) + } +} + +// testBatchARCStandardEntryClassCode validates BatchARC create for an invalid StandardEntryClassCode +func testBatchARCStandardEntryClassCode (t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCStandardEntryClassCode tests validating BatchARC create for an invalid StandardEntryClassCode +func TestBatchARCStandardEntryClassCode(t *testing.T) { + testBatchARCStandardEntryClassCode(t) +} + +// BenchmarkBatchARCStandardEntryClassCode benchmarks validating BatchARC create for an invalid StandardEntryClassCode +func BenchmarkBatchARCStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCStandardEntryClassCode(b) + } +} + +// testBatchARCServiceClass200 validates BatchARC create for an invalid ServiceClassCode 200 +func testBatchARCServiceClass200(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.Header.ServiceClassCode = 200 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCServiceClass200 tests validating BatchARC create for an invalid ServiceClassCode 200 +func TestBatchARCServiceClass200(t *testing.T) { + testBatchARCServiceClass200(t) +} + +// BenchmarkBatchARCServiceClass200 benchmarks validating BatchARC create for an invalid ServiceClassCode 200 +func BenchmarkBatchARCServiceClass200(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCServiceClass200(b) + } +} + +// testBatchARCServiceClass220 validates BatchARC create for an invalid ServiceClassCode 220 +func testBatchARCServiceClass220(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.Header.ServiceClassCode = 220 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCServiceClass220 tests validating BatchARC create for an invalid ServiceClassCode 220 +func TestBatchARCServiceClass220(t *testing.T) { + testBatchARCServiceClass220(t) +} + +// BenchmarkBatchARCServiceClass220 benchmarks validating BatchARC create for an invalid ServiceClassCode 220 +func BenchmarkBatchARCServiceClass220(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCServiceClass220(b) + } +} + +// testBatchARCServiceClass280 validates BatchARC create for an invalid ServiceClassCode 280 +func testBatchARCServiceClass280(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.Header.ServiceClassCode = 280 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCServiceClass280 tests validating BatchARC create for an invalid ServiceClassCode 280 +func TestBatchARCServiceClass280(t *testing.T) { + testBatchARCServiceClass280(t) +} + +// BenchmarkBatchARCServiceClass280 benchmarks validating BatchARC create for an invalid ServiceClassCode 280 +func BenchmarkBatchARCServiceClass280(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCServiceClass280(b) + } +} + +// testBatchARCAmount validates BatchARC create for an invalid Amount +func testBatchARCAmount(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.Entries[0].Amount = 2600000 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Amount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCAmount validates BatchARC create for an invalid Amount +func TestBatchARCAmount(t *testing.T) { + testBatchARCAmount(t) +} + +// BenchmarkBatchARCAmount validates BatchARC create for an invalid Amount +func BenchmarkBatchARCAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCAmount(b) + } +} + +// testBatchARCCheckSerialNumber validates BatchARC CheckSerialNumber / IdentificationNumber is a mandatory field +func testBatchARCCheckSerialNumber(t testing.TB) { + mockBatch := mockBatchARC() + // modify CheckSerialNumber / IdentificationNumber to nothing + mockBatch.GetEntries()[0].SetCheckSerialNumber("") + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CheckSerialNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCCheckSerialNumber tests validating BatchARC +// CheckSerialNumber / IdentificationNumber is a mandatory field +func TestBatchARCCheckSerialNumber (t *testing.T) { + testBatchARCCheckSerialNumber(t) +} + +// BenchmarkBatchARCCheckSerialNumber benchmarks validating BatchARC +// CheckSerialNumber / IdentificationNumber is a mandatory field +func BenchmarkBatchARCCheckSerialNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCCheckSerialNumber(b) + } +} + +// testBatchARCTransactionCode validates BatchARC TransactionCode is not a credit +func testBatchARCTransactionCode(t testing.TB) { + mockBatch := mockBatchARCCredit() + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCTransactionCode tests validating BatchARC TransactionCode is not a credit +func TestBatchARCTransactionCode (t *testing.T) { + testBatchARCTransactionCode(t) +} + +// BenchmarkBatchARCTransactionCode benchmarks validating BatchARC TransactionCode is not a credit +func BenchmarkBatchARCTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCTransactionCode(b) + } +} + +// testBatchARCAddendaCount validates BatchARC Addenda count +func testBatchARCAddendaCount(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCAddendaCount tests validating BatchARC Addenda count +func TestBatchARCAddendaCount(t *testing.T) { + testBatchARCAddendaCount(t) +} + +// BenchmarkBatchARCAddendaCount benchmarks validating BatchARC Addenda count +func BenchmarkBatchARCAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCAddendaCount(b) + } +} \ No newline at end of file diff --git a/README.md b/README.md index 1df470fa1..b0a93294d 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ ACH is under active development but already in production for multiple companies * CCD (Corporate credit or debit) * TEL (Telephone-Initiated Entry) * COR (Automated Notification of Change(NOC)) + * RCK (Represented Check Entries) + * BOC (Back Office Conversion) + * ARC (Accounts Receivable Entry) * Return Entries diff --git a/addenda99.go b/addenda99.go index 87e4b402d..7c9083119 100644 --- a/addenda99.go +++ b/addenda99.go @@ -169,10 +169,12 @@ func makeReturnCodeDict() map[string]*returnCode { // Return Reason Codes for RDFIs {"R01", "Insufficient Funds", "Available balance is not sufficient to cover the dollar value of the debit entry"}, {"R02", "Account Closed", "Previously active account has been closed by customer or RDFI"}, + // R03 may not be used to return ARC, BOC or POP entries solely because they do not contain an Individual Name. {"R03", "No Account/Unable to Locate Account", "Account number structure is valid and passes editing process, but does not correspond to individual or is not an open account"}, {"R04", "Invalid Account Number", "Account number structure not valid; entry may fail check digit validation or may contain an incorrect number of digits."}, {"R05", "Improper Debit to Consumer Account", "A CCD, CTX, or CBR debit entry was transmitted to a Consumer Account of the Receiver and was not authorized by the Receiver"}, {"R06", "Returned per ODFI's Request", "ODFI has requested RDFI to return the ACH entry (optional to RDFI - ODFI indemnifies RDFI)}"}, + // R07 Prohibited use for ARC, BOC, POP and RCK. {"R07", "Authorization Revoked by Customer", "Consumer, who previously authorized ACH payment, has revoked authorization from Originator (must be returned no later than 60 days from settlement date and customer must sign affidavit)"}, {"R08", "Payment Stopped", "Receiver of a recurring debit transaction has stopped payment to a specific ACH debit. RDFI should verify the Receiver's intent when a request for stop payment is made to insure this is not intended to be a revocation of authorization"}, {"R09", "Uncollected Funds", "Sufficient book or ledger balance exists to satisfy dollar value of the transaction, but the dollar value of transaction is in process of collection (i.e., uncollected checks) or cash reserve balance below dollar value of the debit entry."}, @@ -202,6 +204,13 @@ func makeReturnCodeDict() map[string]*returnCode { {"R33", "Return of XCK entry", "RDFI determines at its sole discretion to return an XCK entry; an XCK return entry may be initiated by midnight of the sixtieth day following the settlement date if the XCK entry"}, {"R34", "Limited participation RDFI", "RDFI participation has been limited by a federal or state supervisor"}, {"R35", "Return of improper debit entry", "ACH debit not permitted for use with the CIE standard entry class code (except for reversals)"}, + {"R37", "Source Document Presented for Payment (Adjustment Entry)", "The source document to which an ARC, BOC or POP entry relateshas been presented for payment. RDFI must obtain a Written Statement and return the entry within 60 days following Settlement Date"}, + {"R38", "Stop Payment on Source Document (Adjustment Entry)", "A stop payment has been placed on the source document to which the ARC or BOC entry relates. RDFI must return no later than 60 days following Settlement Date. No Written Statement is required as the original stop payment form covers the return"}, + {"R39", "Improper Source Document", "The RDFI has determined the source document used for the ARC, BOC or POP entry to its Receiver’s account is improper."}, + {"R50", "State Law Affecting RCK Acceptance", "RDFI is located in a state that has not adopted Revised Article 4 of the UCC or the RDFI is located in a state that requires all canceled checks to be returned within the periodic statement"}, + {"R51", "Item Related to RCK Entry is Ineligible or RCK Entry is Improper", "The item to which the RCK entry relates was not eligible, Originator did not provide notice of the RCK policy, signature on the item was not genuine, the item has been altered or amount of the entry was not accurately obtained from the item. RDFI must obtain a Written Statement and return the entry within 60 days following Settlement Date"}, + {"R52", "Stop Payment on Item (Adjustment Entry)", "A stop payment has been placed on the item to which the RCK entry relates. RDFI must return no later than 60 days following Settlement Date. No Written Statement is required as the original stop payment form covers the return."}, + {"R53", "Item and RCK Entry Presented for Payment (Adjustment Entry)", "Both the RCK entry and check have been presented forpayment. RDFI must obtain a Written Statement and return the entry within 60 days following Settlement Date"}, // More return codes will be added when more SEC types are added to the library. } // populate the map diff --git a/batch.go b/batch.go index fdf23514d..3063dba6c 100644 --- a/batch.go +++ b/batch.go @@ -27,16 +27,22 @@ type batch struct { // NewBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported. func NewBatch(bh *BatchHeader) (Batcher, error) { switch bh.StandardEntryClassCode { - case "PPD": - return NewBatchPPD(bh), nil - case "WEB": - return NewBatchWEB(bh), nil + case "ARC": + return NewBatchARC(bh), nil + case "BOC": + return NewBatchBOC(bh), nil case "CCD": return NewBatchCCD(bh), nil case "COR": return NewBatchCOR(bh), nil + case "PPD": + return NewBatchPPD(bh), nil + case "RCK": + return NewBatchRCK(bh), nil case "TEL": return NewBatchTEL(bh), nil + case "WEB": + return NewBatchWEB(bh), nil default: } msg := fmt.Sprintf(msgFileNoneSEC, bh.StandardEntryClassCode) diff --git a/batchARC.go b/batchARC.go new file mode 100644 index 000000000..a45eb80c7 --- /dev/null +++ b/batchARC.go @@ -0,0 +1,95 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "fmt" + +// BatchARC holds the BatchHeader and BatchControl and all EntryDetail for ARC Entries. +// +// Accounts Receivable Entry (ARC). A consumer check converted to a one-time ACH debit. +// The Accounts Receivable (ARC) Entry provides billers the opportunity to initiate single-entry ACH +// debits to customer accounts by converting checks at the point of receipt through the U.S. mail, at +// a drop box location or in-person for payment of a bill at a manned location. The biller is required +// to provide the customer with notice prior to the acceptance of the check that states the receipt of +// the customer’s check will be deemed as the authorization for an ARC debit entry to the customer’s +// account. The provision of the notice and the receipt of the check together constitute authorization +// for the ARC entry. The customer’s check is solely be used as a source document to obtain the routing +// number, account number and check serial number. +// +// The difference between ARC and POP is that ARC can result from a check mailed in whereas POP is in-person. +type BatchARC struct { + batch +} + + +// NewBatchARC returns a *BatchARC +func NewBatchARC(bh *BatchHeader) *BatchARC { + batch := new(BatchARC) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchARC) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // Batch ARC cannot have an addenda record + if err := batch.isAddendaCount(0); err != nil { + return err + } + + // Add type specific validation. + if batch.Header.StandardEntryClassCode != "ARC" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "ARC") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + // RCK detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 220, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "ARC") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + } + + for _, entry := range batch.Entries { + // ARC detail entries must be a debit + if entry.CreditOrDebit() != "D" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + + // Amount must be 25,000 or less + if entry.Amount > 2500000 { + msg := fmt.Sprintf(msgBatchAmount,"25,000", "ARC") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} + } + + // CheckSerialNumber underlying IdentificationNumber, must be defined + if entry.IdentificationNumber == "" { + msg := fmt.Sprintf(msgBatchCheckSerialNumber, "ARC") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg} + } + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchARC) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + return batch.Validate() +} + + diff --git a/batchBOC.go b/batchBOC.go new file mode 100644 index 000000000..5d9e5bafe --- /dev/null +++ b/batchBOC.go @@ -0,0 +1,99 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "fmt" + +// BatchBOC holds the BatchHeader and BatchControl and all EntryDetail for BOC Entries. +// +// Back Office Conversion (BOC) A single entry debit initiated at the point of purchase +// or at a manned bill payment location to transfer funds through conversion to an +// ACH debit entry during back office processing. +// +// BOC allows retailers/billers, and ODFIs acting as Originators, +// to electronically convert checks received at the point-of-purchase as well as at a +// manned bill payment location into a single-entry ACH debit. The authorization to +// convert the check will be obtained through a notice at the checkout or manned bill +// payment location (e.g., loan payment at financial institution’s teller window) and the +// receipt of the Receiver’s check. The decision to process the check item as an ACH debit +// will be made in the “back office” instead of at the point-of-purchase. The customer’s +// check will solely be used as a source document to obtain the routing number, account +// number and check serial number. +// +// Unlike ARC entries, BOC conversions require the customer to be present and a notice that +// checks may be converted to BOC ACH entries be posted. +type BatchBOC struct { + batch +} + + +// NewBatchBOC returns a *BatchBOC +func NewBatchBOC(bh *BatchHeader) *BatchBOC { + batch := new(BatchBOC) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchBOC) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // Batch BOC cannot have an addenda record + if err := batch.isAddendaCount(0); err != nil { + return err + } + + // Add type specific validation. + if batch.Header.StandardEntryClassCode != "BOC" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "BOC") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + // BOC detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 220, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "RCK") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + } + + for _, entry := range batch.Entries { + // BOC detail entries must be a debit + if entry.CreditOrDebit() != "D" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + + // Amount must be 25,000 or less + if entry.Amount > 2500000 { + msg := fmt.Sprintf(msgBatchAmount,"25,000", "BOC") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} + } + + // CheckSerialNumber underlying IdentificationNumber, must be defined + if entry.IdentificationNumber == "" { + msg := fmt.Sprintf(msgBatchCheckSerialNumber, "BOC") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg} + } + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchBOC) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + return batch.Validate() +} + diff --git a/batchBOC_test.go b/batchBOC_test.go new file mode 100644 index 000000000..873e94a33 --- /dev/null +++ b/batchBOC_test.go @@ -0,0 +1,351 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +// mockBatchBOCHeader creates a BatchBOC BatchHeader +func mockBatchBOCHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "BOC" + bh.CompanyName = "Company Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "BOC" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockBOCEntryDetail creates a BatchBOC EntryDetail +func mockBOCEntryDetail() *EntryDetail{ + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetCheckSerialNumber("123456789") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchBOCHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchBOC creates a BatchBOC +func mockBatchBOC() *BatchBOC { + mockBatch := NewBatchBOC(mockBatchBOCHeader()) + mockBatch.AddEntry(mockBOCEntryDetail()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// mockBatchBOCHeaderCredit creates a BatchBOC BatchHeader +func mockBatchBOCHeaderCredit() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "BOC" + bh.CompanyName = "Company Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "REDEPCHECK" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockBOCEntryDetailCredit creates a BatchBOC EntryDetail with a credit +func mockBOCEntryDetailCredit() *EntryDetail{ + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetCheckSerialNumber("123456789") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchBOCHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchBOCCredit creates a BatchBOC with a Credit entry +func mockBatchBOCCredit() *BatchBOC { + mockBatch := NewBatchBOC(mockBatchBOCHeaderCredit()) + mockBatch.AddEntry(mockBOCEntryDetailCredit()) + return mockBatch +} + +// testBatchBOCHeader creates a BatchBOC BatchHeader +func testBatchBOCHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchBOCHeader()) + err, ok := batch.(*BatchBOC) + if !ok { + t.Errorf("Expecting BatchBOC got %T", err) + } +} + +// TestBatchBOCHeader tests validating BatchBOC BatchHeader +func TestBatchBOCHeader(t *testing.T) { + testBatchBOCHeader(t) +} + +// BenchmarkBatchBOCHeader benchmarks validating BatchBOC BatchHeader +func BenchmarkBatchBOCHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCHeader(b) + } +} + +// testBatchBOCCreate validates BatchBOC create +func testBatchBOCCreate(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchBOCCreate tests validating BatchBOC create +func TestBatchBOCCreate(t *testing.T) { + testBatchBOCCreate(t) +} + +// BenchmarkBatchBOCCreate benchmarks validating BatchBOC create +func BenchmarkBatchBOCCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCCreate(b) + } +} + +// testBatchBOCStandardEntryClassCode validates BatchBOC create for an invalid StandardEntryClassCode +func testBatchBOCStandardEntryClassCode (t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCStandardEntryClassCode tests validating BatchBOC create for an invalid StandardEntryClassCode +func TestBatchBOCStandardEntryClassCode(t *testing.T) { + testBatchBOCStandardEntryClassCode(t) +} + +// BenchmarkBatchBOCStandardEntryClassCode benchmarks validating BatchBOC create for an invalid StandardEntryClassCode +func BenchmarkBatchBOCStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCStandardEntryClassCode(b) + } +} + +// testBatchBOCServiceClass200 validates BatchBOC create for an invalid ServiceClassCode 200 +func testBatchBOCServiceClass200(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.Header.ServiceClassCode = 200 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCServiceClass200 tests validating BatchBOC create for an invalid ServiceClassCode 200 +func TestBatchBOCServiceClass200(t *testing.T) { + testBatchBOCServiceClass200(t) +} + +// BenchmarkBatchBOCServiceClass200 benchmarks validating BatchBOC create for an invalid ServiceClassCode 200 +func BenchmarkBatchBOCServiceClass200(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCServiceClass200(b) + } +} + +// testBatchBOCServiceClass220 validates BatchBOC create for an invalid ServiceClassCode 220 +func testBatchBOCServiceClass220(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.Header.ServiceClassCode = 220 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCServiceClass220 tests validating BatchBOC create for an invalid ServiceClassCode 220 +func TestBatchBOCServiceClass220(t *testing.T) { + testBatchBOCServiceClass220(t) +} + +// BenchmarkBatchBOCServiceClass220 benchmarks validating BatchBOC create for an invalid ServiceClassCode 220 +func BenchmarkBatchBOCServiceClass220(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCServiceClass220(b) + } +} + +// testBatchBOCServiceClass280 validates BatchBOC create for an invalid ServiceClassCode 280 +func testBatchBOCServiceClass280(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.Header.ServiceClassCode = 280 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCServiceClass280 tests validating BatchBOC create for an invalid ServiceClassCode 280 +func TestBatchBOCServiceClass280(t *testing.T) { + testBatchBOCServiceClass280(t) +} + +// BenchmarkBatchBOCServiceClass280 benchmarks validating BatchBOC create for an invalid ServiceClassCode 280 +func BenchmarkBatchBOCServiceClass280(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCServiceClass280(b) + } +} + +// testBatchBOCAmount validates BatchBOC create for an invalid Amount +func testBatchBOCAmount(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.Entries[0].Amount = 2500001 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Amount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCAmount validates BatchBOC create for an invalid Amount +func TestBatchBOCAmount(t *testing.T) { + testBatchBOCAmount(t) +} + +// BenchmarkBatchBOCAmount validates BatchBOC create for an invalid Amount +func BenchmarkBatchBOCAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCAmount(b) + } +} + +// testBatchBOCCheckSerialNumber validates BatchBOC CheckSerialNumber / IdentificationNumber is a mandatory field +func testBatchBOCCheckSerialNumber(t testing.TB) { + mockBatch := mockBatchBOC() + // modify CheckSerialNumber / IdentificationNumber to empty string + mockBatch.GetEntries()[0].SetCheckSerialNumber("") + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CheckSerialNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCCheckSerialNumber tests validating BatchBOC CheckSerialNumber / IdentificationNumber is a mandatory field +func TestBatchBOCCheckSerialNumber (t *testing.T) { + testBatchBOCCheckSerialNumber(t) +} + +// BenchmarkBatchBOCCheckSerialNumber benchmarks validating BatchBOC +// CheckSerialNumber / IdentificationNumber is a mandatory field +func BenchmarkBatchBOCCheckSerialNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCCheckSerialNumber(b) + } +} + +// testBatchBOCTransactionCode validates BatchBOC TransactionCode is not a credit +func testBatchBOCTransactionCode(t testing.TB) { + mockBatch := mockBatchBOCCredit() + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCTransactionCode tests validating BatchBOC TransactionCode is not a credit +func TestBatchBOCTransactionCode (t *testing.T) { + testBatchBOCTransactionCode(t) +} + +// BenchmarkBatchBOCTransactionCode benchmarks validating BatchBOC TransactionCode is not a credit +func BenchmarkBatchBOCTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCTransactionCode(b) + } +} + +// testBatchBOCAddendaCount validates BatchBOC Addenda count +func testBatchBOCAddendaCount(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCAddendaCount tests validating BatchBOC Addenda count +func TestBatchBOCAddendaCount(t *testing.T) { + testBatchBOCAddendaCount(t) +} + +// BenchmarkBatchBOCAddendaCount benchmarks validating BatchBOC Addenda count +func BenchmarkBatchBOCAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCAddendaCount(b) + } +} \ No newline at end of file diff --git a/batchRCK.go b/batchRCK.go new file mode 100644 index 000000000..ec1961dc3 --- /dev/null +++ b/batchRCK.go @@ -0,0 +1,89 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "fmt" + +// BatchRCK holds the BatchHeader and BatchControl and all EntryDetail for RCK Entries. +// +// Represented Check Entries (RCK). A physical check that was presented but returned because of +// insufficient funds may be represented as an ACH entry. +type BatchRCK struct { + batch +} + +// NewBatchRCK returns a *BatchRCK +func NewBatchRCK(bh *BatchHeader) *BatchRCK { + batch := new(BatchRCK) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchRCK) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + + // Batch RCK cannot have an addenda record + if err := batch.isAddendaCount(0); err != nil { + return err + } + + // Add type specific validation. + if batch.Header.StandardEntryClassCode != "RCK" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "RCK") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + // RCK detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 220, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "RCK") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + } + + // CompanyEntryDescription is required to be REDEPCHECK + if batch.Header.CompanyEntryDescription != "REDEPCHECK" { + msg := fmt.Sprintf(msgBatchCompanyEntryDescription, batch.Header.CompanyEntryDescription, "RCK") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CompanyEntryDescription", Msg: msg} + } + + for _, entry := range batch.Entries { + // RCK detail entries must be a debit + if entry.CreditOrDebit() != "D" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + + // // Amount must be 2,500 or less + if entry.Amount > 250000 { + msg := fmt.Sprintf(msgBatchAmount,"2,500", "RCK") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} + } + + // CheckSerialNumber underlying IdentificationNumber, must be defined + if entry.IdentificationNumber == "" { + msg := fmt.Sprintf(msgBatchCheckSerialNumber, "RCK") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg} + } + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchRCK) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + return batch.Validate() +} + diff --git a/batchRCK_test.go b/batchRCK_test.go new file mode 100644 index 000000000..b0817bc01 --- /dev/null +++ b/batchRCK_test.go @@ -0,0 +1,382 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +// mockBatchRCKHeader creates a BatchRCK BatchHeader +func mockBatchRCKHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "RCK" + bh.CompanyName = "Company Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "REDEPCHECK" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockRCKEntryDetail creates a BatchRCK ntryDetail +func mockRCKEntryDetail() *EntryDetail{ + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 2400 + entry.SetCheckSerialNumber("123456789") + entry.IndividualName = "Wade Arnold" + entry.SetTraceNumber(mockBatchRCKHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchRCK creates a BatchRCK +func mockBatchRCK() *BatchRCK { + mockBatch := NewBatchRCK(mockBatchRCKHeader()) + mockBatch.AddEntry(mockRCKEntryDetail()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// mockBatchRCKHeaderCredit creates a BatchRCK BatchHeader +func mockBatchRCKHeaderCredit() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "RCK" + bh.CompanyName = "Company Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "REDEPCHECK" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockRCKEntryDetailCredit creates a BatchRCK EntryDetail with a credit entry +func mockRCKEntryDetailCredit() *EntryDetail{ + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 2400 + entry.SetCheckSerialNumber("123456789") + entry.IndividualName = "Wade Arnold" + entry.SetTraceNumber(mockBatchRCKHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchRCKCredit creates a BatchRCK with a credit entry +func mockBatchRCKCredit() *BatchRCK { + mockBatch := NewBatchRCK(mockBatchRCKHeaderCredit()) + mockBatch.AddEntry(mockRCKEntryDetailCredit()) + return mockBatch +} + + +// testBatchRCKHeader creates a BatchRCK BatchHeader +func testBatchRCKHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchRCKHeader()) + err, ok := batch.(*BatchRCK) + if !ok { + t.Errorf("Expecting BatchRCK got %T", err) + } +} + +// TestBatchRCKHeader tests validating BatchRCK BatchHeader +func TestBatchRCKHeader(t *testing.T) { + testBatchRCKHeader(t) +} + +// BenchmarkBatchRCKHeader benchmarks validating BatchRCK BatchHeader +func BenchmarkBatchRCKHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKHeader(b) + } +} + +// testBatchRCKCreate validates BatchRCK create +func testBatchRCKCreate(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchRCKCreate tests validating BatchRCK create +func TestBatchRCKCreate(t *testing.T) { + testBatchRCKCreate(t) +} + +// BenchmarkBatchRCKCreate benchmarks validating BatchRCK create +func BenchmarkBatchRCKCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKCreate(b) + } +} + +// testBatchRCKStandardEntryClassCode validates BatchRCK create for an invalid StandardEntryClassCode +func testBatchRCKStandardEntryClassCode (t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKStandardEntryClassCode tests validating BatchRCK create for an invalid StandardEntryClassCode +func TestBatchRCKStandardEntryClassCode(t *testing.T) { + testBatchRCKStandardEntryClassCode(t) +} + +// BenchmarkBatchRCKStandardEntryClassCode benchmarks validating BatchRCK create for an invalid StandardEntryClassCode +func BenchmarkBatchRCKStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKStandardEntryClassCode(b) + } +} + +// testBatchRCKServiceClass200 validates BatchRCK create for an invalid ServiceClassCode 200 +func testBatchRCKServiceClass200(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Header.ServiceClassCode = 200 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKServiceClass200 tests validating BatchRCK create for an invalid ServiceClassCode 200 +func TestBatchRCKServiceClass200(t *testing.T) { + testBatchRCKServiceClass200(t) +} + +// BenchmarkBatchRCKServiceClass200 benchmarks validating BatchRCK create for an invalid ServiceClassCode 200 +func BenchmarkBatchRCKServiceClass200(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKServiceClass200(b) + } +} + +// testBatchRCKServiceClass220 validates BatchRCK create for an invalid ServiceClassCode 220 +func testBatchRCKServiceClass220(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Header.ServiceClassCode = 220 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKServiceClass220 tests validating BatchRCK create for an invalid ServiceClassCode 220 +func TestBatchRCKServiceClass220(t *testing.T) { + testBatchRCKServiceClass220(t) +} + +// BenchmarkBatchRCKServiceClass220 benchmarks validating BatchRCK create for an invalid ServiceClassCode 220 +func BenchmarkBatchRCKServiceClass220(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKServiceClass220(b) + } +} + +// testBatchRCKServiceClass280 validates BatchRCK create for an invalid ServiceClassCode 280 +func testBatchRCKServiceClass280(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Header.ServiceClassCode = 280 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKServiceClass280 tests validating BatchRCK create for an invalid ServiceClassCode 280 +func TestBatchRCKServiceClass280(t *testing.T) { + testBatchRCKServiceClass280(t) +} + +// BenchmarkBatchRCKServiceClass280 benchmarks validating BatchRCK create for an invalid ServiceClassCode 280 +func BenchmarkBatchRCKServiceClass280(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKServiceClass280(b) + } +} + +// testBatchRCKCompanyEntryDescription validates BatchRCK create for an invalid CompanyEntryDescription +func testBatchRCKCompanyEntryDescription(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Header.CompanyEntryDescription = "XYZ975" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CompanyEntryDescription" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKCompanyEntryDescription validates BatchRCK create for an invalid CompanyEntryDescription +func TestBatchRCKCompanyEntryDescription(t *testing.T) { + testBatchRCKCompanyEntryDescription(t) +} + +// BenchmarkBatchRCKCompanyEntryDescription validates BatchRCK create for an invalid CompanyEntryDescription +func BenchmarkBatchRCKCompanyEntryDescription(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKCompanyEntryDescription(b) + } +} + +// testBatchRCKAmount validates BatchRCK create for an invalid Amount +func testBatchRCKAmount(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Entries[0].Amount = 250001 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Amount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKAmount validates BatchRCK create for an invalid Amount +func TestBatchRCKAmount(t *testing.T) { + testBatchRCKAmount(t) +} + +// BenchmarkBatchRCKAmount validates BatchRCK create for an invalid Amount +func BenchmarkBatchRCKAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKAmount(b) + } +} + +// testBatchRCKCheckSerialNumber validates BatchRCK CheckSerialNumber / IdentificationNumber is a mandatory field +func testBatchRCKCheckSerialNumber(t testing.TB) { + mockBatch := mockBatchRCK() + // modify CheckSerialNumber / IdentificationNumber to empty string + mockBatch.GetEntries()[0].SetCheckSerialNumber("") + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CheckSerialNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKCheckSerialNumber tests validating BatchRCK +// CheckSerialNumber / IdentificationNumber is a mandatory field +func TestBatchRCKCheckSerialNumber (t *testing.T) { + testBatchRCKCheckSerialNumber(t) +} + +// BenchmarkBatchRCKCheckSerialNumber benchmarks validating BatchRCK +// CheckSerialNumber / IdentificationNumber is a mandatory field +func BenchmarkBatchRCKCheckSerialNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKCheckSerialNumber(b) + } +} + +// testBatchRCKTransactionCode validates BatchRCK TransactionCode is not a credit +func testBatchRCKTransactionCode(t testing.TB) { + mockBatch := mockBatchRCKCredit() + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKTransactionCode tests validating BatchRCK TransactionCode is not a credit +func TestBatchRCKTransactionCode (t *testing.T) { + testBatchRCKTransactionCode(t) +} + +// BenchmarkBatchRCKTransactionCode benchmarks validating BatchRCK TransactionCode is not a credit +func BenchmarkBatchRCKTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKTransactionCode(b) + } +} + +// testBatchRCKAddendaCount validates BatchRCK addenda count +func testBatchRCKAddendaCount(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKAddendaCount tests validating BatchRCK addenda count +func TestBatchRCKAddendaCount(t *testing.T) { + testBatchRCKAddendaCount(t) +} + +// BenchmarkBatchRCKAddendaCount benchmarks validating BatchRCK addenda count +func BenchmarkBatchRCKAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKAddendaCount(b) + } +} \ No newline at end of file diff --git a/batch_test.go b/batch_test.go index 18be510d6..47e9b14ce 100644 --- a/batch_test.go +++ b/batch_test.go @@ -20,7 +20,7 @@ func mockBatch() *batch { return mockBatch } -// Batch with mismatched Trace Number ODFI +// Batch with mismatched TraceNumber ODFI func mockBatchInvalidTraceNumberODFI() *batch { mockBatch := &batch{} mockBatch.SetHeader(mockBatchHeader()) @@ -28,7 +28,7 @@ func mockBatchInvalidTraceNumberODFI() *batch { return mockBatch } -// Entry Detail with mismatched Trace Number ODFI +// EntryDetail with mismatched TraceNumber ODFI func mockEntryDetailInvalidTraceNumberODFI() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 @@ -49,7 +49,7 @@ func mockBatchNoEntry() *batch { return mockBatch } -// Invalid SEC CODE Batch Header +// Invalid SEC CODE BatchHeader func mockBatchInvalidSECHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 diff --git a/batcher.go b/batcher.go index 992b41aa7..30f3c8e7f 100644 --- a/batcher.go +++ b/batcher.go @@ -47,6 +47,7 @@ var ( msgBatchCalculatedControlEquality = "calculated %v is out-of-balance with control %v" msgBatchAscending = "%v is less than last %v. Must be in ascending order" // specific messages for error + msgBatchCompanyEntryDescription = "Company entry description %v is not valid for batch type %v" msgBatchOriginatorDNE = "%v is not “2” for DNE with entry transaction code of 23 or 33" msgBatchTraceNumberNotODFI = "%v in header does not match entry trace number %v" msgBatchAddendaIndicator = "is 0 but found addenda record(s)" @@ -56,5 +57,8 @@ var ( msgBatchTransactionCodeCredit = "%v a credit is not allowed" msgBatchSECType = "header SEC type code %v for batch type %v" msgBatchTypeCode = "%v found in addenda and expecting %v for batch type %v" + msgBatchServiceClassCode = "Service Class Code %v is not valid for batch type %v" msgBatchForwardReturn = "Forward and Return entries found in the same batch" + msgBatchAmount = "Amount must be less than %v for SEC code %v" + msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" ) diff --git a/cmd/readACH/main_test.go b/cmd/readACH/main_test.go index 9e25eab8c..68bde6087 100644 --- a/cmd/readACH/main_test.go +++ b/cmd/readACH/main_test.go @@ -6,13 +6,13 @@ func TestFileRead(t *testing.T) { testFileRead(t) } -//BenchmarkTestFileCreate benchmarks creating an ACH File +/*//BenchmarkTestFileCreate benchmarks creating an ACH File func BenchmarkTestFileRead(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { testFileRead(b) } -} +}*/ // FileCreate creates an ACH File func testFileRead(t testing.TB) { diff --git a/cmd/readACH/old b/cmd/readACH/old index 1000c961b..3d38a255c 100644 --- a/cmd/readACH/old +++ b/cmd/readACH/old @@ -1,32 +1,10 @@ -total amount debit: 0 -total amount credit: 500000000 -C:\Users\Owner\AppData\Local\Temp\go-build543880490\b001\readACH.test.exe flag redefined: fPath ---- FAIL: TestFileRead (0.00s) -panic: C:\Users\Owner\AppData\Local\Temp\go-build543880490\b001\readACH.test.exe flag redefined: fPath [recovered] - panic: C:\Users\Owner\AppData\Local\Temp\go-build543880490\b001\readACH.test.exe flag redefined: fPath - -goroutine 20 [running]: -testing.tRunner.func1(0xc0420ac2d0) - C:/Go/src/testing/testing.go:742 +0x2a4 -panic(0x532f80, 0xc042369d70) - C:/Go/src/runtime/panic.go:505 +0x237 -flag.(*FlagSet).Var(0xc042092000, 0x587060, 0xc042369d30, 0x56988e, 0x5, 0x56a171, 0x9) - C:/Go/src/flag/flag.go:810 +0x547 -flag.(*FlagSet).StringVar(0xc042092000, 0xc042369d30, 0x56988e, 0x5, 0x56bb10, 0x10, 0x56a171, 0x9) - C:/Go/src/flag/flag.go:713 +0x92 -flag.(*FlagSet).String(0xc042092000, 0x56988e, 0x5, 0x56bb10, 0x10, 0x56a171, 0x9, 0xc0420b0000) - C:/Go/src/flag/flag.go:726 +0x92 -flag.String(0x56988e, 0x5, 0x56bb10, 0x10, 0x56a171, 0x9, 0x0) - C:/Go/src/flag/flag.go:733 +0x70 -github.com/bkmoovio/ach/cmd/readACH.main() - C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/readACH/main.go:15 +0x7c -github.com/bkmoovio/ach/cmd/readACH.FileRead(0x588100, 0xc0420ac2d0) - C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/readACH/main_test.go:20 +0x27 -github.com/bkmoovio/ach/cmd/readACH.TestFileRead(0xc0420ac2d0) - C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/readACH/main_test.go:6 +0x3e -testing.tRunner(0xc0420ac2d0, 0x575c00) - C:/Go/src/testing/testing.go:777 +0xd7 -created by testing.(*T).Run - C:/Go/src/testing/testing.go:824 +0x2e7 -exit status 2 -FAIL github.com/bkmoovio/ach/cmd/readACH 4.474s +goos: windows +goarch: amd64 +pkg: github.com/bkmoovio/ach/cmd/readACH +BenchmarkTestFileRead-8 50 33572202 ns/op 3102486 B/op 134694 allocs/op +BenchmarkTestFileRead-8 50 33500368 ns/op 3102480 B/op 134694 allocs/op +BenchmarkTestFileRead-8 50 33610982 ns/op 3102478 B/op 134694 allocs/op +BenchmarkTestFileRead-8 50 33380088 ns/op 3102479 B/op 134694 allocs/op +BenchmarkTestFileRead-8 50 32952968 ns/op 3102482 B/op 134694 allocs/op +PASS +ok github.com/bkmoovio/ach/cmd/readACH 11.783s diff --git a/cmd/writeACH/main_test.go b/cmd/writeACH/main_test.go index 3e6e34a52..edbcbfe3b 100644 --- a/cmd/writeACH/main_test.go +++ b/cmd/writeACH/main_test.go @@ -9,13 +9,13 @@ func TestFileWrite(t *testing.T) { testFileWrite(t) } -//BenchmarkTestFileCreate benchmarks creating an ACH File +/*//BenchmarkTestFileCreate benchmarks creating an ACH File func BenchmarkTestFileWrite(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { testFileWrite(b) } -} +}*/ // FileCreate creates an ACH File func testFileWrite(t testing.TB) { diff --git a/cmd/writeACH/old b/cmd/writeACH/old index 95eb3485d..822b80b06 100644 --- a/cmd/writeACH/old +++ b/cmd/writeACH/old @@ -1,30 +1,10 @@ -C:\Users\Owner\AppData\Local\Temp\go-build176990002\b001\writeACH.test.exe flag redefined: fPath ---- FAIL: TestFileCreate (0.00s) -panic: C:\Users\Owner\AppData\Local\Temp\go-build176990002\b001\writeACH.test.exe flag redefined: fPath [recovered] - panic: C:\Users\Owner\AppData\Local\Temp\go-build176990002\b001\writeACH.test.exe flag redefined: fPath - -goroutine 50 [running]: -testing.tRunner.func1(0xc0420fe2d0) - C:/Go/src/testing/testing.go:742 +0x2a4 -panic(0x556400, 0xc042226270) - C:/Go/src/runtime/panic.go:505 +0x237 -flag.(*FlagSet).Var(0xc042092000, 0x5b1a40, 0xc042226230, 0x591fa0, 0x5, 0x59292e, 0x9) - C:/Go/src/flag/flag.go:810 +0x547 -flag.(*FlagSet).StringVar(0xc042092000, 0xc042226230, 0x591fa0, 0x5, 0x0, 0x0, 0x59292e, 0x9) - C:/Go/src/flag/flag.go:713 +0x92 -flag.(*FlagSet).String(0xc042092000, 0x591fa0, 0x5, 0x0, 0x0, 0x59292e, 0x9, 0x0) - C:/Go/src/flag/flag.go:726 +0x92 -flag.String(0x591fa0, 0x5, 0x0, 0x0, 0x59292e, 0x9, 0x0) - C:/Go/src/flag/flag.go:733 +0x70 -github.com/bkmoovio/ach/cmd/writeACH.main() - C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/writeACH/main.go:18 +0x6f -github.com/bkmoovio/ach/cmd/writeACH.FileCreate(0x5b3200, 0xc0420fe2d0) - C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/writeACH/main_test.go:23 +0x27 -github.com/bkmoovio/ach/cmd/writeACH.TestFileCreate(0xc0420fe2d0) - C:/Users/Owner/go/src/github.com/bkmoovio/ach/cmd/writeACH/main_test.go:10 +0x3e -testing.tRunner(0xc0420fe2d0, 0x59ef18) - C:/Go/src/testing/testing.go:777 +0xd7 -created by testing.(*T).Run - C:/Go/src/testing/testing.go:824 +0x2e7 -exit status 2 -FAIL github.com/bkmoovio/ach/cmd/writeACH 0.579s +goos: windows +goarch: amd64 +pkg: github.com/bkmoovio/ach/cmd/writeACH +BenchmarkTestFileWrite-8 100 16101905 ns/op 2325178 B/op 119293 allocs/op +BenchmarkTestFileWrite-8 100 16306688 ns/op 2325160 B/op 119293 allocs/op +BenchmarkTestFileWrite-8 100 20868257 ns/op 2325161 B/op 119293 allocs/op +BenchmarkTestFileWrite-8 100 16199637 ns/op 2325160 B/op 119293 allocs/op +BenchmarkTestFileWrite-8 100 16090635 ns/op 2325160 B/op 119293 allocs/op +PASS +ok github.com/bkmoovio/ach/cmd/writeACH 10.120s diff --git a/entryDetail.go b/entryDetail.go index 01e50555e..e65b48e83 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. @@ -270,6 +270,18 @@ func (ed *EntryDetail) IdentificationNumberField() string { return ed.alphaField(ed.IdentificationNumber, 15) } +// CheckSerialNumberField is used in RCK, ARC, BOC files but returns +// a space padded string of the underlying IdentificationNumber field +func (ed *EntryDetail) CheckSerialNumberField() string { + return ed.alphaField(ed.IdentificationNumber, 15) +} + +// SetCheckSerialNumber setter for RCK, ARC, BOC CheckSerialNumber +// which is underlying IdentificationNumber +func (ed *EntryDetail) SetCheckSerialNumber(s string) { + ed.IdentificationNumber = s +} + // IndividualNameField returns a space padded string of IndividualName func (ed *EntryDetail) IndividualNameField() string { return ed.alphaField(ed.IndividualName, 22) @@ -280,7 +292,7 @@ func (ed *EntryDetail) ReceivingCompanyField() string { return ed.IndividualNameField() } -// SetReceivingCompany setter for CCD receiving company individual name +// SetReceivingCompany setter for CCD ReceivingCompany which is underlying IndividualName func (ed *EntryDetail) SetReceivingCompany(s string) { ed.IndividualName = s } @@ -290,7 +302,7 @@ func (ed *EntryDetail) DiscretionaryDataField() string { return ed.alphaField(ed.DiscretionaryData, 2) } -// PaymentTypeField returns the discretionary data field used in WEB batch files +// PaymentTypeField returns the DiscretionaryData field used in WEB batch files func (ed *EntryDetail) PaymentTypeField() string { // because DiscretionaryData can be changed outside of PaymentType we reset the value for safety ed.SetPaymentType(ed.DiscretionaryData) @@ -307,7 +319,7 @@ func (ed *EntryDetail) SetPaymentType(t string) { } } -// TraceNumberField returns a zero padded traceNumber string +// TraceNumberField returns a zero padded TraceNumber string func (ed *EntryDetail) TraceNumberField() string { return ed.numericField(ed.TraceNumber, 15) } @@ -315,12 +327,12 @@ func (ed *EntryDetail) TraceNumberField() string { // CreditOrDebit returns a "C" for credit or "D" for debit based on the entry TransactionCode func (ed *EntryDetail) CreditOrDebit() string { tc := strconv.Itoa(ed.TransactionCode) - // take the second number in the Transaction code + // take the second number in the TransactionCode switch tc[1:2] { - case "1", "2", "3": + case "1", "2", "3", "4": return "C" - case "6", "7", "8": + case "5","6", "7", "8", "9": return "D" } return "" -} +} \ No newline at end of file diff --git a/example/ach-ppd-read/main.go b/test/ach-ppd-read/main.go similarity index 100% rename from example/ach-ppd-read/main.go rename to test/ach-ppd-read/main.go diff --git a/example/ach-ppd-read/main_test.go b/test/ach-ppd-read/main_test.go similarity index 100% rename from example/ach-ppd-read/main_test.go rename to test/ach-ppd-read/main_test.go diff --git a/example/ach-ppd-read/ppd-debit.ach b/test/ach-ppd-read/ppd-debit.ach similarity index 100% rename from example/ach-ppd-read/ppd-debit.ach rename to test/ach-ppd-read/ppd-debit.ach diff --git a/example/ach-ppd-write/main.go b/test/ach-ppd-write/main.go similarity index 100% rename from example/ach-ppd-write/main.go rename to test/ach-ppd-write/main.go diff --git a/example/ach-ppd-write/main_test.go b/test/ach-ppd-write/main_test.go similarity index 100% rename from example/ach-ppd-write/main_test.go rename to test/ach-ppd-write/main_test.go diff --git a/test/ach-rck-read/main.go b/test/ach-rck-read/main.go new file mode 100644 index 000000000..df656275b --- /dev/null +++ b/test/ach-rck-read/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/moov-io/ach" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("rck-debit.ach") + if err != nil { + log.Panicf("Can not open file: %s: \n", err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formating is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("total amount debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) +} diff --git a/test/ach-rck-read/main_test.go b/test/ach-rck-read/main_test.go new file mode 100644 index 000000000..ceb689537 --- /dev/null +++ b/test/ach-rck-read/main_test.go @@ -0,0 +1,8 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} + diff --git a/test/ach-rck-read/rck-debit.ach b/test/ach-rck-read/rck-debit.ach new file mode 100644 index 000000000..56df3395a --- /dev/null +++ b/test/ach-rck-read/rck-debit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821805310000A094101Federal Reserve Bank My Bank Name +5225Name on Account 121042882 RCKREDEPCHECK 180601 0121042880000001 +62723138010412345678 0000002400123123123 Wade Arnold 0121042880000001 +82250000010023138010000000002400000000000000121042882 121042880000001 +9000001000001000000010023138010000000002400000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 diff --git a/test/ach-rck-write/main.go b/test/ach-rck-write/main.go new file mode 100644 index 000000000..7a3d08c9d --- /dev/null +++ b/test/ach-rck-write/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "log" + "os" + "time" + "github.com/moov-io/ach" +) + +func main() { + // Example transfer to write an ACH RCK file to send/credit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + // Set originator bank ODFI and destination Operator for the financial institution + // this is the funding/receiving source of the transfer + +/* f, err := os.Create( time.Now().UTC().Format("200601021504") + ".ach") + if err != nil { + fmt.Printf("%T: %s", err, err) + }*/ + + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Todays Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "RCK" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "REDEPCHECK" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 2400 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.IndividualName = "Wade Arnold" // Identifies the receiver of the transaction + entry.SetCheckSerialNumber("123123123") + + // build the batch + batch := ach.NewBatchRCK(bh) + batch.AddEntry(entry) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + //w := ach.NewWriter(f) + if err := w.WriteAll([]*ach.File{file}); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() + //f.Close() +} + diff --git a/test/ach-rck-write/main_test.go b/test/ach-rck-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-rck-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From 8e30bd83f1065c7a8516a8b6a10ea208c5740116 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 31 May 2018 20:26:58 -0400 Subject: [PATCH 0137/1694] Examples file --- {test => example}/ach-ppd-read/main.go | 0 {test => example}/ach-ppd-read/main_test.go | 0 {test => example}/ach-ppd-read/ppd-debit.ach | 0 {test => example}/ach-ppd-write/main.go | 0 {test => example}/ach-ppd-write/main_test.go | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {test => example}/ach-ppd-read/main.go (100%) rename {test => example}/ach-ppd-read/main_test.go (100%) rename {test => example}/ach-ppd-read/ppd-debit.ach (100%) rename {test => example}/ach-ppd-write/main.go (100%) rename {test => example}/ach-ppd-write/main_test.go (100%) diff --git a/test/ach-ppd-read/main.go b/example/ach-ppd-read/main.go similarity index 100% rename from test/ach-ppd-read/main.go rename to example/ach-ppd-read/main.go diff --git a/test/ach-ppd-read/main_test.go b/example/ach-ppd-read/main_test.go similarity index 100% rename from test/ach-ppd-read/main_test.go rename to example/ach-ppd-read/main_test.go diff --git a/test/ach-ppd-read/ppd-debit.ach b/example/ach-ppd-read/ppd-debit.ach similarity index 100% rename from test/ach-ppd-read/ppd-debit.ach rename to example/ach-ppd-read/ppd-debit.ach diff --git a/test/ach-ppd-write/main.go b/example/ach-ppd-write/main.go similarity index 100% rename from test/ach-ppd-write/main.go rename to example/ach-ppd-write/main.go diff --git a/test/ach-ppd-write/main_test.go b/example/ach-ppd-write/main_test.go similarity index 100% rename from test/ach-ppd-write/main_test.go rename to example/ach-ppd-write/main_test.go From fd5046405eb4c9d3f499a0e8910f560a6027d158 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 31 May 2018 20:38:01 -0400 Subject: [PATCH 0138/1694] Go fmt and Go vet Go fmt and Go vet --- BatchARC_test.go | 13 ++++++------- batchARC.go | 7 ++----- batchBOC.go | 6 ++---- batchBOC_test.go | 12 ++++++------ batchRCK.go | 7 +++---- batchRCK_test.go | 13 ++++++------- batcher.go | 26 +++++++++++++------------- test/ach-rck-read/main_test.go | 1 - test/ach-rck-write/main.go | 15 +++++++-------- 9 files changed, 45 insertions(+), 55 deletions(-) diff --git a/BatchARC_test.go b/BatchARC_test.go index 5b4a9831d..d1b372ed3 100644 --- a/BatchARC_test.go +++ b/BatchARC_test.go @@ -19,7 +19,7 @@ func mockBatchARCHeader() *BatchHeader { } // mockARCEntryDetail creates a BatchARC EntryDetail -func mockARCEntryDetail() *EntryDetail{ +func mockARCEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 entry.SetRDFI("231380104") @@ -55,7 +55,7 @@ func mockBatchARCHeaderCredit() *BatchHeader { } // mockARCEntryDetailCredit creates a ARC EntryDetail with a credit entry -func mockARCEntryDetailCredit() *EntryDetail{ +func mockARCEntryDetailCredit() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 entry.SetRDFI("231380104") @@ -97,7 +97,6 @@ func BenchmarkBatchARCHeader(b *testing.B) { } } - // testBatchARCCreate validates BatchARC create func testBatchARCCreate(t testing.TB) { mockBatch := mockBatchARC() @@ -121,7 +120,7 @@ func BenchmarkBatchARCCreate(b *testing.B) { } // testBatchARCStandardEntryClassCode validates BatchARC create for an invalid StandardEntryClassCode -func testBatchARCStandardEntryClassCode (t testing.TB) { +func testBatchARCStandardEntryClassCode(t testing.TB) { mockBatch := mockBatchARC() mockBatch.Header.StandardEntryClassCode = "WEB" mockBatch.Create() @@ -283,7 +282,7 @@ func testBatchARCCheckSerialNumber(t testing.TB) { // TestBatchARCCheckSerialNumber tests validating BatchARC // CheckSerialNumber / IdentificationNumber is a mandatory field -func TestBatchARCCheckSerialNumber (t *testing.T) { +func TestBatchARCCheckSerialNumber(t *testing.T) { testBatchARCCheckSerialNumber(t) } @@ -311,7 +310,7 @@ func testBatchARCTransactionCode(t testing.TB) { } // TestBatchARCTransactionCode tests validating BatchARC TransactionCode is not a credit -func TestBatchARCTransactionCode (t *testing.T) { +func TestBatchARCTransactionCode(t *testing.T) { testBatchARCTransactionCode(t) } @@ -350,4 +349,4 @@ func BenchmarkBatchARCAddendaCount(b *testing.B) { for i := 0; i < b.N; i++ { testBatchARCAddendaCount(b) } -} \ No newline at end of file +} diff --git a/batchARC.go b/batchARC.go index a45eb80c7..1836b2f1d 100644 --- a/batchARC.go +++ b/batchARC.go @@ -23,7 +23,6 @@ type BatchARC struct { batch } - // NewBatchARC returns a *BatchARC func NewBatchARC(bh *BatchHeader) *BatchARC { batch := new(BatchARC) @@ -67,13 +66,13 @@ func (batch *BatchARC) Validate() error { // Amount must be 25,000 or less if entry.Amount > 2500000 { - msg := fmt.Sprintf(msgBatchAmount,"25,000", "ARC") + msg := fmt.Sprintf(msgBatchAmount, "25,000", "ARC") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} } // CheckSerialNumber underlying IdentificationNumber, must be defined if entry.IdentificationNumber == "" { - msg := fmt.Sprintf(msgBatchCheckSerialNumber, "ARC") + msg := fmt.Sprintf(msgBatchCheckSerialNumber, "ARC") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg} } } @@ -91,5 +90,3 @@ func (batch *BatchARC) Create() error { return batch.Validate() } - - diff --git a/batchBOC.go b/batchBOC.go index 5d9e5bafe..e39941521 100644 --- a/batchBOC.go +++ b/batchBOC.go @@ -28,7 +28,6 @@ type BatchBOC struct { batch } - // NewBatchBOC returns a *BatchBOC func NewBatchBOC(bh *BatchHeader) *BatchBOC { batch := new(BatchBOC) @@ -72,13 +71,13 @@ func (batch *BatchBOC) Validate() error { // Amount must be 25,000 or less if entry.Amount > 2500000 { - msg := fmt.Sprintf(msgBatchAmount,"25,000", "BOC") + msg := fmt.Sprintf(msgBatchAmount, "25,000", "BOC") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} } // CheckSerialNumber underlying IdentificationNumber, must be defined if entry.IdentificationNumber == "" { - msg := fmt.Sprintf(msgBatchCheckSerialNumber, "BOC") + msg := fmt.Sprintf(msgBatchCheckSerialNumber, "BOC") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg} } } @@ -96,4 +95,3 @@ func (batch *BatchBOC) Create() error { return batch.Validate() } - diff --git a/batchBOC_test.go b/batchBOC_test.go index 873e94a33..2fc05a3ee 100644 --- a/batchBOC_test.go +++ b/batchBOC_test.go @@ -19,7 +19,7 @@ func mockBatchBOCHeader() *BatchHeader { } // mockBOCEntryDetail creates a BatchBOC EntryDetail -func mockBOCEntryDetail() *EntryDetail{ +func mockBOCEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 entry.SetRDFI("231380104") @@ -55,7 +55,7 @@ func mockBatchBOCHeaderCredit() *BatchHeader { } // mockBOCEntryDetailCredit creates a BatchBOC EntryDetail with a credit -func mockBOCEntryDetailCredit() *EntryDetail{ +func mockBOCEntryDetailCredit() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 entry.SetRDFI("231380104") @@ -120,7 +120,7 @@ func BenchmarkBatchBOCCreate(b *testing.B) { } // testBatchBOCStandardEntryClassCode validates BatchBOC create for an invalid StandardEntryClassCode -func testBatchBOCStandardEntryClassCode (t testing.TB) { +func testBatchBOCStandardEntryClassCode(t testing.TB) { mockBatch := mockBatchBOC() mockBatch.Header.StandardEntryClassCode = "WEB" mockBatch.Create() @@ -281,7 +281,7 @@ func testBatchBOCCheckSerialNumber(t testing.TB) { } // TestBatchBOCCheckSerialNumber tests validating BatchBOC CheckSerialNumber / IdentificationNumber is a mandatory field -func TestBatchBOCCheckSerialNumber (t *testing.T) { +func TestBatchBOCCheckSerialNumber(t *testing.T) { testBatchBOCCheckSerialNumber(t) } @@ -309,7 +309,7 @@ func testBatchBOCTransactionCode(t testing.TB) { } // TestBatchBOCTransactionCode tests validating BatchBOC TransactionCode is not a credit -func TestBatchBOCTransactionCode (t *testing.T) { +func TestBatchBOCTransactionCode(t *testing.T) { testBatchBOCTransactionCode(t) } @@ -348,4 +348,4 @@ func BenchmarkBatchBOCAddendaCount(b *testing.B) { for i := 0; i < b.N; i++ { testBatchBOCAddendaCount(b) } -} \ No newline at end of file +} diff --git a/batchRCK.go b/batchRCK.go index ec1961dc3..42642db53 100644 --- a/batchRCK.go +++ b/batchRCK.go @@ -47,7 +47,7 @@ func (batch *BatchRCK) Validate() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} } - // CompanyEntryDescription is required to be REDEPCHECK + // CompanyEntryDescription is required to be REDEPCHECK if batch.Header.CompanyEntryDescription != "REDEPCHECK" { msg := fmt.Sprintf(msgBatchCompanyEntryDescription, batch.Header.CompanyEntryDescription, "RCK") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CompanyEntryDescription", Msg: msg} @@ -62,13 +62,13 @@ func (batch *BatchRCK) Validate() error { // // Amount must be 2,500 or less if entry.Amount > 250000 { - msg := fmt.Sprintf(msgBatchAmount,"2,500", "RCK") + msg := fmt.Sprintf(msgBatchAmount, "2,500", "RCK") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} } // CheckSerialNumber underlying IdentificationNumber, must be defined if entry.IdentificationNumber == "" { - msg := fmt.Sprintf(msgBatchCheckSerialNumber, "RCK") + msg := fmt.Sprintf(msgBatchCheckSerialNumber, "RCK") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg} } } @@ -86,4 +86,3 @@ func (batch *BatchRCK) Create() error { return batch.Validate() } - diff --git a/batchRCK_test.go b/batchRCK_test.go index b0817bc01..b89db3bfe 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -19,7 +19,7 @@ func mockBatchRCKHeader() *BatchHeader { } // mockRCKEntryDetail creates a BatchRCK ntryDetail -func mockRCKEntryDetail() *EntryDetail{ +func mockRCKEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 entry.SetRDFI("231380104") @@ -55,7 +55,7 @@ func mockBatchRCKHeaderCredit() *BatchHeader { } // mockRCKEntryDetailCredit creates a BatchRCK EntryDetail with a credit entry -func mockRCKEntryDetailCredit() *EntryDetail{ +func mockRCKEntryDetailCredit() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 22 entry.SetRDFI("231380104") @@ -75,7 +75,6 @@ func mockBatchRCKCredit() *BatchRCK { return mockBatch } - // testBatchRCKHeader creates a BatchRCK BatchHeader func testBatchRCKHeader(t testing.TB) { batch, _ := NewBatch(mockBatchRCKHeader()) @@ -121,7 +120,7 @@ func BenchmarkBatchRCKCreate(b *testing.B) { } // testBatchRCKStandardEntryClassCode validates BatchRCK create for an invalid StandardEntryClassCode -func testBatchRCKStandardEntryClassCode (t testing.TB) { +func testBatchRCKStandardEntryClassCode(t testing.TB) { mockBatch := mockBatchRCK() mockBatch.Header.StandardEntryClassCode = "WEB" mockBatch.Create() @@ -312,7 +311,7 @@ func testBatchRCKCheckSerialNumber(t testing.TB) { // TestBatchRCKCheckSerialNumber tests validating BatchRCK // CheckSerialNumber / IdentificationNumber is a mandatory field -func TestBatchRCKCheckSerialNumber (t *testing.T) { +func TestBatchRCKCheckSerialNumber(t *testing.T) { testBatchRCKCheckSerialNumber(t) } @@ -340,7 +339,7 @@ func testBatchRCKTransactionCode(t testing.TB) { } // TestBatchRCKTransactionCode tests validating BatchRCK TransactionCode is not a credit -func TestBatchRCKTransactionCode (t *testing.T) { +func TestBatchRCKTransactionCode(t *testing.T) { testBatchRCKTransactionCode(t) } @@ -379,4 +378,4 @@ func BenchmarkBatchRCKAddendaCount(b *testing.B) { for i := 0; i < b.N; i++ { testBatchRCKAddendaCount(b) } -} \ No newline at end of file +} diff --git a/batcher.go b/batcher.go index 30f3c8e7f..ed5779c8b 100644 --- a/batcher.go +++ b/batcher.go @@ -48,17 +48,17 @@ var ( msgBatchAscending = "%v is less than last %v. Must be in ascending order" // specific messages for error msgBatchCompanyEntryDescription = "Company entry description %v is not valid for batch type %v" - msgBatchOriginatorDNE = "%v is not “2” for DNE with entry transaction code of 23 or 33" - msgBatchTraceNumberNotODFI = "%v in header does not match entry trace number %v" - msgBatchAddendaIndicator = "is 0 but found addenda record(s)" - msgBatchAddendaTraceNumber = "%v does not match proceeding entry detail trace number %v" - msgBatchEntries = "must have Entry Record(s) to be built" - msgBatchAddendaCount = "%v addendum found where %v is allowed for batch type %v" - msgBatchTransactionCodeCredit = "%v a credit is not allowed" - msgBatchSECType = "header SEC type code %v for batch type %v" - msgBatchTypeCode = "%v found in addenda and expecting %v for batch type %v" - msgBatchServiceClassCode = "Service Class Code %v is not valid for batch type %v" - msgBatchForwardReturn = "Forward and Return entries found in the same batch" - msgBatchAmount = "Amount must be less than %v for SEC code %v" - msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" + msgBatchOriginatorDNE = "%v is not “2” for DNE with entry transaction code of 23 or 33" + msgBatchTraceNumberNotODFI = "%v in header does not match entry trace number %v" + msgBatchAddendaIndicator = "is 0 but found addenda record(s)" + msgBatchAddendaTraceNumber = "%v does not match proceeding entry detail trace number %v" + msgBatchEntries = "must have Entry Record(s) to be built" + msgBatchAddendaCount = "%v addendum found where %v is allowed for batch type %v" + msgBatchTransactionCodeCredit = "%v a credit is not allowed" + msgBatchSECType = "header SEC type code %v for batch type %v" + msgBatchTypeCode = "%v found in addenda and expecting %v for batch type %v" + msgBatchServiceClassCode = "Service Class Code %v is not valid for batch type %v" + msgBatchForwardReturn = "Forward and Return entries found in the same batch" + msgBatchAmount = "Amount must be less than %v for SEC code %v" + msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" ) diff --git a/test/ach-rck-read/main_test.go b/test/ach-rck-read/main_test.go index ceb689537..45fcc779c 100644 --- a/test/ach-rck-read/main_test.go +++ b/test/ach-rck-read/main_test.go @@ -5,4 +5,3 @@ import "testing" func Test(t *testing.T) { main() } - diff --git a/test/ach-rck-write/main.go b/test/ach-rck-write/main.go index 7a3d08c9d..87f45129a 100644 --- a/test/ach-rck-write/main.go +++ b/test/ach-rck-write/main.go @@ -1,10 +1,10 @@ package main import ( + "github.com/moov-io/ach" "log" "os" "time" - "github.com/moov-io/ach" ) func main() { @@ -14,10 +14,10 @@ func main() { // Set originator bank ODFI and destination Operator for the financial institution // this is the funding/receiving source of the transfer -/* f, err := os.Create( time.Now().UTC().Format("200601021504") + ".ach") - if err != nil { - fmt.Printf("%T: %s", err, err) - }*/ + /* f, err := os.Create( time.Now().UTC().Format("200601021504") + ".ach") + if err != nil { + fmt.Printf("%T: %s", err, err) + }*/ fh := ach.NewFileHeader() fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent @@ -40,10 +40,10 @@ func main() { // can be multiple entry's per batch entry := ach.NewEntryDetail() // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) - entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account + entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account entry.SetRDFI("231380104") // Receivers bank transit routing number entry.DFIAccountNumber = "12345678" // Receivers bank account number - entry.Amount = 2400 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.Amount = 2400 // Amount of transaction with no decimal. One dollar and eleven cents = 111 entry.SetTraceNumber(bh.ODFIIdentification, 1) entry.IndividualName = "Wade Arnold" // Identifies the receiver of the transaction entry.SetCheckSerialNumber("123123123") @@ -72,4 +72,3 @@ func main() { w.Flush() //f.Close() } - From 8029164ac6d5782cf5a7b99ad18bba4b62b2c687 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 1 Jun 2018 09:19:48 -0400 Subject: [PATCH 0139/1694] Formatting, Spelling Formatting, Spelling --- batch.go | 10 +++++----- batchRCK_test.go | 2 +- batcher.go | 2 +- test/ach-rck-read/main.go | 2 +- test/ach-rck-write/main.go | 14 ++------------ 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/batch.go b/batch.go index 3063dba6c..c78d23ddc 100644 --- a/batch.go +++ b/batch.go @@ -71,7 +71,7 @@ func (batch *batch) verify() error { msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.CompanyIdentification, batch.Control.CompanyIdentification) return &BatchError{BatchNumber: batchNumber, FieldName: "CompanyIdentification", Msg: msg} } - // Control ODFI Identification must be the same as batch header + // Control ODFIIdentification must be the same as batch header if batch.Header.ODFIIdentification != batch.Control.ODFIIdentification { msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) return &BatchError{BatchNumber: batchNumber, FieldName: "ODFIIdentification", Msg: msg} @@ -316,7 +316,7 @@ func (batch *batch) isOriginatorDNE() error { } // isTraceNumberODFI checks if the first 8 positions of the entry detail trace number -// match the batch header odfi +// match the batch header ODFI func (batch *batch) isTraceNumberODFI() error { for _, entry := range batch.Entries { if batch.Header.ODFIIdentificationField() != entry.TraceNumberField()[:8] { @@ -337,7 +337,7 @@ func (batch *batch) isAddendaSequence() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} } lastSeq := -1 - // check if sequence is assending + // check if sequence is ascending for _, addenda := range entry.Addendum { // sequences don't exist in NOC or Return addenda if a, ok := addenda.(*Addenda05); ok { @@ -359,7 +359,7 @@ func (batch *batch) isAddendaSequence() error { return nil } -// isAddendaCount iterates through each entry detail and checks the number of addendum is greater than the count paramater otherwise it returns an error. +// isAddendaCount iterates through each entry detail and checks the number of addendum is greater than the count parameter otherwise it returns an error. // Following SEC codes allow for none or one Addendum // "PPD", "WEB", "CCD", "CIE", "DNE", "MTE", "POS", "SHR" func (batch *batch) isAddendaCount(count int) error { @@ -373,7 +373,7 @@ func (batch *batch) isAddendaCount(count int) error { return nil } -// isTypeCode takes a typecode string and verifies addenda records match +// isTypeCode takes a TypeCode string and verifies Addenda records match func (batch *batch) isTypeCode(typeCode string) error { for _, entry := range batch.Entries { for _, addenda := range entry.Addendum { diff --git a/batchRCK_test.go b/batchRCK_test.go index b89db3bfe..47296555c 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -18,7 +18,7 @@ func mockBatchRCKHeader() *BatchHeader { return bh } -// mockRCKEntryDetail creates a BatchRCK ntryDetail +// mockRCKEntryDetail creates a BatchRCK EntryDetail func mockRCKEntryDetail() *EntryDetail { entry := NewEntryDetail() entry.TransactionCode = 27 diff --git a/batcher.go b/batcher.go index ed5779c8b..9203b6b53 100644 --- a/batcher.go +++ b/batcher.go @@ -14,7 +14,7 @@ import ( // * The SEC Code pertains to all items within batch // * Determines format of the entry detail records // * Determines addenda records (required or optional PLUS one or up to 9,999 records) -// * Determines rules to follow (return timeframes) +// * Determines rules to follow (return time frames) // * Some SEC codes require specific data in predetermined fields within the ACH record type Batcher interface { GetHeader() *BatchHeader diff --git a/test/ach-rck-read/main.go b/test/ach-rck-read/main.go index df656275b..ad35f13c7 100644 --- a/test/ach-rck-read/main.go +++ b/test/ach-rck-read/main.go @@ -23,7 +23,7 @@ func main() { if achFile.Validate(); err != nil { fmt.Printf("Could not validate entire read file: %v", err) } - // If you trust the file but it's formating is off building will probably resolve the malformed file. + // If you trust the file but it's formatting is off building will probably resolve the malformed file. if achFile.Create(); err != nil { fmt.Printf("Could not build file with read properties: %v", err) } diff --git a/test/ach-rck-write/main.go b/test/ach-rck-write/main.go index 87f45129a..fa81c70f6 100644 --- a/test/ach-rck-write/main.go +++ b/test/ach-rck-write/main.go @@ -8,21 +8,13 @@ import ( ) func main() { - // Example transfer to write an ACH RCK file to send/credit a external institutions account + // Example transfer to write an ACH RCK file to debit a external institutions account // Important: All financial institutions are different and will require registration and exact field values. - // Set originator bank ODFI and destination Operator for the financial institution - // this is the funding/receiving source of the transfer - - /* f, err := os.Create( time.Now().UTC().Format("200601021504") + ".ach") - if err != nil { - fmt.Printf("%T: %s", err, err) - }*/ - fh := ach.NewFileHeader() fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file - fh.FileCreationDate = time.Now() // Todays Date + fh.FileCreationDate = time.Now() // Today's Date fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name" @@ -65,10 +57,8 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - //w := ach.NewWriter(f) if err := w.WriteAll([]*ach.File{file}); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() - //f.Close() } From 233e1180e12e7d7149061122c9d7e8da805bb31d Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 3 Jun 2018 15:31:08 -0600 Subject: [PATCH 0140/1694] Resolve build issues --- .travis.yml | 7 ++++--- entryDetail.go | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e08c5e4e..13383adf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: go sudo: false go: -- tip +- 1.9.x +- 1.10.x env: global: secure: QPkcX77j8QEqTwOYyLGItqvxYwE6Na5WaSZWjmhp48OlxYatWRHxJBwcFYSn1OWD5FMn+3oW39fHknReIxtrnhXMaNvI7x3/0gy4zujD/xZ2xAg7NsQ+l5buvEFO8/LEwwo0fp4knItFcBv8xH/ziJBJyXvgfMtj7Is4Q/pB1p6pWDdVy1vtAj3zH02bcqh1yXXS3HvcD8UhTszfU017gVNXDN1ow0rp1L3ainr3btrVK9izUxZfKvb7PlWJO1ogah7xNr/dIOJLsx2SfKgzKp+3H28L2WegtbzON74Op4jXvRywCwqjmUt/nwJ/Y9anunMNHT136h+ye4ziG1i/VdbWq0Q4PopQ8yYqinujG7SjfQio+wNCV2cwc2r/WjNBjbH0N9/Pflogq3RHvgy/9VtPif1tY+RrZCSntohoEZbYpVcFQFE1xDyf6xq/uLxVeEcCU33gqq7cKEfpcUgyCITa+yCPfBdtgkLBJ8h7Sew1j08D1kTKUW6g3D1epmwlCh/Z16oHG5VwSnCLGDjJy8wm/hQk1i/g7qeP7g24CfNzffzlFBCy88HhjzmrhUpcaTyfVVDf4h8wK6Zu/J3dHjHXQYwfiQRqpMa+2DYyjGgZhniccuh4GWolGZauDQdmO9SD4Ugyt9PEMk02i32ax3A4XE/Q6VNOam+qszviX3Q= @@ -12,13 +13,13 @@ before_install: - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo before_script: -- GO_FILES=$(find . -iname '*.go' -type f | grep -v /example/ | grep -v /cmd/) +- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/ | grep -v /example/) script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES - megacheck $GO_FILES - misspell -error -locale US . - gocyclo -over 19 $GO_FILES -- golint -set_exit_status $(go list $GO_FILES) +- golint -set_exit_status $GO_FILES after_success: - goveralls -repotoken $COVERALLS_TOKEN diff --git a/entryDetail.go b/entryDetail.go index e65b48e83..abb3cffe8 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -331,8 +331,8 @@ func (ed *EntryDetail) CreditOrDebit() string { switch tc[1:2] { case "1", "2", "3", "4": return "C" - case "5","6", "7", "8", "9": + case "5", "6", "7", "8", "9": return "D" } return "" -} \ No newline at end of file +} From b6c3854e4476cde34beb5f043de9ee6c75a498b4 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 3 Jun 2018 16:50:32 -0600 Subject: [PATCH 0141/1694] Move README contributing info to CONTRIBUTING --- CONTRIBUTING.md | 48 +++++++++++++++- README.md | 149 +----------------------------------------------- 2 files changed, 50 insertions(+), 147 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1e168904e..f2110a230 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -123,12 +123,57 @@ case "MTE": Pull request require a batchMTE_test.go file that covers the logic of the type. -## References +## Benchmarks and Profiling + +**Write:** + +github.com/moov-io/ach/cmd/writeACH + +main.go will create an ACH file with 4 batches each containing 1250 detail and addenda records + +A custom path can be used by defining fPath ( e.g. -fPath=github.com/moov-io/_directory_ ) + +Benchmark + + +```bash +github.com/moov-io/ach/cmd/writeACH>go test -bench=BenchmarkTestFileWrite -count=5 > old +``` + +Profiling + + +```bash +github.com/moov-io/ach/cmd/writeACH>main -cpuprofile=writeACH.pprof +``` + +**Read:** + +```bash +github.com/moov-io/ach/cmd/readACH +``` + +Use fPath to define the file to be read ( e.g. -fPath=github.com/moov-io/ach/cmd/readACH/_filename_ ) + +Benchmark + +```bash +github.com/moov-io/ach/cmd/readACH>go test -bench=BenchmarkTestFileRead -count=5 > old +``` +Profiling + +```bash +github.com/moov-io/ach/cmd/readACH>main -fPath=_filename_ -cpuprofile=ReadACH.pprof +``` + +## References + * [Wikipeda: Automated Clearing House](http://en.wikipedia.org/wiki/Automated_Clearing_House) * [Nacha ACH Network: How it Works](https://www.nacha.org/ach-network) * [Federal ACH Directory](https://www.frbservices.org/EPaymentsDirectory/search.html) ## Format Specification + * [NACHA ACH File Formatting](https://www.nacha.org/system/files/resources/AAP201%20-%20ACH%20File%20Formatting.pdf) * [PNC ACH File Specification](http://content.pncmc.com/live/pnc/corporate/treasury-management/ach-conversion/ACH-File-Specifications.pdf) * [Thomson Reuters ACH FIle Structure](http://cs.thomsonreuters.com/ua/acct_pr/acs/cs_us_en/pr/dd/ach_file_structure_and_content.htm) @@ -137,6 +182,7 @@ Pull request require a batchMTE_test.go file that covers the logic of the type. ![ACH File Layout](https://github.com/moov-io/ach/blob/master/documentation/ach_file_structure_shg.gif) ## Inspiration + * [ACH:Builder - Tools for Building ACH](http://search.cpan.org/~tkeefer/ACH-Builder-0.03/lib/ACH/Builder.pm) * [mosscode / ach](https://github.com/mosscode/ach) * [Helper for building ACH files in Ruby](https://github.com/jm81/ach) diff --git a/README.md b/README.md index b0a93294d..fc629343a 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,7 @@ Which will generate a well formed ACH flat file. 82200000020010200101000000000000000000000799123456789 234567890000002 9000002000001000000040020400202000000017500000000000799 ``` -# Getting help +# Getting Help channel | info ------- | ------- @@ -260,155 +260,12 @@ Twitter [@moov_io](https://twitter.com/moov_io) | You can follow Moov.IO's Twitt [GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. [moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. -# Contributing - -We use GitHub to manage reviews of pull requests. - -* If you have a trivial fix or improvement, go ahead and create a pull - request, addressing (with `@...`) one or more of the maintainers - (see [AUTHORS.md](AUTHORS.md)) in the description of the pull request. - -* If you plan to do something more involved, first propose your ideas - in a Github issue. This will avoid unnecessary work and surely give - you and us a good deal of inspiration. - -* Relevant coding style guidelines are the [Go Code Review - Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) - and the _Formatting and style_ section of Peter Bourgon's [Go: Best - Practices for Production - Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). - -# Additional SEC (Standard Entry Class) code batch types. -SEC type's in the Batch Header record define the payment type of the following Entry Details and Addenda. The format of the records in the batch is the same between all payment types but NACHA defines different rules for the values that are held in each record field. To add support for an additional SEC type you will need to implement NACHA rules for that type. The vast majority of rules are implemented in ach.batch and then composed into Batch(SEC) for reuse. All Batch(SEC) types must be a ach.Batcher. - -1. Create a milestone for the new SEC type that you want supported. -2. Add issues to that milestone to meet the NACHA rules for the batch type. -3. Create a new struct of the batch type. In the following example we will use MTE(Machine Transfer Entry) as our example. -4. The following code would be place in a new file batchMTE.go next to the existing batch types. -5. The code is stub code and the MTE type is not implemented. For concrete examples review the existing batch types in the source. - -Create a new struct and compose ach.batch - -```go -type BatchMTE struct { - batch -} -``` -Add the ability for the new type to be created. - -```go -func NewBatchMTE(bh *BatchHeader) *BatchMTE { - batch := new(BatchMTE) - batch.setControl(NewBatchControl) - batch.SetHeader(bh) - return batch -} -``` - -To support the Batcher interface you must add the following functions that are not implemented in ach.batch. -* Validate() error -* Create() error - -Validate is designed to enforce the NACHA rules for the MTE payment type. Validate is run after a batch of this type is read from a file. If you are creating a batch from code call validate afterwards. - -```go -// Validate checks valid NACHA batch rules. Assumes properly parsed records. -func (batch *BatchMTE) Validate() error { - // basic verification of the batch before we validate specific rules. - if err := batch.verify(); err != nil { - return err - } - // Add configuration based validation for this type. - // ... batch.isAddendaCount(1) - // Add type specific validation. - // ... - return nil -} -``` -Create takes the Batch Header and Entry details and creates the proper sequence number and batch control. If additional logic specific to the SEC type is required it building a batch file it should be added here. - -```go -// Create takes Batch Header and Entries and builds a valid batch -func (batch *BatchMTE) Create() error { - // generates sequence numbers and batch control - if err := batch.build(); err != nil { - return err - } - // Additional steps specific to batch type - // ... - - if err := batch.Validate(); err != nil { - return err - } - return nil -} -``` - -Finally add the batch type to the NewBatch factory in batch.go. - -```go -//... -case "MTE": - return NewBatchMTE(bh), nil -//... -``` -Pull request require a batchMTE_test.go file that covers the logic of the type. - -Command line ACH file write, read, test, benchmark, profiling - -**Write:** - -github.com/moov-io/ach/cmd/writeACH - -main.go will create an ACH file with 4 batches each containing 1250 detail and addenda records - -A custom path can be used by defining fPath ( e.g. -fPath=github.com/moov-io/_directory_ ) - -Benchmark - -github.com/moov-io/ach/cmd/writeACH>go test -bench=BenchmarkTestFileWrite -count=5 > old - -Profiling - -github.com/moov-io/ach/cmd/writeACH>main -cpuprofile=writeACH.pprof - -**Read:** - -github.com/moov-io/ach/cmd/readACH - -Use fPath to define the file to be read ( e.g. -fPath=github.com/moov-io/ach/cmd/readACH/_filename_ ) - -Benchmark - -github.com/moov-io/ach/cmd/readACH>go test -bench=BenchmarkTestFileRead -count=5 > old - -Profiling - -github.com/moov-io/ach/cmd/readACH>main -fPath=_filename_ -cpuprofile=ReadACH.pprof - -## References -* [Wikipeda: Automated Clearing House](http://en.wikipedia.org/wiki/Automated_Clearing_House) -* [Nacha ACH Network: How it Works](https://www.nacha.org/ach-network) -* [Federal ACH Directory](https://www.frbservices.org/EPaymentsDirectory/search.html) - -## Format Specification -* [NACHA ACH File Formatting](https://www.nacha.org/system/files/resources/AAP201%20-%20ACH%20File%20Formatting.pdf) -* [PNC ACH File Specification](http://content.pncmc.com/live/pnc/corporate/treasury-management/ach-conversion/ACH-File-Specifications.pdf) -* [Thomson Reuters ACH FIle Structure](http://cs.thomsonreuters.com/ua/acct_pr/acs/cs_us_en/pr/dd/ach_file_structure_and_content.htm) -* [Gusto: How ACH Works: A developer perspective](http://engineering.gusto.com/how-ach-works-a-developer-perspective-part-4/) - -![ACH File Layout](https://github.com/moov-io/ach/blob/master/documentation/ach_file_structure_shg.gif) - -## Inspiration -* [ACH:Builder - Tools for Building ACH](http://search.cpan.org/~tkeefer/ACH-Builder-0.03/lib/ACH/Builder.pm) -* [mosscode / ach](https://github.com/mosscode/ach) -* [Helper for building ACH files in Ruby](https://github.com/jm81/ach) +## Contributing Yes please! Please review our [Contributing guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) to get started! - ## License -Apache License 2.0 See [LICENSE](LICENSE) for details. +Apache License 2.0 See [LICENSE](LICENSE) for details. [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoov-io%2Fach.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoov-io%2Fach?ref=badge_large) From 696cc4ae29ee2943aabe12bd7d406ae2730dc077 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 6 Jun 2018 10:29:28 -0400 Subject: [PATCH 0142/1694] #189 Support for SEC Code POP #189 Support for SEC Code POP --- README.md | 11 +- batch.go | 2 + batchPOP.go | 95 +++++++++++ batchPOP_test.go | 431 +++++++++++++++++++++++++++++++++++++++++++++++ entryDetail.go | 39 +++++ 5 files changed, 573 insertions(+), 5 deletions(-) create mode 100644 batchPOP.go create mode 100644 batchPOP_test.go diff --git a/README.md b/README.md index fc629343a..30bbaa659 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,15 @@ Package 'moov-io/ach' implements a file reader and writer for parsing [ACH](http ACH is under active development but already in production for multiple companies. Please star the project if you are interested in its progress. * Library currently supports the reading and writing - * PPD (Prearranged payment and deposits) - * WEB (Internet-initiated Entries ) + * ARC (Accounts Receivable Entry + * BOC (Back Office Conversion) * CCD (Corporate credit or debit) - * TEL (Telephone-Initiated Entry) * COR (Automated Notification of Change(NOC)) + * POP (Point of Purchase) + * PPD (Prearranged payment and deposits) * RCK (Represented Check Entries) - * BOC (Back Office Conversion) - * ARC (Accounts Receivable Entry) + * TEL (Telephone-Initiated Entry) + * WEB (Internet-initiated Entries) * Return Entries diff --git a/batch.go b/batch.go index c78d23ddc..6d9c74614 100644 --- a/batch.go +++ b/batch.go @@ -35,6 +35,8 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { return NewBatchCCD(bh), nil case "COR": return NewBatchCOR(bh), nil + case "POP": + return NewBatchPOP(bh), nil case "PPD": return NewBatchPPD(bh), nil case "RCK": diff --git a/batchPOP.go b/batchPOP.go new file mode 100644 index 000000000..e150e2439 --- /dev/null +++ b/batchPOP.go @@ -0,0 +1,95 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "fmt" + +// BatchPOP holds the BatchHeader and BatchControl and all EntryDetail for POP Entries. +// +// Point of Purchase. A check presented in-person to a merchant for purchase is presented +// as an ACH entry instead of a physical check. +// +// This ACH debit application is used by originators as a method of payment for the +// in-person purchase of goods or services by consumers. These Single Entry debit +// entries are initiated by the originator based on a written authorization and +// account information drawn from the source document (a check) obtained from the +// consumer at the point-of-purchase. The source document, which is voided by the +// merchant and returned to the consumer at the point-of-purchase, is used to +// collect the consumer’s routing number, account number and check serial number that +// will be used to generate the debit entry to the consumer’s account. +// +// The difference between POP and ARC is that ARC can result from a check mailed in whereas POP is in-person. +type BatchPOP struct { + batch +} + +// NewBatchPOP returns a *BatchPOP +func NewBatchPOP(bh *BatchHeader) *BatchPOP { + batch := new(BatchPOP) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchPOP) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // Batch POP cannot have an addenda record + if err := batch.isAddendaCount(0); err != nil { + return err + } + + // Add type specific validation. + if batch.Header.StandardEntryClassCode != "POP" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "POP") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + // RCK detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 220, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "POP") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + } + + for _, entry := range batch.Entries { + // POP detail entries must be a debit + if entry.CreditOrDebit() != "D" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + + // Amount must be 25,000 or less + if entry.Amount > 2500000 { + msg := fmt.Sprintf(msgBatchAmount, "25,000", "POP") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} + } + + // CheckSerialNumber, Terminal City, Terminal State underlying IdentificationNumber, must be defined + if entry.IdentificationNumber == "" { + msg := fmt.Sprintf(msgBatchCheckSerialNumber, "POP") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg} + } + + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchPOP) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + return batch.Validate() +} diff --git a/batchPOP_test.go b/batchPOP_test.go new file mode 100644 index 000000000..2935c5396 --- /dev/null +++ b/batchPOP_test.go @@ -0,0 +1,431 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +// mockBatchPOPHeader creates a BatchPOP BatchHeader +func mockBatchPOPHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "POP" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "Point of Purchase" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockPOPEntryDetail creates a BatchPOP EntryDetail +func mockPOPEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetPOPCheckSerialNumber("123456789") + entry.SetPOPTerminalCity("PHIL") + entry.SetPOPTerminalState("PA") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchPOPHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchPOP creates a BatchPOP +func mockBatchPOP() *BatchPOP { + mockBatch := NewBatchPOP(mockBatchPOPHeader()) + mockBatch.AddEntry(mockPOPEntryDetail()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// mockBatchPOPHeaderCredit creates a BatchPOP BatchHeader +func mockBatchPOPHeaderCredit() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "POP" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "POP" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockPOPEntryDetailCredit creates a POP EntryDetail with a credit entry +func mockPOPEntryDetailCredit() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetPOPCheckSerialNumber("123456789") + entry.SetPOPTerminalCity("PHIL") + entry.SetPOPTerminalState("PA") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchPOPHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchPOPCredit creates a BatchPOP with a Credit entry +func mockBatchPOPCredit() *BatchPOP { + mockBatch := NewBatchPOP(mockBatchPOPHeaderCredit()) + mockBatch.AddEntry(mockPOPEntryDetailCredit()) + return mockBatch +} + +// testBatchPOPHeader creates a BatchPOP BatchHeader +func testBatchPOPHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchPOPHeader()) + err, ok := batch.(*BatchPOP) + if !ok { + t.Errorf("Expecting BatchPOP got %T", err) + } +} + +// TestBatchPOPHeader tests validating BatchPOP BatchHeader +func TestBatchPOPHeader(t *testing.T) { + testBatchPOPHeader(t) +} + +// BenchmarkBatchPOPHeader benchmarks validating BatchPOP BatchHeader +func BenchmarkBatchPOPHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPHeader(b) + } +} + +// testBatchPOPCreate validates BatchPOP create +func testBatchPOPCreate(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchPOPCreate tests validating BatchPOP create +func TestBatchPOPCreate(t *testing.T) { + testBatchPOPCreate(t) +} + +// BenchmarkBatchPOPCreate benchmarks validating BatchPOP create +func BenchmarkBatchPOPCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPCreate(b) + } +} + +// testBatchPOPStandardEntryClassCode validates BatchPOP create for an invalid StandardEntryClassCode +func testBatchPOPStandardEntryClassCode(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPStandardEntryClassCode tests validating BatchPOP create for an invalid StandardEntryClassCode +func TestBatchPOPStandardEntryClassCode(t *testing.T) { + testBatchPOPStandardEntryClassCode(t) +} + +// BenchmarkBatchPOPStandardEntryClassCode benchmarks validating BatchPOP create for an invalid StandardEntryClassCode +func BenchmarkBatchPOPStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPStandardEntryClassCode(b) + } +} + +// testBatchPOPServiceClass200 validates BatchPOP create for an invalid ServiceClassCode 200 +func testBatchPOPServiceClass200(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.Header.ServiceClassCode = 200 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPServiceClass200 tests validating BatchPOP create for an invalid ServiceClassCode 200 +func TestBatchPOPServiceClass200(t *testing.T) { + testBatchPOPServiceClass200(t) +} + +// BenchmarkBatchPOPServiceClass200 benchmarks validating BatchPOP create for an invalid ServiceClassCode 200 +func BenchmarkBatchPOPServiceClass200(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPServiceClass200(b) + } +} + +// testBatchPOPServiceClass220 validates BatchPOP create for an invalid ServiceClassCode 220 +func testBatchPOPServiceClass220(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.Header.ServiceClassCode = 220 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPServiceClass220 tests validating BatchPOP create for an invalid ServiceClassCode 220 +func TestBatchPOPServiceClass220(t *testing.T) { + testBatchPOPServiceClass220(t) +} + +// BenchmarkBatchPOPServiceClass220 benchmarks validating BatchPOP create for an invalid ServiceClassCode 220 +func BenchmarkBatchPOPServiceClass220(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPServiceClass220(b) + } +} + +// testBatchPOPServiceClass280 validates BatchPOP create for an invalid ServiceClassCode 280 +func testBatchPOPServiceClass280(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.Header.ServiceClassCode = 280 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPServiceClass280 tests validating BatchPOP create for an invalid ServiceClassCode 280 +func TestBatchPOPServiceClass280(t *testing.T) { + testBatchPOPServiceClass280(t) +} + +// BenchmarkBatchPOPServiceClass280 benchmarks validating BatchPOP create for an invalid ServiceClassCode 280 +func BenchmarkBatchPOPServiceClass280(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPServiceClass280(b) + } +} + +// testBatchPOPAmount validates BatchPOP create for an invalid Amount +func testBatchPOPAmount(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.Entries[0].Amount = 2600000 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Amount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPAmount validates BatchPOP create for an invalid Amount +func TestBatchPOPAmount(t *testing.T) { + testBatchPOPAmount(t) +} + +// BenchmarkBatchPOPAmount validates BatchPOP create for an invalid Amount +func BenchmarkBatchPOPAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPAmount(b) + } +} + +// testBatchPOPCheckSerialNumber validates BatchPOP CheckSerialNumber / IdentificationNumber is a mandatory field +func testBatchPOPCheckSerialNumber(t testing.TB) { + mockBatch := mockBatchPOP() + // modify CheckSerialNumber / IdentificationNumber to nothing + mockBatch.GetEntries()[0].SetCheckSerialNumber("") + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CheckSerialNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPCheckSerialNumber tests validating BatchPOP +// CheckSerialNumber / IdentificationNumber is a mandatory field +func TestBatchPOPCheckSerialNumber(t *testing.T) { + testBatchPOPCheckSerialNumber(t) +} + +// BenchmarkBatchPOPCheckSerialNumber benchmarks validating BatchPOP +// CheckSerialNumber / IdentificationNumber is a mandatory field +func BenchmarkBatchPOPCheckSerialNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPCheckSerialNumber(b) + } +} + +// testBatchPOPCheckSerialNumberField validates POPCheckSerialNumberField characters 1-9 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func testBatchPOPCheckSerialNumberField(t testing.TB) { + mockBatch := mockBatchPOP() + tc := mockBatch.Entries[0].POPCheckSerialNumberField() + if tc != "123456789" { + t.Error("CheckSerialNumber is invalid") + } +} + +// TestBatchPPOPCheckSerialNumberField tests validating POPCheckSerialNumberField characters 1-9 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func TestBatchPOPCheckSerialNumberField(t *testing.T) { + testBatchPOPCheckSerialNumberField(t) +} + +// BenchmarkBatchPOPCheckSerialNumberField benchmarks validating POPCheckSerialNumberField characters 1-9 of underlying +// BatchPOP CheckSerialNumber / IdentificationNumber +func BenchmarkBatchPOPCheckSerialNumberField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPTerminalCityField(b) + } +} + +// testBatchPOPTerminalCityField validates POPTerminalCity characters 10-13 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func testBatchPOPTerminalCityField(t testing.TB) { + mockBatch := mockBatchPOP() + tc := mockBatch.Entries[0].POPTerminalCityField() + if tc != "PHIL" { + t.Error("TerminalCity is invalid") + } +} + +// TestBatchPOPTerminalCityField tests validating POPTerminalCity characters 10-13 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func TestBatchPOPTerminalCityField(t *testing.T) { + testBatchPOPTerminalCityField(t) +} + +// BenchmarkBatchPOPTerminalCityField benchmarks validating POPTerminalCity characters 10-13 of underlying +// BatchPOP CheckSerialNumber / IdentificationNumber +func BenchmarkBatchPOPTerminalCityField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPTerminalCityField(b) + } +} + +// testBatchPOPTerminalStateField validates POPTerminalState characters 14-15 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func testBatchPOPTerminalStateField(t testing.TB) { + mockBatch := mockBatchPOP() + ts := mockBatch.Entries[0].POPTerminalStateField() + if ts != "PA" { + t.Error("TerminalState is invalid") + } +} + +// TestBatchPOPTerminalStateField tests validating POPTerminalState characters 14-15 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func TestBatchPOPTerminalStateField(t *testing.T) { + testBatchPOPTerminalStateField(t) +} + +// BenchmarkBatchPOPTerminalStateField benchmarks validating POPTerminalState characters 14-15 of underlying +// BatchPOP CheckSerialNumber / IdentificationNumber +func BenchmarkBatchPOPTerminalStateField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPTerminalStateField(b) + } +} + +// testBatchPOPTransactionCode validates BatchPOP TransactionCode is not a credit +func testBatchPOPTransactionCode(t testing.TB) { + mockBatch := mockBatchPOPCredit() + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPTransactionCode tests validating BatchPOP TransactionCode is not a credit +func TestBatchPOPTransactionCode(t *testing.T) { + testBatchPOPTransactionCode(t) +} + +// BenchmarkBatchPOPTransactionCode benchmarks validating BatchPOP TransactionCode is not a credit +func BenchmarkBatchPOPTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPTransactionCode(b) + } +} + +// testBatchPOPAddendaCount validates BatchPOP Addenda count +func testBatchPOPAddendaCount(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPAddendaCount tests validating BatchPOP Addenda count +func TestBatchPOPAddendaCount(t *testing.T) { + testBatchPOPAddendaCount(t) +} + +// BenchmarkBatchPOPAddendaCount benchmarks validating BatchPOP Addenda count +func BenchmarkBatchPOPAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCAddendaCount(b) + } +} diff --git a/entryDetail.go b/entryDetail.go index abb3cffe8..a4d90d0f5 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -282,6 +282,45 @@ func (ed *EntryDetail) SetCheckSerialNumber(s string) { ed.IdentificationNumber = s } +// SetPOPCheckSerialNumber setter for POP CheckSerialNumber +// which is characters 1-9 of underlying CheckSerialNumber \ IdentificationNumber +func (ed *EntryDetail) SetPOPCheckSerialNumber(s string) { + ed.IdentificationNumber = ed.alphaField(s, 9) +} + +// SetPOPTerminalCity setter for POP Terminal City +// which is characters 10-13 of underlying CheckSerialNumber \ IdentificationNumber +func (ed *EntryDetail) SetPOPTerminalCity(s string) { + ed.IdentificationNumber = ed.IdentificationNumber + ed.alphaField(s, 4) +} + +// SetPOPTerminalCity setter for POP Terminal State +// which is characters 14-15 of underlying CheckSerialNumber \ IdentificationNumber +func (ed *EntryDetail) SetPOPTerminalState(s string) { + ed.IdentificationNumber = ed.IdentificationNumber + ed.alphaField(s, 2) +} + +// POPCheckSerialNumberField is used in POP, characters 1-9 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func (ed *EntryDetail) POPCheckSerialNumberField() string { + //return ed.alphaField(ed.IdentificationNumber, 9) + return ed.parseStringField(ed.IdentificationNumber[0:9]) +} + +// POPTerminalCityField is used in POP, characters 10-13 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func (ed *EntryDetail) POPTerminalCityField() string { + //return ed.alphaField(ed.IdentificationNumber, 9) + return ed.parseStringField(ed.IdentificationNumber[9:13]) +} + +// POPTerminalStateField is used in POP, characters 14-15 of underlying BatchPOP +// CheckSerialNumber / IdentificationNumber +func (ed *EntryDetail) POPTerminalStateField() string { + //return ed.alphaField(ed.IdentificationNumber, 9) + return ed.parseStringField(ed.IdentificationNumber[13:15]) +} + // IndividualNameField returns a space padded string of IndividualName func (ed *EntryDetail) IndividualNameField() string { return ed.alphaField(ed.IndividualName, 22) From 42433cd44df6a6043b8f2a1442411f2ba368437d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 6 Jun 2018 10:50:00 -0400 Subject: [PATCH 0143/1694] #189 Golint error #189 Golint error --- entryDetail.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entryDetail.go b/entryDetail.go index a4d90d0f5..8507bc7d0 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -294,7 +294,7 @@ func (ed *EntryDetail) SetPOPTerminalCity(s string) { ed.IdentificationNumber = ed.IdentificationNumber + ed.alphaField(s, 4) } -// SetPOPTerminalCity setter for POP Terminal State +// SetPOPTerminalState setter for POP Terminal State // which is characters 14-15 of underlying CheckSerialNumber \ IdentificationNumber func (ed *EntryDetail) SetPOPTerminalState(s string) { ed.IdentificationNumber = ed.IdentificationNumber + ed.alphaField(s, 2) From ff5e0fe7a8feed055b7759e0ceaa87767f4b8537 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 6 Jun 2018 13:46:20 -0400 Subject: [PATCH 0144/1694] #189 SEC code POP test #189 SEC code POP test --- test/ach-pop-read/main.go | 36 ++++++++++++++++++ test/ach-pop-read/main_test.go | 7 ++++ test/ach-pop-read/pop-debit.ach | 10 +++++ test/ach-pop-write/main.go | 66 +++++++++++++++++++++++++++++++++ test/ach-pop-write/main_test.go | 7 ++++ 5 files changed, 126 insertions(+) create mode 100644 test/ach-pop-read/main.go create mode 100644 test/ach-pop-read/main_test.go create mode 100644 test/ach-pop-read/pop-debit.ach create mode 100644 test/ach-pop-write/main.go create mode 100644 test/ach-pop-write/main_test.go diff --git a/test/ach-pop-read/main.go b/test/ach-pop-read/main.go new file mode 100644 index 000000000..e47c1fbb3 --- /dev/null +++ b/test/ach-pop-read/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "github.com/moov-io/ach" + "fmt" + "log" + "os" + + +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("pop-debit.ach") + if err != nil { + log.Panicf("Can not open file: %s: \n", err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("total amount debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("POP check serial number: %v \n", achFile.Batches[0].GetEntries()[0].POPCheckSerialNumberField()) + fmt.Printf("POP terminal city: %v \n", achFile.Batches[0].GetEntries()[0].POPTerminalCityField()) + fmt.Printf("POP terminal state: %v \n", achFile.Batches[0].GetEntries()[0].POPTerminalStateField()) +} diff --git a/test/ach-pop-read/main_test.go b/test/ach-pop-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-pop-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-pop-read/pop-debit.ach b/test/ach-pop-read/pop-debit.ach new file mode 100644 index 000000000..61c75692d --- /dev/null +++ b/test/ach-pop-read/pop-debit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821806060000A094101Federal Reserve Bank My Bank Name +5225Name on Account 121042882 POPACH POP 180607 0121042880000001 +62723138010412345678 0000250500123456 PHILPAWade Arnold 0121042880000001 +82250000010023138010000000250500000000000000121042882 121042880000001 +9000001000001000000010023138010000000250500000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-pop-write/main.go b/test/ach-pop-write/main.go new file mode 100644 index 000000000..5cc822333 --- /dev/null +++ b/test/ach-pop-write/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "github.com/moov-io/ach" + "log" + "os" + "time" +) + +func main() { + // Example transfer to write an ACH RCK file to debit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "POP" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "ACH POP" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 250500 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.IndividualName = "Wade Arnold" // Identifies the receiver of the transaction + entry.SetPOPCheckSerialNumber("123456") + entry.SetPOPTerminalCity("PHIL") + entry.SetPOPTerminalState("PA") + + // build the batch + batch := ach.NewBatchPOP(bh) + batch.AddEntry(entry) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.WriteAll([]*ach.File{file}); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/test/ach-pop-write/main_test.go b/test/ach-pop-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-pop-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From 29806e772602c87bf6d7d0dd8d0337f212400570 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 7 Jun 2018 10:16:46 -0400 Subject: [PATCH 0145/1694] SEC Code Tests SEC Code Tests: Minor fixes to existing tests Move PPD from example package to test package Added ARC ACh file test --- test/ach-arc-read/arc-debit.ach | 10 +++ test/ach-arc-read/main.go | 34 ++++++++++ .../ach-arc-read}/main_test.go | 0 test/ach-arc-write/main.go | 64 +++++++++++++++++++ .../ach-arc-write}/main_test.go | 0 test/ach-pop-read/main.go | 13 ++-- test/ach-pop-read/pop-debit.ach | 4 +- test/ach-pop-write/main.go | 10 +-- {example => test}/ach-ppd-read/main.go | 5 +- test/ach-ppd-read/main_test.go | 7 ++ {example => test}/ach-ppd-read/ppd-debit.ach | 0 {example => test}/ach-ppd-write/main.go | 2 +- test/ach-ppd-write/main_test.go | 7 ++ test/ach-rck-read/main.go | 4 +- 14 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 test/ach-arc-read/arc-debit.ach create mode 100644 test/ach-arc-read/main.go rename {example/ach-ppd-read => test/ach-arc-read}/main_test.go (100%) create mode 100644 test/ach-arc-write/main.go rename {example/ach-ppd-write => test/ach-arc-write}/main_test.go (100%) rename {example => test}/ach-ppd-read/main.go (71%) create mode 100644 test/ach-ppd-read/main_test.go rename {example => test}/ach-ppd-read/ppd-debit.ach (100%) rename {example => test}/ach-ppd-write/main.go (98%) create mode 100644 test/ach-ppd-write/main_test.go diff --git a/test/ach-arc-read/arc-debit.ach b/test/ach-arc-read/arc-debit.ach new file mode 100644 index 000000000..42d410b0e --- /dev/null +++ b/test/ach-arc-read/arc-debit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821806070000A094101Federal Reserve Bank My Bank Name +5225Payee Name 121042882 ARCACH ARC 180608 0121042880000001 +62723138010412345678 0000250000123879654 ABC Company 0121042880000001 +82250000010023138010000000250000000000000000121042882 121042880000001 +9000001000001000000010023138010000000250000000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-arc-read/main.go b/test/ach-arc-read/main.go new file mode 100644 index 000000000..04768ce06 --- /dev/null +++ b/test/ach-arc-read/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/moov-io/ach" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("arc-debit.ach") + if err != nil { + log.Panicf("Can not open file: %s: \n", err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("Check Serial Number: %v \n", achFile.Batches[0].GetEntries()[0].CheckSerialNumberField()) +} diff --git a/example/ach-ppd-read/main_test.go b/test/ach-arc-read/main_test.go similarity index 100% rename from example/ach-ppd-read/main_test.go rename to test/ach-arc-read/main_test.go diff --git a/test/ach-arc-write/main.go b/test/ach-arc-write/main.go new file mode 100644 index 000000000..a9a680dac --- /dev/null +++ b/test/ach-arc-write/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "github.com/moov-io/ach" + "log" + "os" + "time" +) + +func main() { + // Example transfer to write an ACH ARC file to debit an external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Payee Name" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "ARC" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "ACH ARC" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 250000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetCheckSerialNumber("123879654") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(bh.ODFIIdentification, 1) + + // build the batch + batch := ach.NewBatchARC(bh) + batch.AddEntry(entry) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.WriteAll([]*ach.File{file}); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/example/ach-ppd-write/main_test.go b/test/ach-arc-write/main_test.go similarity index 100% rename from example/ach-ppd-write/main_test.go rename to test/ach-arc-write/main_test.go diff --git a/test/ach-pop-read/main.go b/test/ach-pop-read/main.go index e47c1fbb3..2a94d5b89 100644 --- a/test/ach-pop-read/main.go +++ b/test/ach-pop-read/main.go @@ -1,12 +1,10 @@ package main import ( - "github.com/moov-io/ach" "fmt" + "github.com/moov-io/ach" "log" "os" - - ) func main() { @@ -29,8 +27,9 @@ func main() { fmt.Printf("Could not build file with read properties: %v", err) } - fmt.Printf("total amount debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) - fmt.Printf("POP check serial number: %v \n", achFile.Batches[0].GetEntries()[0].POPCheckSerialNumberField()) - fmt.Printf("POP terminal city: %v \n", achFile.Batches[0].GetEntries()[0].POPTerminalCityField()) - fmt.Printf("POP terminal state: %v \n", achFile.Batches[0].GetEntries()[0].POPTerminalStateField()) + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("POP Check Serial Number: %v \n", achFile.Batches[0].GetEntries()[0].POPCheckSerialNumberField()) + fmt.Printf("POP Terminal City: %v \n", achFile.Batches[0].GetEntries()[0].POPTerminalCityField()) + fmt.Printf("POP Terminal State: %v \n", achFile.Batches[0].GetEntries()[0].POPTerminalStateField()) } diff --git a/test/ach-pop-read/pop-debit.ach b/test/ach-pop-read/pop-debit.ach index 61c75692d..3ccba9dda 100644 --- a/test/ach-pop-read/pop-debit.ach +++ b/test/ach-pop-read/pop-debit.ach @@ -1,5 +1,5 @@ -101 231380104 1210428821806060000A094101Federal Reserve Bank My Bank Name -5225Name on Account 121042882 POPACH POP 180607 0121042880000001 +101 231380104 1210428821806070000A094101Federal Reserve Bank My Bank Name +5225Originator 121042882 POPACH POP 180608 0121042880000001 62723138010412345678 0000250500123456 PHILPAWade Arnold 0121042880000001 82250000010023138010000000250500000000000000121042882 121042880000001 9000001000001000000010023138010000000250500000000000000 diff --git a/test/ach-pop-write/main.go b/test/ach-pop-write/main.go index 5cc822333..654f77d70 100644 --- a/test/ach-pop-write/main.go +++ b/test/ach-pop-write/main.go @@ -8,7 +8,7 @@ import ( ) func main() { - // Example transfer to write an ACH RCK file to debit a external institutions account + // Example transfer to write an ACH POP file to debit a external institutions account // Important: All financial institutions are different and will require registration and exact field values. fh := ach.NewFileHeader() @@ -20,10 +20,10 @@ func main() { // BatchHeader identifies the originating entity and the type of transactions contained in the batch bh := ach.NewBatchHeader() - bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. - bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Originator" // The name of the company/person that has relationship with receiver bh.CompanyIdentification = fh.ImmediateOrigin - bh.StandardEntryClassCode = "POP" // Consumer destination vs Company CCD + bh.StandardEntryClassCode = "POP" // Consumer destination vs Company CCD bh.CompanyEntryDescription = "ACH POP" // will be on receiving accounts statement bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) bh.ODFIIdentification = "121042882" // Originating Routing Number @@ -35,7 +35,7 @@ func main() { entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account entry.SetRDFI("231380104") // Receivers bank transit routing number entry.DFIAccountNumber = "12345678" // Receivers bank account number - entry.Amount = 250500 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.Amount = 250500 // Amount of transaction with no decimal. One dollar and eleven cents = 111 entry.SetTraceNumber(bh.ODFIIdentification, 1) entry.IndividualName = "Wade Arnold" // Identifies the receiver of the transaction entry.SetPOPCheckSerialNumber("123456") diff --git a/example/ach-ppd-read/main.go b/test/ach-ppd-read/main.go similarity index 71% rename from example/ach-ppd-read/main.go rename to test/ach-ppd-read/main.go index 3afec70bf..c7c6d1ebf 100644 --- a/example/ach-ppd-read/main.go +++ b/test/ach-ppd-read/main.go @@ -23,10 +23,11 @@ func main() { if achFile.Validate(); err != nil { fmt.Printf("Could not validate entire read file: %v", err) } - // If you trust the file but it's formating is off building will probably resolve the malformed file. + // If you trust the file but it's formatting is off building will probably resolve the malformed file. if achFile.Create(); err != nil { fmt.Printf("Could not build file with read properties: %v", err) } - fmt.Printf("total amount debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) } diff --git a/test/ach-ppd-read/main_test.go b/test/ach-ppd-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-ppd-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/example/ach-ppd-read/ppd-debit.ach b/test/ach-ppd-read/ppd-debit.ach similarity index 100% rename from example/ach-ppd-read/ppd-debit.ach rename to test/ach-ppd-read/ppd-debit.ach diff --git a/example/ach-ppd-write/main.go b/test/ach-ppd-write/main.go similarity index 98% rename from example/ach-ppd-write/main.go rename to test/ach-ppd-write/main.go index 96b7d805d..a320bfea6 100644 --- a/example/ach-ppd-write/main.go +++ b/test/ach-ppd-write/main.go @@ -17,7 +17,7 @@ func main() { fh := ach.NewFileHeader() fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file - fh.FileCreationDate = time.Now() // Todays Date + fh.FileCreationDate = time.Now() // Today's Date fh.ImmediateDestinationName = "Federal Reserve Bank" fh.ImmediateOriginName = "My Bank Name" diff --git a/test/ach-ppd-write/main_test.go b/test/ach-ppd-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-ppd-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-rck-read/main.go b/test/ach-rck-read/main.go index ad35f13c7..9075f677c 100644 --- a/test/ach-rck-read/main.go +++ b/test/ach-rck-read/main.go @@ -28,5 +28,7 @@ func main() { fmt.Printf("Could not build file with read properties: %v", err) } - fmt.Printf("total amount debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("Check Serial Number: %v \n", achFile.Batches[0].GetEntries()[0].CheckSerialNumberField()) } From 5480333fb292bf8cd9c634c134b5419414ae7f77 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 7 Jun 2018 10:29:42 -0400 Subject: [PATCH 0146/1694] SEC-Code-Tests Add BOC ACh File Test --- test/ach-boc-read/boc-debit.ach | 10 ++++++ test/ach-boc-read/main.go | 34 ++++++++++++++++++ test/ach-boc-read/main_test.go | 7 ++++ test/ach-boc-write/main.go | 64 +++++++++++++++++++++++++++++++++ test/ach-boc-write/main_test.go | 7 ++++ 5 files changed, 122 insertions(+) create mode 100644 test/ach-boc-read/boc-debit.ach create mode 100644 test/ach-boc-read/main.go create mode 100644 test/ach-boc-read/main_test.go create mode 100644 test/ach-boc-write/main.go create mode 100644 test/ach-boc-write/main_test.go diff --git a/test/ach-boc-read/boc-debit.ach b/test/ach-boc-read/boc-debit.ach new file mode 100644 index 000000000..22422f926 --- /dev/null +++ b/test/ach-boc-read/boc-debit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821806070000A094101Federal Reserve Bank My Bank Name +5225Payee Name 121042882 BOCACH BOC 180608 0121042880000001 +62723138010412345678 0000250000123879654 ABC Company 0121042880000001 +82250000010023138010000000250000000000000000121042882 121042880000001 +9000001000001000000010023138010000000250000000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-boc-read/main.go b/test/ach-boc-read/main.go new file mode 100644 index 000000000..e1e354395 --- /dev/null +++ b/test/ach-boc-read/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/moov-io/ach" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("boc-debit.ach") + if err != nil { + log.Panicf("Can not open file: %s: \n", err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("Check Serial Number: %v \n", achFile.Batches[0].GetEntries()[0].CheckSerialNumberField()) +} diff --git a/test/ach-boc-read/main_test.go b/test/ach-boc-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-boc-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-boc-write/main.go b/test/ach-boc-write/main.go new file mode 100644 index 000000000..a8794521e --- /dev/null +++ b/test/ach-boc-write/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "github.com/moov-io/ach" + "log" + "os" + "time" +) + +func main() { + // Example transfer to write an ACH BOC file to debit an external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Payee Name" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "BOC" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "ACH BOC" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 250000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetCheckSerialNumber("123879654") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(bh.ODFIIdentification, 1) + + // build the batch + batch := ach.NewBatchBOC(bh) + batch.AddEntry(entry) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.WriteAll([]*ach.File{file}); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/test/ach-boc-write/main_test.go b/test/ach-boc-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-boc-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From 5c694ae82a6c2aa3aacadb69df04a97eeb5b8c16 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 7 Jun 2018 11:27:45 -0400 Subject: [PATCH 0147/1694] SEC Code Tests SEC Code Tests - Update read tests to log.Fatal(err) if a file cannot be opened. Move simple-file-creation test from example package to test package --- test/ach-arc-read/main.go | 2 +- test/ach-boc-read/main.go | 2 +- test/ach-pop-read/main.go | 2 +- test/ach-ppd-read/main.go | 2 +- test/ach-rck-read/main.go | 2 +- {example => test}/simple-file-creation/main.go | 2 +- {example => test}/simple-file-creation/main_test.go | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename {example => test}/simple-file-creation/main.go (98%) rename {example => test}/simple-file-creation/main_test.go (100%) diff --git a/test/ach-arc-read/main.go b/test/ach-arc-read/main.go index 04768ce06..128c0f724 100644 --- a/test/ach-arc-read/main.go +++ b/test/ach-arc-read/main.go @@ -12,7 +12,7 @@ func main() { // open a file for reading. Any io.Reader Can be used f, err := os.Open("arc-debit.ach") if err != nil { - log.Panicf("Can not open file: %s: \n", err) + log.Fatal(err) } r := ach.NewReader(f) achFile, err := r.Read() diff --git a/test/ach-boc-read/main.go b/test/ach-boc-read/main.go index e1e354395..f3444db2a 100644 --- a/test/ach-boc-read/main.go +++ b/test/ach-boc-read/main.go @@ -12,7 +12,7 @@ func main() { // open a file for reading. Any io.Reader Can be used f, err := os.Open("boc-debit.ach") if err != nil { - log.Panicf("Can not open file: %s: \n", err) + log.Fatal(err) } r := ach.NewReader(f) achFile, err := r.Read() diff --git a/test/ach-pop-read/main.go b/test/ach-pop-read/main.go index 2a94d5b89..a29485aea 100644 --- a/test/ach-pop-read/main.go +++ b/test/ach-pop-read/main.go @@ -11,7 +11,7 @@ func main() { // open a file for reading. Any io.Reader Can be used f, err := os.Open("pop-debit.ach") if err != nil { - log.Panicf("Can not open file: %s: \n", err) + log.Fatal(err) } r := ach.NewReader(f) achFile, err := r.Read() diff --git a/test/ach-ppd-read/main.go b/test/ach-ppd-read/main.go index c7c6d1ebf..2446e9657 100644 --- a/test/ach-ppd-read/main.go +++ b/test/ach-ppd-read/main.go @@ -12,7 +12,7 @@ func main() { // open a file for reading. Any io.Reader Can be used f, err := os.Open("ppd-debit.ach") if err != nil { - log.Panicf("Can not open file: %s: \n", err) + log.Fatal(err) } r := ach.NewReader(f) achFile, err := r.Read() diff --git a/test/ach-rck-read/main.go b/test/ach-rck-read/main.go index 9075f677c..e52c76c3f 100644 --- a/test/ach-rck-read/main.go +++ b/test/ach-rck-read/main.go @@ -12,7 +12,7 @@ func main() { // open a file for reading. Any io.Reader Can be used f, err := os.Open("rck-debit.ach") if err != nil { - log.Panicf("Can not open file: %s: \n", err) + log.Fatal(err) } r := ach.NewReader(f) achFile, err := r.Read() diff --git a/example/simple-file-creation/main.go b/test/simple-file-creation/main.go similarity index 98% rename from example/simple-file-creation/main.go rename to test/simple-file-creation/main.go index c1163f788..2e3774db6 100644 --- a/example/simple-file-creation/main.go +++ b/test/simple-file-creation/main.go @@ -71,7 +71,7 @@ func main() { bh2.CompanyName = "Your Company" bh2.CompanyIdentification = file.Header.ImmediateOrigin bh2.StandardEntryClassCode = "WEB" - bh2.CompanyEntryDescription = "Subscr" + bh2.CompanyEntryDescription = "Subscribe" bh2.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) bh2.ODFIIdentification = "121042882" diff --git a/example/simple-file-creation/main_test.go b/test/simple-file-creation/main_test.go similarity index 100% rename from example/simple-file-creation/main_test.go rename to test/simple-file-creation/main_test.go From fa8fee87c5b9fe9f06fde7b1dc86e62d7b1e5fd5 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 7 Jun 2018 11:31:58 -0400 Subject: [PATCH 0148/1694] SEC_Code_Tests Remove example directory from GO_FILES --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 13383adf2..9675d5e26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_install: - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo before_script: -- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/ | grep -v /example/) +- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/) script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES From 29fa4c0624c36249c12a3d02886cbd90d887051b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 7 Jun 2018 11:20:38 -0600 Subject: [PATCH 0149/1694] Remove example directory --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9675d5e26..13383adf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_install: - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo before_script: -- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/) +- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/ | grep -v /example/) script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES From 829029c809bc2f4ec3672b21259e250e227f686b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 7 Jun 2018 20:14:45 -0400 Subject: [PATCH 0150/1694] #153 BatchCOR TransactionCode #153 COR TransactionCode must be 21, 26, 31, 36, 41, 46, 51, or 56. --- batchCOR.go | 13 +++++- batchCOR_test.go | 113 +++++++++++++++++++++++++++++++++++------------ batcher.go | 1 + entryDetail.go | 16 +++++++ 4 files changed, 114 insertions(+), 29 deletions(-) diff --git a/batchCOR.go b/batchCOR.go index e3be0db77..3fc04e965 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -34,7 +34,7 @@ func (batch *BatchCOR) Validate() error { return err } // Add configuration based validation for this type. - // Web can have up to one addenda per entry record + // COR Addenda must be Addenda98 if err := batch.isAddenda98(); err != nil { return err } @@ -52,6 +52,17 @@ func (batch *BatchCOR) Validate() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Amount", Msg: msg} } + for _, entry := range batch.Entries { + // COR TransactionCode must be a Return or NOC transaction Code + // Return/NOC of a credit 21, 31, 41, 51 + // Return/NOC of a debit 26, 36, 46, 56 + if entry.TransactionCodeDescription() != ReturnOrNoc { + msg := fmt.Sprintf(msgBatchTransactionCode, entry.TransactionCode, "COR") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + + } + return nil } diff --git a/batchCOR_test.go b/batchCOR_test.go index 3ed5e68a8..f756a9bec 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -10,22 +10,22 @@ import ( // TODO make all the mock values cor fields -// mockBatchCORHeader creates a COR batch header +// mockBatchCORHeader creates a COR BatchHeader func mockBatchCORHeader() *BatchHeader { bh := NewBatchHeader() bh.ServiceClassCode = 220 bh.StandardEntryClassCode = "COR" bh.CompanyName = "Your Company, inc" bh.CompanyIdentification = "121042882" - bh.CompanyEntryDescription = "Vndr Pay" + bh.CompanyEntryDescription = "Vendor Pay" bh.ODFIIdentification = "121042882" return bh } -// mockCOREntryDetail creates a COR entry detail +// mockCOREntryDetail creates a COR EntryDetail func mockCOREntryDetail() *EntryDetail { entry := NewEntryDetail() - entry.TransactionCode = 27 + entry.TransactionCode = 21 entry.SetRDFI("231380104") entry.DFIAccountNumber = "744-5678-99" entry.Amount = 0 @@ -36,7 +36,7 @@ func mockCOREntryDetail() *EntryDetail { return entry } -// mockBatchCOR creates a COR batch +// mockBatchCOR creates a BatchCOR func mockBatchCOR() *BatchCOR { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) @@ -47,7 +47,7 @@ func mockBatchCOR() *BatchCOR { return mockBatch } -// testBatchCORHeader creates a COR batch header +// testBatchCORHeader creates a COR BatchHeader func testBatchCORHeader(t testing.TB) { batch, _ := NewBatch(mockBatchCORHeader()) @@ -57,12 +57,12 @@ func testBatchCORHeader(t testing.TB) { } } -// TestBatchCORHeader tests creating a COR batch header +// TestBatchCORHeader tests creating a COR BatchHeader func TestBatchCORHeader(t *testing.T) { testBatchCORHeader(t) } -// BenchmarkBatchCORHeader benchmarks creating a COR batch header +// BenchmarkBatchCORHeader benchmarks creating a COR BatchHeader func BenchmarkBatchCORHeader(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -70,10 +70,10 @@ func BenchmarkBatchCORHeader(b *testing.B) { } } -// testBatchCORSEC validates COR SEC code +// testBatchCORSEC validates BatchCOR SEC code func testBatchCORSEC(t testing.TB) { mockBatch := mockBatchCOR() - mockBatch.Header.StandardEntryClassCode = "RCK" + mockBatch.Header.StandardEntryClassCode = "COR" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "StandardEntryClassCode" { @@ -85,12 +85,12 @@ func testBatchCORSEC(t testing.TB) { } } -// TestBatchCORSEC tests validating COR SEC code +// TestBatchCORSEC tests validating BatchCOR SEC code func TestBatchCORSEC(t *testing.T) { testBatchCORSEC(t) } -// BenchmarkBatchCORSEC benchmarks validating COR SEC code +// BenchmarkBatchCORSEC benchmarks validating BatchCOR SEC code func BenchmarkBatchCORSEC(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -98,7 +98,7 @@ func BenchmarkBatchCORSEC(b *testing.B) { } } -// testBatchCORAddendumCountTwo validates addendum count of 2 +// testBatchCORAddendumCountTwo validates Addendum count of 2 func testBatchCORAddendumCountTwo(t testing.TB) { mockBatch := mockBatchCOR() // Adding a second addenda to the mock entry @@ -114,12 +114,12 @@ func testBatchCORAddendumCountTwo(t testing.TB) { } } -// TestBatchCORAddendumCountTwo tests validating addendum count of 2 +// TestBatchCORAddendumCountTwo tests validating Addendum count of 2 func TestBatchCORAddendumCountTwo(t *testing.T) { testBatchCORAddendumCountTwo(t) } -// BenchmarkBatchCORAddendumCountTwo benchmarks validating addendum count of 2 +// BenchmarkBatchCORAddendumCountTwo benchmarks validating Addendum count of 2 func BenchmarkBatchCORAddendumCountTwo(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -127,7 +127,7 @@ func BenchmarkBatchCORAddendumCountTwo(b *testing.B) { } } -// testBatchCORAddendaCountZero validates addendum count of 0 +// testBatchCORAddendaCountZero validates Addendum count of 0 func testBatchCORAddendaCountZero(t testing.TB) { mockBatch := NewBatchCOR(mockBatchCORHeader()) mockBatch.AddEntry(mockCOREntryDetail()) @@ -142,12 +142,12 @@ func testBatchCORAddendaCountZero(t testing.TB) { } } -// TestBatchCORAddendaCountZero tests validating addendum count of 0 +// TestBatchCORAddendaCountZero tests validating Addendum count of 0 func TestBatchCORAddendaCountZero(t *testing.T) { testBatchCORAddendaCountZero(t) } -// BenchmarkBatchCORAddendaCountZero benchmarks validating addendum count of 0 +// BenchmarkBatchCORAddendaCountZero benchmarks validating Addendum count of 0 func BenchmarkBatchCORAddendaCountZero(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -184,7 +184,7 @@ func BenchmarkBatchCORAddendaType(b *testing.B) { } } -// testBatchCORAddendaTypeCode validates Type Code +// testBatchCORAddendaTypeCode validates TypeCode func testBatchCORAddendaTypeCode(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.GetEntries()[0].Addendum[0].(*Addenda98).typeCode = "07" @@ -199,12 +199,12 @@ func testBatchCORAddendaTypeCode(t testing.TB) { } } -// TestBatchCORAddendaTypeCode tests validating Type Code +// TestBatchCORAddendaTypeCode tests validating TypeCode func TestBatchCORAddendaTypeCode(t *testing.T) { testBatchCORAddendaTypeCode(t) } -// BenchmarkBatchCORAddendaTypeCode benchmarks validating Type Code +// BenchmarkBatchCORAddendaTypeCode benchmarks validating TypeCode func BenchmarkBatchCORAddendaTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -212,7 +212,7 @@ func BenchmarkBatchCORAddendaTypeCode(b *testing.B) { } } -// testBatchCORAmount validates batch COR amount +// testBatchCORAmount validates BatchCOR Amount func testBatchCORAmount(t testing.TB) { mockBatch := mockBatchCOR() mockBatch.GetEntries()[0].Amount = 9999 @@ -227,12 +227,12 @@ func testBatchCORAmount(t testing.TB) { } } -// TestBatchCORAmount tests validating batch COR amount +// TestBatchCORAmount tests validating BatchCOR Amount func TestBatchCORAmount(t *testing.T) { testBatchCORAmount(t) } -// BenchmarkBatchCORAmount benchmarks validating batch COR amount +// BenchmarkBatchCORAmount benchmarks validating BatchCOR Amount func BenchmarkBatchCORAmount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -240,10 +240,67 @@ func BenchmarkBatchCORAmount(b *testing.B) { } } -// testBatchCORCreate creates batch COR +// testBatchCORTransactionCode27 validates BatchCOR TransactionCode 27 returns an error +func testBatchCORTransactionCode27(t testing.TB) { + mockBatch := mockBatchCOR() + mockBatch.GetEntries()[0].TransactionCode = 27 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCORTransactionCode27 tests validating BatchCOR TransactionCode 27 returns an error +func TestBatchCORTransactionCode27(t *testing.T) { + testBatchCORTransactionCode27(t) +} + +// BenchmarkBatchCORTransactionCode27 benchmarks validating +// BatchCOR TransactionCode 27 returns an error +func BenchmarkBatchCORTransactionCode27(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORTransactionCode27(b) + } +} + +// testBatchCORTransactionCode21 validates BatchCOR TransactionCode 21 +func testBatchCORTransactionCode21(t testing.TB) { + mockBatch := mockBatchCOR() + mockBatch.GetEntries()[0].TransactionCode = 21 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCORTransactionCode21 tests validating BatchCOR TransactionCode 21 +func TestBatchCORTransactionCode21(t *testing.T) { + testBatchCORTransactionCode21(t) +} + +// BenchmarkBatchCORTransactionCode21 benchmarks validating BatchCOR TransactionCode 21 +func BenchmarkBatchCORTransactionCode21(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORTransactionCode21(b) + } +} + +// testBatchCORCreate creates BatchCOR func testBatchCORCreate(t testing.TB) { mockBatch := mockBatchCOR() - // Must have valid batch header to create a batch + // Must have valid batch header to create a Batch mockBatch.GetHeader().ServiceClassCode = 63 if err := mockBatch.Create(); err != nil { if e, ok := err.(*FieldError); ok { @@ -256,12 +313,12 @@ func testBatchCORCreate(t testing.TB) { } } -// TestBatchCORCreate tests creating batch COR +// TestBatchCORCreate tests creating BatchCOR func TestBatchCORCreate(t *testing.T) { testBatchCORCreate(t) } -// BenchmarkBatchCORCreate benchmarks creating batch COR +// BenchmarkBatchCORCreate benchmarks creating BatchCOR func BenchmarkBatchCORCreate(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/batcher.go b/batcher.go index 9203b6b53..c281d790f 100644 --- a/batcher.go +++ b/batcher.go @@ -61,4 +61,5 @@ var ( msgBatchForwardReturn = "Forward and Return entries found in the same batch" msgBatchAmount = "Amount must be less than %v for SEC code %v" msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" + msgBatchTransactionCode = "%v transaction code is not allowed for batch type %v" ) diff --git a/entryDetail.go b/entryDetail.go index 8507bc7d0..1534a457b 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -93,6 +93,8 @@ const ( CategoryReturn = "Return" // CategoryNOC defines the entry as being a notification of change of a forward entry to the originating institution CategoryNOC = "NOC" + // ReturnOrNoc is the description for the following TransactionCode: 21, 31, 41, 51, 26, 36, 46, 56 + ReturnOrNoc = "RN" ) // NewEntryDetail returns a new EntryDetail with default values for non exported fields @@ -375,3 +377,17 @@ func (ed *EntryDetail) CreditOrDebit() string { } return "" } + +// TransactionCodeDescription determines the transaction code description based on the second number +// in the TransactionCode +func (ed *EntryDetail) TransactionCodeDescription() string { + tc := strconv.Itoa(ed.TransactionCode) + // Take the second number in the TransactionCode + switch tc[1:2] { + // Return or NOC + case "1", "6": + return ReturnOrNoc + default: + } + return "" +} From aad236d1264109ffcb773dac75835c897784bb8c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 8 Jun 2018 09:15:19 -0400 Subject: [PATCH 0151/1694] #153 Add Function ReturnOrNoc #153 Add Function ReturnOrNoc --- batchCOR.go | 4 ++-- batcher.go | 2 +- entryDetail.go | 26 +++++++++++++------------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/batchCOR.go b/batchCOR.go index 3fc04e965..7f2df468d 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -10,7 +10,7 @@ import ( // BatchCOR COR - Automated Notification of Change (NOC) or Refused Notification of Change // This Standard Entry Class Code is used by an RDFI or ODFI when originating a Notification of Change or Refused Notification of Change in automated format. -// A Notification of Change may be created by an RDFI to notify the ODFI that a posted Entry or Prenotification Entry contains invalid or erroneous information and should be changed. +// A Notification of Change may be created by an RDFI to notify the ODFI that a posted Entry or Pre-notification Entry contains invalid or erroneous information and should be changed. type BatchCOR struct { batch } @@ -56,7 +56,7 @@ func (batch *BatchCOR) Validate() error { // COR TransactionCode must be a Return or NOC transaction Code // Return/NOC of a credit 21, 31, 41, 51 // Return/NOC of a debit 26, 36, 46, 56 - if entry.TransactionCodeDescription() != ReturnOrNoc { + if entry.ReturnOrNOC() != true { msg := fmt.Sprintf(msgBatchTransactionCode, entry.TransactionCode, "COR") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} } diff --git a/batcher.go b/batcher.go index c281d790f..7c1c34956 100644 --- a/batcher.go +++ b/batcher.go @@ -61,5 +61,5 @@ var ( msgBatchForwardReturn = "Forward and Return entries found in the same batch" msgBatchAmount = "Amount must be less than %v for SEC code %v" msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" - msgBatchTransactionCode = "%v transaction code is not allowed for batch type %v" + msgBatchTransactionCode = "Transaction code %v is not allowed for batch type %v" ) diff --git a/entryDetail.go b/entryDetail.go index 1534a457b..f8618cf76 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -93,8 +93,6 @@ const ( CategoryReturn = "Return" // CategoryNOC defines the entry as being a notification of change of a forward entry to the originating institution CategoryNOC = "NOC" - // ReturnOrNoc is the description for the following TransactionCode: 21, 31, 41, 51, 26, 36, 46, 56 - ReturnOrNoc = "RN" ) // NewEntryDetail returns a new EntryDetail with default values for non exported fields @@ -378,16 +376,18 @@ func (ed *EntryDetail) CreditOrDebit() string { return "" } -// TransactionCodeDescription determines the transaction code description based on the second number +// ReturnOrNOC determines if transaction code is for a Return or NOC based on the second number // in the TransactionCode -func (ed *EntryDetail) TransactionCodeDescription() string { - tc := strconv.Itoa(ed.TransactionCode) - // Take the second number in the TransactionCode - switch tc[1:2] { - // Return or NOC - case "1", "6": - return ReturnOrNoc - default: - } - return "" +func (ed *EntryDetail) ReturnOrNOC() bool { + tc := strconv.Itoa(ed.TransactionCode) + // Take the second number in the TransactionCode + switch tc[1:2] { + // Return or NOC + case "1", "6": + return true + default: + return false + } + return false } + From a189b2cbb8a7ae4f53ac003617ca06bdb8ca187e Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 8 Jun 2018 09:52:59 -0400 Subject: [PATCH 0152/1694] # 153 Go vet Go vet --- batchCOR.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batchCOR.go b/batchCOR.go index 7f2df468d..a54f542c2 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -56,7 +56,7 @@ func (batch *BatchCOR) Validate() error { // COR TransactionCode must be a Return or NOC transaction Code // Return/NOC of a credit 21, 31, 41, 51 // Return/NOC of a debit 26, 36, 46, 56 - if entry.ReturnOrNOC() != true { + if !entry.ReturnOrNOC() { msg := fmt.Sprintf(msgBatchTransactionCode, entry.TransactionCode, "COR") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} } From 0600c8ff5282368483a6add6090caa0ad447f0bc Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 8 Jun 2018 09:56:29 -0400 Subject: [PATCH 0153/1694] #153 Unreachable code # 153 Unreachable code --- entryDetail.go | 1 - 1 file changed, 1 deletion(-) diff --git a/entryDetail.go b/entryDetail.go index f8618cf76..ddcfc2537 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -386,7 +386,6 @@ func (ed *EntryDetail) ReturnOrNOC() bool { case "1", "6": return true default: - return false } return false } From eec9a022cdb34f10c25af5bab458a0095d5c0784 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 8 Jun 2018 10:08:26 -0400 Subject: [PATCH 0154/1694] # 153 Revert # 153 Revert --- batchCOR.go | 4 ++-- entryDetail.go | 25 +++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/batchCOR.go b/batchCOR.go index a54f542c2..3fc04e965 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -10,7 +10,7 @@ import ( // BatchCOR COR - Automated Notification of Change (NOC) or Refused Notification of Change // This Standard Entry Class Code is used by an RDFI or ODFI when originating a Notification of Change or Refused Notification of Change in automated format. -// A Notification of Change may be created by an RDFI to notify the ODFI that a posted Entry or Pre-notification Entry contains invalid or erroneous information and should be changed. +// A Notification of Change may be created by an RDFI to notify the ODFI that a posted Entry or Prenotification Entry contains invalid or erroneous information and should be changed. type BatchCOR struct { batch } @@ -56,7 +56,7 @@ func (batch *BatchCOR) Validate() error { // COR TransactionCode must be a Return or NOC transaction Code // Return/NOC of a credit 21, 31, 41, 51 // Return/NOC of a debit 26, 36, 46, 56 - if !entry.ReturnOrNOC() { + if entry.TransactionCodeDescription() != ReturnOrNoc { msg := fmt.Sprintf(msgBatchTransactionCode, entry.TransactionCode, "COR") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} } diff --git a/entryDetail.go b/entryDetail.go index ddcfc2537..1534a457b 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -93,6 +93,8 @@ const ( CategoryReturn = "Return" // CategoryNOC defines the entry as being a notification of change of a forward entry to the originating institution CategoryNOC = "NOC" + // ReturnOrNoc is the description for the following TransactionCode: 21, 31, 41, 51, 26, 36, 46, 56 + ReturnOrNoc = "RN" ) // NewEntryDetail returns a new EntryDetail with default values for non exported fields @@ -376,17 +378,16 @@ func (ed *EntryDetail) CreditOrDebit() string { return "" } -// ReturnOrNOC determines if transaction code is for a Return or NOC based on the second number +// TransactionCodeDescription determines the transaction code description based on the second number // in the TransactionCode -func (ed *EntryDetail) ReturnOrNOC() bool { - tc := strconv.Itoa(ed.TransactionCode) - // Take the second number in the TransactionCode - switch tc[1:2] { - // Return or NOC - case "1", "6": - return true - default: - } - return false +func (ed *EntryDetail) TransactionCodeDescription() string { + tc := strconv.Itoa(ed.TransactionCode) + // Take the second number in the TransactionCode + switch tc[1:2] { + // Return or NOC + case "1", "6": + return ReturnOrNoc + default: + } + return "" } - From 2c7911e0569c5bbfa68ffc4c59a77e473fb44b78 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 8 Jun 2018 12:07:33 -0400 Subject: [PATCH 0155/1694] #153 code comment typo #153 code comment typo --- batchARC.go | 2 +- batchPOP.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/batchARC.go b/batchARC.go index 1836b2f1d..98f5f6dc7 100644 --- a/batchARC.go +++ b/batchARC.go @@ -50,7 +50,7 @@ func (batch *BatchARC) Validate() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } - // RCK detail entries can only be a debit, ServiceClassCode must allow debits + // ARC detail entries can only be a debit, ServiceClassCode must allow debits switch batch.Header.ServiceClassCode { case 200, 220, 280: msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "ARC") diff --git a/batchPOP.go b/batchPOP.go index e150e2439..9ad97b084 100644 --- a/batchPOP.go +++ b/batchPOP.go @@ -52,7 +52,7 @@ func (batch *BatchPOP) Validate() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } - // RCK detail entries can only be a debit, ServiceClassCode must allow debits + // POP detail entries can only be a debit, ServiceClassCode must allow debits switch batch.Header.ServiceClassCode { case 200, 220, 280: msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "POP") From 5a38b84ed07c1600605c844dd2f0b29f2b69cfc2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 14:57:46 -0400 Subject: [PATCH 0156/1694] code refactor code refactor --- addenda05.go | 6 ------ addenda05_test.go | 30 +++++++++++++++++++++++------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/addenda05.go b/addenda05.go index aeb03bae2..570ee8f94 100644 --- a/addenda05.go +++ b/addenda05.go @@ -68,12 +68,6 @@ func (addenda05 *Addenda05) String() string { addenda05.EntryDetailSequenceNumberField()) } -// SetPaymentRelatedInformation allows additional information about the transaction -func (addenda05 *Addenda05) SetPaymentRelatedInformation(s string) *Addenda05 { - addenda05.PaymentRelatedInformation = s - return addenda05 -} - // Validate performs NACHA format rule checks on the record and returns an error if not Validated // The first error encountered is returned and stops that parsing. func (addenda05 *Addenda05) Validate() error { diff --git a/addenda05_test.go b/addenda05_test.go index 16624677b..2b9cbd657 100644 --- a/addenda05_test.go +++ b/addenda05_test.go @@ -27,6 +27,7 @@ func TestMockAddenda05(t *testing.T) { } } +// testParseAddenda05 parses an Addenda05 record for a PPD detail entry func testParseAddenda05(t testing.TB) { addendaPPD := NewAddenda05() var line = "705PPD DIEGO MAY 00010000001" @@ -34,16 +35,16 @@ func testParseAddenda05(t testing.TB) { r := NewReader(strings.NewReader(line)) - //Add a new BatchWEB + //Add a new BatchPPD r.addCurrentBatch(NewBatchPPD(mockBatchPPDHeader())) - //Add a WEB EntryDetail + //Add a PPDEntryDetail entryDetail := mockPPDEntryDetail() - //Add an addenda to the WEB EntryDetail + //Add an addenda to the PPD EntryDetail entryDetail.AddAddenda(addendaPPD) - // add the WEB entry detail to the batch + // add the PPD entry detail to the batch r.currentBatch.AddEntry(entryDetail) record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda05) @@ -52,7 +53,7 @@ func testParseAddenda05(t testing.TB) { t.Errorf("RecordType Expected '7' got: %v", record.recordType) } if record.TypeCode() != "05" { - t.Errorf("TypeCode Expected 10 got: %v", record.TypeCode()) + t.Errorf("TypeCode Expected 05 got: %v", record.TypeCode()) } if record.PaymentRelatedInformationField() != "PPD DIEGO MAY " { t.Errorf("PaymentRelatedInformation Expected 'PPD DIEGO MAY ' got: %v", record.PaymentRelatedInformationField()) @@ -65,10 +66,12 @@ func testParseAddenda05(t testing.TB) { } } +// TestParseAddenda05 tests parsing an Addenda05 record for a PPD detail entry func TestParseAddenda05(t *testing.T) { testParseAddenda05(t) } +// BenchmarkParseAddenda05 benchmarks parsing an Addenda05 record for a PPD detail entry func BenchmarkParseAddenda05(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -76,7 +79,7 @@ func BenchmarkParseAddenda05(b *testing.B) { } } -// TestAddenda05 String validates that a known parsed file can be return to a string of the same value +// testAddenda05String validates that a known parsed file can be return to a string of the same value func testAddenda05String(t testing.TB) { addenda05 := NewAddenda05() var line = "705WEB DIEGO MAY 00010000001" @@ -87,10 +90,12 @@ func testAddenda05String(t testing.TB) { } } +// TestAddenda05 String tests validating that a known parsed file can be return to a string of the same value func TestAddenda05String(t *testing.T) { testAddenda05String(t) } +// BenchmarkAddenda05 String benchmarks validating that a known parsed file can be return to a string of the same value func BenchmarkAddenda05String(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -158,7 +163,7 @@ func TestAddenda05PaymentRelatedInformationAlphaNumeric(t *testing.T) { } } -func TestAddenda05TyeCodeNil(t *testing.T) { +func testAddenda05TypeCodeNil(t testing.TB) { addenda05 := mockAddenda05() addenda05.typeCode = "" if err := addenda05.Validate(); err != nil { @@ -169,3 +174,14 @@ func TestAddenda05TyeCodeNil(t *testing.T) { } } } + +func TestAddenda05TypeCodeNil(t *testing.T) { + testAddenda05TypeCodeNil(t) +} + +func BenchmarkAddenda05TypeCodeNil(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda05TypeCodeNil(b) + } +} From 87bef41577f5dee428c5e6a3d0b400ec22c0d3aa Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 15:01:22 -0400 Subject: [PATCH 0157/1694] code refactor Fix comment Add covergae test fro RCK --- batchPOP.go | 2 +- batchRCK_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/batchPOP.go b/batchPOP.go index 9ad97b084..87a5f1f26 100644 --- a/batchPOP.go +++ b/batchPOP.go @@ -8,7 +8,7 @@ import "fmt" // BatchPOP holds the BatchHeader and BatchControl and all EntryDetail for POP Entries. // -// Point of Purchase. A check presented in-person to a merchant for purchase is presented +// Point-of-Purchase. A check presented in-person to a merchant for purchase is presented // as an ACH entry instead of a physical check. // // This ACH debit application is used by originators as a method of payment for the diff --git a/batchRCK_test.go b/batchRCK_test.go index 47296555c..cd4baf6cc 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -379,3 +379,30 @@ func BenchmarkBatchRCKAddendaCount(b *testing.B) { testBatchRCKAddendaCount(b) } } + +// testBatchRCKParseCheckSerialNumber validates BatchRCK create +func testBatchRCKParseCheckSerialNumber(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + checkSerialNumber := "123456789 " + if checkSerialNumber != mockBatch.GetEntries()[0].CheckSerialNumberField() { + t.Errorf("RecordType Expected '123456789' got: %v", mockBatch.GetEntries()[0].CheckSerialNumberField()) + } +} + +// TestBatchRCKParseCheckSerialNumber tests validating BatchRCK create +func TestBatchRCKParseCheckSerialNumber(t *testing.T) { + testBatchRCKParseCheckSerialNumber(t) +} + +// BenchmarkBatchRCKParseCheckSerialNumber benchmarks validating BatchRCK create +func BenchmarkBatchRCKParseCheckSerialNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKParseCheckSerialNumber(b) + } +} From 1229622d7399faa2374e57a567f7f04afafecabd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 15:12:11 -0400 Subject: [PATCH 0158/1694] code refactor Remove example directory from GO_FILES --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 13383adf2..9675d5e26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_install: - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo before_script: -- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/ | grep -v /example/) +- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/) script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES From b680dcc1b6cb2c0298696f8943beab9c4edc2bcd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 15:23:26 -0400 Subject: [PATCH 0159/1694] # 208 Support of SEC Code POS Add batchPOS Add addenda02 --- addenda02.go | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++ batch.go | 2 + batchPOS.go | 109 ++++++++++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 addenda02.go create mode 100644 batchPOS.go diff --git a/addenda02.go b/addenda02.go new file mode 100644 index 000000000..3ec93c598 --- /dev/null +++ b/addenda02.go @@ -0,0 +1,215 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" + "strings" +) + +// Addenda02 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 02 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +type Addenda02 struct { + //ToDo: Verify which fields should be omitempty + //ToDo: Add mor descriptive comments + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. entryAddenda02 Pos 7 + recordType string + // TypeCode Addenda02 types code '02' + typeCode string + // ReferenceInformationOne + ReferenceInformationOne string `json:"referenceInformationOne"` + // ReferenceInformationTwo + ReferenceInformationTwo string `json:"referenceInformationTwo"` + // TerminalIdentificationCode + TerminalIdentificationCode string `json:"terminalIdentificationCode"` + // TransactionSerialNumber + TransactionSerialNumber string `json:"transactionSerialNumber"` + // TransactionDate MMDD + TransactionDate string `json:"transactionDate"` + // TraceNumber matches the Entry Detail Trace Number of the entry being returned. + // AuthorizationCodeOrExpireDate + AuthorizationCodeOrExpireDate string `json:"authorizationCodeOrExpireDate"` + // Terminal Location + TerminalLocation string `json:"terminalLocation"` + // TerminalCity + TerminalCity string `json:"terminalCity"` + // TerminalState + TerminalState string `json:"terminalState"` + // TraceNumber matches the Entry Detail Trace Number of the entry being returned. + TraceNumber int `json:"traceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda02 returns a new Addenda02 with default values for none exported fields +func NewAddenda02() *Addenda02 { + addenda02 := new(Addenda02) + addenda02.recordType = "7" + addenda02.typeCode = "02" + return addenda02 +} + +// Parse takes the input record string and parses the Addenda02 values +func (addenda02 *Addenda02) Parse(record string) { + // 1-1 Always "7" + addenda02.recordType = "7" + // 2-3 Always 02 + addenda02.typeCode = record[1:3] + // 4-10 Based on the information entered (04-10) 7 alphanumeric + addenda02.ReferenceInformationOne = strings.TrimSpace(record[3:10]) + // 11-13 Based on the information entered (11-13) 3 alphanumeric + addenda02.ReferenceInformationTwo = strings.TrimSpace(record[10:13]) + // 14-19 + addenda02.TerminalIdentificationCode = strings.TrimSpace(record[13:19]) + // 20-25 + addenda02.TransactionSerialNumber = strings.TrimSpace(record[19:25]) + // 26-29 + addenda02.TransactionDate = strings.TrimSpace(record[25:29]) + // 30-35 + addenda02.AuthorizationCodeOrExpireDate = strings.TrimSpace(record[29:35]) + // 36-62 + addenda02.TerminalLocation = strings.TrimSpace(record[35:62]) + // 63-77 + addenda02.TerminalCity = strings.TrimSpace(record[62:77]) + // 78-79 + addenda02.TerminalState = strings.TrimSpace(record[77:79]) + // 80-94 + addenda02.TraceNumber = addenda02.parseNumField(record[79:94]) +} + +// String writes the Addenda02 struct to a 94 character string. +func (addenda02 *Addenda02) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v", + addenda02.recordType, + addenda02.typeCode, + addenda02.ReferenceInformationOneField(), + addenda02.ReferenceInformationTwoField(), + addenda02.TerminalIdentificationCodeField(), + addenda02.TransactionSerialNumberField(), + // ToDo: Follow up on best way to get TransactionDate - should it be treated as an alpha field + addenda02.TransactionDateField(), + addenda02.AuthorizationCodeOrExpireDateField(), + addenda02.TerminalLocationField(), + addenda02.TerminalCityField(), + addenda02.TerminalStateField(), + addenda02.TraceNumberField(), + ) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda02 *Addenda02) Validate() error { + if err := addenda02.fieldInclusion(); err != nil { + return err + } + if addenda02.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda02.recordType, Msg: msg} + } + if err := addenda02.isTypeCode(addenda02.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: err.Error()} + } + // Type Code must be 02 + // ToDo: Evaluate if Addenda05 and Addenda99 should be modified to validate on 05 and 99 + if addenda02.typeCode != "02" { + return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgAddendaTypeCode} + } + // ToDo: Validation for TransactionDate MMDD + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. + +// ToDo: check if we should do fieldInclusion or validate on required fields + +func (addenda02 *Addenda02) fieldInclusion() error { + if addenda02.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda02.recordType, Msg: msgFieldInclusion} + } + if addenda02.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgFieldInclusion} + } + if addenda02.TerminalIdentificationCode == "" { + return &FieldError{FieldName: "TerminalIdentificationCode", Value: addenda02.TerminalIdentificationCode, Msg: msgFieldInclusion} + } + if addenda02.TransactionSerialNumber == "" { + return &FieldError{FieldName: "TransactionSerialNumber", Value: addenda02.TransactionSerialNumber, Msg: msgFieldInclusion} + } + if addenda02.TransactionDate == "" { + return &FieldError{FieldName: "TransactionDate", Value: addenda02.TransactionDate, Msg: msgFieldInclusion} + } + if addenda02.TerminalLocation == "" { + return &FieldError{FieldName: "TerminalLocation", Value: addenda02.TerminalLocation, Msg: msgFieldInclusion} + } + if addenda02.TerminalCity == "" { + return &FieldError{FieldName: "TerminalCity", Value: addenda02.TerminalCity, Msg: msgFieldInclusion} + } + if addenda02.TerminalState == "" { + return &FieldError{FieldName: "TerminalState", Value: addenda02.TerminalState, Msg: msgFieldInclusion} + } + // ToDo: Determine if TraceNumber fieldInclusion is necessary + return nil +} + +// TypeCode Defines the specific explanation and format for the addenda02 information +func (addenda02 *Addenda02) TypeCode() string { + return addenda02.typeCode +} + +// ReferenceInformationOneField returns a space padded ReferenceInformationOne string +func (addenda02 *Addenda02) ReferenceInformationOneField() string { + return addenda02.alphaField(addenda02.ReferenceInformationOne, 7) +} + +// ReferenceInformationTwoField returns a space padded ReferenceInformationTwo string +func (addenda02 *Addenda02) ReferenceInformationTwoField() string { + return addenda02.alphaField(addenda02.ReferenceInformationOne, 3) +} + +// TerminalIdentificationCodeField returns a space padded TerminalIdentificationCode string +func (addenda02 *Addenda02) TerminalIdentificationCodeField() string { + return addenda02.alphaField(addenda02.TerminalIdentificationCode, 6) +} + +// TransactionSerialNumberField returns a zero padded TransactionSerialNumber string +func (addenda02 *Addenda02) TransactionSerialNumberField() string { + return addenda02.alphaField(addenda02.TransactionSerialNumber, 6) +} + +// TransactionDateField returns TransactionDate MMDD string +func (addenda02 *Addenda02) TransactionDateField() string { + //ToDo: see about padding + return addenda02.TransactionDate +} + +// AuthorizationCodeOrExpireDateField returns a space padded AuthorizationCodeOrExpireDate string +func (addenda02 *Addenda02) AuthorizationCodeOrExpireDateField() string { + return addenda02.alphaField(addenda02.AuthorizationCodeOrExpireDate, 6) +} + +//TerminalLocationField returns a space padded TerminalLocation string +func (addenda02 *Addenda02) TerminalLocationField() string { + return addenda02.alphaField(addenda02.TerminalLocation, 27) +} + +//TerminalCityField returns a space padded TerminalCity string +func (addenda02 *Addenda02) TerminalCityField() string { + return addenda02.alphaField(addenda02.TerminalCity, 15) +} + +//TerminalStateField returns a space padded TerminalState string +func (addenda02 *Addenda02) TerminalStateField() string { + return addenda02.alphaField(addenda02.TerminalState, 2) +} + +// TraceNumberField returns a space padded traceNumber string +func (addenda02 *Addenda02) TraceNumberField() string { + return addenda02.numericField(addenda02.TraceNumber, 15) +} diff --git a/batch.go b/batch.go index 6d9c74614..70915491e 100644 --- a/batch.go +++ b/batch.go @@ -37,6 +37,8 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { return NewBatchCOR(bh), nil case "POP": return NewBatchPOP(bh), nil + case "POS": + return NewBatchPOS(bh), nil case "PPD": return NewBatchPPD(bh), nil case "RCK": diff --git a/batchPOS.go b/batchPOS.go new file mode 100644 index 000000000..a1ace9c37 --- /dev/null +++ b/batchPOS.go @@ -0,0 +1,109 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "fmt" + +// BatchPOS holds the BatchHeader and BatchControl and all EntryDetail for POS Entries. +// +// A POS Entry is a debit Entry initiated at an “electronic terminal” to a Consumer +// Account of the Receiver to pay an obligation incurred in a point- of-sale +// transaction, or to effect a point-of-sale terminal cash withdrawal. +// +// Point-of-Sale Entries (POS) are ACH debit entries typically initiated by the use +// of a merchant-issued plastic card to pay an obligation at the point-of-sale. Much +// like a financial institution issued debit card, the merchant- issued debit card is +// swiped at the point-of-sale and approved for use; however, the authorization only +// verifies the card is open, active and within the card’s limits—it does not verify +// the Receiver’s account balance or debit the account at the time of the purchase. +// Settlement of the transaction moves from the card network to the ACH Network through +// the creation of a POS entry by the card issuer to debit the Receiver’s account. +type BatchPOS struct { + batch +} + +var msgBatchPOSAddenda = "found and 1 Addenda02 is required for SEC Type POS" + +// NewBatchPOS returns a *BatchPOS +func NewBatchPOS(bh *BatchHeader) *BatchPOS { + batch := new(BatchPOS) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchPOS) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // POS Addenda must be Addenda02 + if err := batch.isAddenda02(); err != nil { + return err + } + + // Add type specific validation. + if batch.Header.StandardEntryClassCode != "POS" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "POS") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + //ToDo; Determine if this is needed + /* // POS detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 220, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "POS") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + }*/ + + for _, entry := range batch.Entries { + // POS detail entries must be a debit + if entry.CreditOrDebit() != "D" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + //ToDo; Additional validations + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchPOS) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + return batch.Validate() +} + +// isAddenda02 verifies that a Addenda02 exists for each EntryDetail and is Validated +func (batch *BatchPOS) isAddenda02() error { + for _, entry := range batch.Entries { + // Addenda type must be equal to 1 + if len(entry.Addendum) != 1 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchPOSAddenda} + } + // Addenda type assertion must be Addenda02 + addenda02, ok := entry.Addendum[0].(*Addenda02) + if !ok { + msg := fmt.Sprintf(msgBatchCORAddendaType, entry.Addendum[0]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} + } + // Addenda02 must be Validated + if err := addenda02.Validate(); err != nil { + // convert the field error in to a batch error for a consistent api + if e, ok := err.(*FieldError); ok { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} + } + } + } + return nil +} From 9623959d230b8e4f2eae14d9ac705caa8b40bea9 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 15:49:42 -0400 Subject: [PATCH 0160/1694] #208 Support of SEC Code POS Add addenda02_test.go Add batchPOS_test.go --- addenda02_test.go | 32 +++++ batchPOS_test.go | 324 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 addenda02_test.go create mode 100644 batchPOS_test.go diff --git a/addenda02_test.go b/addenda02_test.go new file mode 100644 index 000000000..84d445b0b --- /dev/null +++ b/addenda02_test.go @@ -0,0 +1,32 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +func mockAddenda02() *Addenda02 { + addenda02 := NewAddenda02() + addenda02.ReferenceInformationOne = "REFONEA" + addenda02.ReferenceInformationTwo = "REF" + addenda02.TerminalIdentificationCode = "TERM02" + addenda02.TransactionSerialNumber = "100049" + addenda02.TransactionDate = "0612" + addenda02.AuthorizationCodeOrExpireDate = "123456" + addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalCity = "PHILADELPHIA" + addenda02.TerminalState = "PA" + addenda02.TraceNumber = 91012980000088 + return addenda02 +} + +func TestMockAddenda02(t *testing.T) { + addenda02 := mockAddenda02() + if err := addenda02.Validate(); err != nil { + t.Error("mockAddenda02 does not validate and will break other tests") + } +} + diff --git a/batchPOS_test.go b/batchPOS_test.go new file mode 100644 index 000000000..b7a3da28a --- /dev/null +++ b/batchPOS_test.go @@ -0,0 +1,324 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +// mockBatchPOSHeader creates a BatchPOS BatchHeader +func mockBatchPOSHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "POS" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH POS" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockPOSEntryDetail creates a BatchPOS EntryDetail +func mockPOSEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + return entry +} + +// mockBatchPOS creates a BatchPOS +func mockBatchPOS() *BatchPOS { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// mockBatchPOSHeaderCredit creates a BatchPOS BatchHeader +func mockBatchPOSHeaderCredit() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "POS" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "POS" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockPOSEntryDetailCredit creates a POS EntryDetail with a credit entry +func mockPOSEntryDetailCredit() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetCheckSerialNumber("123456789") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchPOSCredit creates a BatchPOS with a Credit entry +func mockBatchPOSCredit() *BatchPOS { + mockBatch := NewBatchPOS(mockBatchPOSHeaderCredit()) + mockBatch.AddEntry(mockPOSEntryDetailCredit()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + return mockBatch +} + +// testBatchPOSHeader creates a BatchPOS BatchHeader +func testBatchPOSHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchPOSHeader()) + err, ok := batch.(*BatchPOS) + if !ok { + t.Errorf("Expecting BatchPOS got %T", err) + } +} + +// TestBatchPOSHeader tests validating BatchPOS BatchHeader +func TestBatchPOSHeader(t *testing.T) { + testBatchPOSHeader(t) +} + +// BenchmarkBatchPOSHeader benchmarks validating BatchPOS BatchHeader +func BenchmarkBatchPOSHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSHeader(b) + } +} + +// testBatchPOSCreate validates BatchPOS create +func testBatchPOSCreate(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchPOSCreate tests validating BatchPOS create +func TestBatchPOSCreate(t *testing.T) { + testBatchPOSCreate(t) +} + +// BenchmarkBatchPOSCreate benchmarks validating BatchPOS create +func BenchmarkBatchPOSCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSCreate(b) + } +} + +// testBatchPOSStandardEntryClassCode validates BatchPOS create for an invalid StandardEntryClassCode +func testBatchPOSStandardEntryClassCode(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSStandardEntryClassCode tests validating BatchPOS create for an invalid StandardEntryClassCode +func TestBatchPOSStandardEntryClassCode(t *testing.T) { + testBatchPOSStandardEntryClassCode(t) +} + +// BenchmarkBatchPOSStandardEntryClassCode benchmarks validating BatchPOS create for an invalid StandardEntryClassCode +func BenchmarkBatchPOSStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSStandardEntryClassCode(b) + } +} + +// testBatchPOSServiceClassCodeEquality validates service class code equality +func testBatchPOSServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchPPD() + mockBatch.GetControl().ServiceClassCode = 220 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSServiceClassCodeEquality tests validating service class code equality +func TestBatchPOSServiceClassCodeEquality(t *testing.T) { + testBatchPOSServiceClassCodeEquality(t) +} + +// BenchmarkBatchPOSServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchPOSServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSServiceClassCodeEquality(b) + } +} + +// testBatchPOSTransactionCode validates BatchPOS TransactionCode is not a credit +func testBatchPOSTransactionCode(t testing.TB) { + mockBatch := mockBatchPOSCredit() + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSTransactionCode tests validating BatchPOS TransactionCode is not a credit +func TestBatchPOSTransactionCode(t *testing.T) { + testBatchPOSTransactionCode(t) +} + +// BenchmarkBatchPOSTransactionCode benchmarks validating BatchPOS TransactionCode is not a credit +func BenchmarkBatchPOSTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSTransactionCode(b) + } +} + +// testBatchPOSAddendaCount validates BatchPOS Addendum count of 2 +func testBatchPOSAddendaCount(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSAddendaCount tests validating BatchPOS Addendum count of 2 +func TestBatchPOSAddendaCount(t *testing.T) { + testBatchPOSAddendaCount(t) +} + +// BenchmarkBatchPOSAddendaCount benchmarks validating BatchPOS Addendum count of 2 +func BenchmarkBatchPOSAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSAddendaCount(b) + } +} + +// testBatchPOSAddendaCountZero validates Addendum count of 0 +func testBatchPOSAddendaCountZero(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSAddendaCountZero tests validating Addendum count of 0 +func TestBatchPOSAddendaCountZero(t *testing.T) { + testBatchPOSAddendaCountZero(t) +} + +// BenchmarkBatchPOSAddendaCountZero benchmarks validating Addendum count of 0 +func BenchmarkBatchPOSAddendaCountZero(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSAddendaCountZero(b) + } +} + +// testBatchPOSInvalidAddendum validates Addendum must be Addenda02 +func testBatchPOSInvalidAddendum(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSInvalidAddendum tests validating Addendum must be Addenda02 +func TestBatchPOSInvalidAddendum(t *testing.T) { + testBatchPOSInvalidAddendum(t) +} + +// BenchmarkBatchPOSInvalidAddendum benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchPOSInvalidAddendum(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSInvalidAddendum(b) + } +} + +// testBatchPOSInvalidAddenda validates Addendum must be Addenda02 +func testBatchPOSInvalidAddenda(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + addenda02 := mockAddenda02() + addenda02.recordType = "63" + mockBatch.GetEntries()[0].AddAddenda(addenda02) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSInvalidAddenda tests validating Addendum must be Addenda02 +func TestBatchPOSInvalidAddenda(t *testing.T) { + testBatchPOSInvalidAddenda(t) +} + +// BenchmarkBatchPOSInvalidAddenda benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSInvalidAddenda(b) + } +} From 62d35ec164a58e858111588163619caaf6926a90 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 16:01:50 -0400 Subject: [PATCH 0161/1694] #208 tests #208 tests --- addenda02_test.go | 10 -- batchPOS_test.go | 233 ---------------------------------------------- 2 files changed, 243 deletions(-) diff --git a/addenda02_test.go b/addenda02_test.go index 84d445b0b..8bc6f451b 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -4,10 +4,6 @@ package ach -import ( - "testing" -) - func mockAddenda02() *Addenda02 { addenda02 := NewAddenda02() addenda02.ReferenceInformationOne = "REFONEA" @@ -23,10 +19,4 @@ func mockAddenda02() *Addenda02 { return addenda02 } -func TestMockAddenda02(t *testing.T) { - addenda02 := mockAddenda02() - if err := addenda02.Validate(); err != nil { - t.Error("mockAddenda02 does not validate and will break other tests") - } -} diff --git a/batchPOS_test.go b/batchPOS_test.go index b7a3da28a..49c6b477a 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -44,39 +44,6 @@ func mockBatchPOS() *BatchPOS { return mockBatch } -// mockBatchPOSHeaderCredit creates a BatchPOS BatchHeader -func mockBatchPOSHeaderCredit() *BatchHeader { - bh := NewBatchHeader() - bh.ServiceClassCode = 225 - bh.StandardEntryClassCode = "POS" - bh.CompanyName = "Payee Name" - bh.CompanyIdentification = "121042882" - bh.CompanyEntryDescription = "POS" - bh.ODFIIdentification = "12104288" - return bh -} - -// mockPOSEntryDetailCredit creates a POS EntryDetail with a credit entry -func mockPOSEntryDetailCredit() *EntryDetail { - entry := NewEntryDetail() - entry.TransactionCode = 22 - entry.SetRDFI("231380104") - entry.DFIAccountNumber = "744-5678-99" - entry.Amount = 25000 - entry.SetCheckSerialNumber("123456789") - entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) - entry.Category = CategoryForward - return entry -} - -// mockBatchPOSCredit creates a BatchPOS with a Credit entry -func mockBatchPOSCredit() *BatchPOS { - mockBatch := NewBatchPOS(mockBatchPOSHeaderCredit()) - mockBatch.AddEntry(mockPOSEntryDetailCredit()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - return mockBatch -} // testBatchPOSHeader creates a BatchPOS BatchHeader func testBatchPOSHeader(t testing.TB) { @@ -122,203 +89,3 @@ func BenchmarkBatchPOSCreate(b *testing.B) { } } -// testBatchPOSStandardEntryClassCode validates BatchPOS create for an invalid StandardEntryClassCode -func testBatchPOSStandardEntryClassCode(t testing.TB) { - mockBatch := mockBatchPOS() - mockBatch.Header.StandardEntryClassCode = "WEB" - mockBatch.Create() - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "StandardEntryClassCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSStandardEntryClassCode tests validating BatchPOS create for an invalid StandardEntryClassCode -func TestBatchPOSStandardEntryClassCode(t *testing.T) { - testBatchPOSStandardEntryClassCode(t) -} - -// BenchmarkBatchPOSStandardEntryClassCode benchmarks validating BatchPOS create for an invalid StandardEntryClassCode -func BenchmarkBatchPOSStandardEntryClassCode(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSStandardEntryClassCode(b) - } -} - -// testBatchPOSServiceClassCodeEquality validates service class code equality -func testBatchPOSServiceClassCodeEquality(t testing.TB) { - mockBatch := mockBatchPPD() - mockBatch.GetControl().ServiceClassCode = 220 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "ServiceClassCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSServiceClassCodeEquality tests validating service class code equality -func TestBatchPOSServiceClassCodeEquality(t *testing.T) { - testBatchPOSServiceClassCodeEquality(t) -} - -// BenchmarkBatchPOSServiceClassCodeEquality benchmarks validating service class code equality -func BenchmarkBatchPOSServiceClassCodeEquality(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSServiceClassCodeEquality(b) - } -} - -// testBatchPOSTransactionCode validates BatchPOS TransactionCode is not a credit -func testBatchPOSTransactionCode(t testing.TB) { - mockBatch := mockBatchPOSCredit() - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "TransactionCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSTransactionCode tests validating BatchPOS TransactionCode is not a credit -func TestBatchPOSTransactionCode(t *testing.T) { - testBatchPOSTransactionCode(t) -} - -// BenchmarkBatchPOSTransactionCode benchmarks validating BatchPOS TransactionCode is not a credit -func BenchmarkBatchPOSTransactionCode(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSTransactionCode(b) - } -} - -// testBatchPOSAddendaCount validates BatchPOS Addendum count of 2 -func testBatchPOSAddendaCount(t testing.TB) { - mockBatch := mockBatchPOS() - mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - mockBatch.Create() - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "Addendum" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSAddendaCount tests validating BatchPOS Addendum count of 2 -func TestBatchPOSAddendaCount(t *testing.T) { - testBatchPOSAddendaCount(t) -} - -// BenchmarkBatchPOSAddendaCount benchmarks validating BatchPOS Addendum count of 2 -func BenchmarkBatchPOSAddendaCount(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSAddendaCount(b) - } -} - -// testBatchPOSAddendaCountZero validates Addendum count of 0 -func testBatchPOSAddendaCountZero(t testing.TB) { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "Addendum" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSAddendaCountZero tests validating Addendum count of 0 -func TestBatchPOSAddendaCountZero(t *testing.T) { - testBatchPOSAddendaCountZero(t) -} - -// BenchmarkBatchPOSAddendaCountZero benchmarks validating Addendum count of 0 -func BenchmarkBatchPOSAddendaCountZero(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSAddendaCountZero(b) - } -} - -// testBatchPOSInvalidAddendum validates Addendum must be Addenda02 -func testBatchPOSInvalidAddendum(t testing.TB) { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "Addendum" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSInvalidAddendum tests validating Addendum must be Addenda02 -func TestBatchPOSInvalidAddendum(t *testing.T) { - testBatchPOSInvalidAddendum(t) -} - -// BenchmarkBatchPOSInvalidAddendum benchmarks validating Addendum must be Addenda02 -func BenchmarkBatchPOSInvalidAddendum(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSInvalidAddendum(b) - } -} - -// testBatchPOSInvalidAddenda validates Addendum must be Addenda02 -func testBatchPOSInvalidAddenda(t testing.TB) { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - addenda02 := mockAddenda02() - addenda02.recordType = "63" - mockBatch.GetEntries()[0].AddAddenda(addenda02) - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSInvalidAddenda tests validating Addendum must be Addenda02 -func TestBatchPOSInvalidAddenda(t *testing.T) { - testBatchPOSInvalidAddenda(t) -} - -// BenchmarkBatchPOSInvalidAddenda benchmarks validating Addendum must be Addenda02 -func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSInvalidAddenda(b) - } -} From 8a162f77671f3cd9a03fc059e23f0800cad9712c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 16:09:47 -0400 Subject: [PATCH 0162/1694] #208 #208 --- addenda02_test.go | 22 ------------ batchPOS_test.go | 91 ----------------------------------------------- 2 files changed, 113 deletions(-) delete mode 100644 addenda02_test.go delete mode 100644 batchPOS_test.go diff --git a/addenda02_test.go b/addenda02_test.go deleted file mode 100644 index 8bc6f451b..000000000 --- a/addenda02_test.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018 The ACH Authors -// Use of this source code is governed by an Apache License -// license that can be found in the LICENSE file. - -package ach - -func mockAddenda02() *Addenda02 { - addenda02 := NewAddenda02() - addenda02.ReferenceInformationOne = "REFONEA" - addenda02.ReferenceInformationTwo = "REF" - addenda02.TerminalIdentificationCode = "TERM02" - addenda02.TransactionSerialNumber = "100049" - addenda02.TransactionDate = "0612" - addenda02.AuthorizationCodeOrExpireDate = "123456" - addenda02.TerminalLocation = "Target Store 0049" - addenda02.TerminalCity = "PHILADELPHIA" - addenda02.TerminalState = "PA" - addenda02.TraceNumber = 91012980000088 - return addenda02 -} - - diff --git a/batchPOS_test.go b/batchPOS_test.go deleted file mode 100644 index 49c6b477a..000000000 --- a/batchPOS_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018 The ACH Authors -// Use of this source code is governed by an Apache License -// license that can be found in the LICENSE file. - -package ach - -import "testing" - -// mockBatchPOSHeader creates a BatchPOS BatchHeader -func mockBatchPOSHeader() *BatchHeader { - bh := NewBatchHeader() - bh.ServiceClassCode = 225 - bh.StandardEntryClassCode = "POS" - bh.CompanyName = "Payee Name" - bh.CompanyIdentification = "121042882" - bh.CompanyEntryDescription = "ACH POS" - bh.ODFIIdentification = "12104288" - return bh -} - -// mockPOSEntryDetail creates a BatchPOS EntryDetail -func mockPOSEntryDetail() *EntryDetail { - entry := NewEntryDetail() - entry.TransactionCode = 27 - entry.SetRDFI("231380104") - entry.DFIAccountNumber = "744-5678-99" - entry.Amount = 25000 - entry.IdentificationNumber = "45689033" - entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) - entry.DiscretionaryData = "01" - entry.Category = CategoryForward - return entry -} - -// mockBatchPOS creates a BatchPOS -func mockBatchPOS() *BatchPOS { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - if err := mockBatch.Create(); err != nil { - panic(err) - } - return mockBatch -} - - -// testBatchPOSHeader creates a BatchPOS BatchHeader -func testBatchPOSHeader(t testing.TB) { - batch, _ := NewBatch(mockBatchPOSHeader()) - err, ok := batch.(*BatchPOS) - if !ok { - t.Errorf("Expecting BatchPOS got %T", err) - } -} - -// TestBatchPOSHeader tests validating BatchPOS BatchHeader -func TestBatchPOSHeader(t *testing.T) { - testBatchPOSHeader(t) -} - -// BenchmarkBatchPOSHeader benchmarks validating BatchPOS BatchHeader -func BenchmarkBatchPOSHeader(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSHeader(b) - } -} - -// testBatchPOSCreate validates BatchPOS create -func testBatchPOSCreate(t testing.TB) { - mockBatch := mockBatchPOS() - mockBatch.Create() - if err := mockBatch.Validate(); err != nil { - t.Errorf("%T: %s", err, err) - } -} - -// TestBatchPOSCreate tests validating BatchPOS create -func TestBatchPOSCreate(t *testing.T) { - testBatchPOSCreate(t) -} - -// BenchmarkBatchPOSCreate benchmarks validating BatchPOS create -func BenchmarkBatchPOSCreate(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSCreate(b) - } -} - From 5a6e19d4d48c02eefd0d36d9c1ace4d276522d45 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:18:39 -0400 Subject: [PATCH 0163/1694] #280 Add addenda02_test.go Add addenda02_test.go --- addenda02_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 addenda02_test.go diff --git a/addenda02_test.go b/addenda02_test.go new file mode 100644 index 000000000..84d445b0b --- /dev/null +++ b/addenda02_test.go @@ -0,0 +1,32 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +func mockAddenda02() *Addenda02 { + addenda02 := NewAddenda02() + addenda02.ReferenceInformationOne = "REFONEA" + addenda02.ReferenceInformationTwo = "REF" + addenda02.TerminalIdentificationCode = "TERM02" + addenda02.TransactionSerialNumber = "100049" + addenda02.TransactionDate = "0612" + addenda02.AuthorizationCodeOrExpireDate = "123456" + addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalCity = "PHILADELPHIA" + addenda02.TerminalState = "PA" + addenda02.TraceNumber = 91012980000088 + return addenda02 +} + +func TestMockAddenda02(t *testing.T) { + addenda02 := mockAddenda02() + if err := addenda02.Validate(); err != nil { + t.Error("mockAddenda02 does not validate and will break other tests") + } +} + From 9f80d9369ca35ece1db559bcd5409d93243663a0 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:24:31 -0400 Subject: [PATCH 0164/1694] change strings change strings --- addenda02_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addenda02_test.go b/addenda02_test.go index 84d445b0b..cd32fe236 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -10,13 +10,13 @@ import ( func mockAddenda02() *Addenda02 { addenda02 := NewAddenda02() - addenda02.ReferenceInformationOne = "REFONEA" - addenda02.ReferenceInformationTwo = "REF" - addenda02.TerminalIdentificationCode = "TERM02" + addenda02.ReferenceInformationOne = "AB99EFG" + addenda02.ReferenceInformationTwo = "ABC" + addenda02.TerminalIdentificationCode = "AB9902" addenda02.TransactionSerialNumber = "100049" addenda02.TransactionDate = "0612" addenda02.AuthorizationCodeOrExpireDate = "123456" - addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalLocation = "Anyway Store 0049" addenda02.TerminalCity = "PHILADELPHIA" addenda02.TerminalState = "PA" addenda02.TraceNumber = 91012980000088 From d1b68aeceb9dfaaa6b6849bbb055b03dc0ea391e Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:31:42 -0400 Subject: [PATCH 0165/1694] test build test build --- addenda02_test.go | 32 ----- batchPOS_test.go | 339 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 32 deletions(-) delete mode 100644 addenda02_test.go create mode 100644 batchPOS_test.go diff --git a/addenda02_test.go b/addenda02_test.go deleted file mode 100644 index cd32fe236..000000000 --- a/addenda02_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018 The ACH Authors -// Use of this source code is governed by an Apache License -// license that can be found in the LICENSE file. - -package ach - -import ( - "testing" -) - -func mockAddenda02() *Addenda02 { - addenda02 := NewAddenda02() - addenda02.ReferenceInformationOne = "AB99EFG" - addenda02.ReferenceInformationTwo = "ABC" - addenda02.TerminalIdentificationCode = "AB9902" - addenda02.TransactionSerialNumber = "100049" - addenda02.TransactionDate = "0612" - addenda02.AuthorizationCodeOrExpireDate = "123456" - addenda02.TerminalLocation = "Anyway Store 0049" - addenda02.TerminalCity = "PHILADELPHIA" - addenda02.TerminalState = "PA" - addenda02.TraceNumber = 91012980000088 - return addenda02 -} - -func TestMockAddenda02(t *testing.T) { - addenda02 := mockAddenda02() - if err := addenda02.Validate(); err != nil { - t.Error("mockAddenda02 does not validate and will break other tests") - } -} - diff --git a/batchPOS_test.go b/batchPOS_test.go new file mode 100644 index 000000000..f73f2d7b9 --- /dev/null +++ b/batchPOS_test.go @@ -0,0 +1,339 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +func mockAddenda02() *Addenda02 { + addenda02 := NewAddenda02() + addenda02.ReferenceInformationOne = "REFONEA" + addenda02.ReferenceInformationTwo = "REF" + addenda02.TerminalIdentificationCode = "TERM02" + addenda02.TransactionSerialNumber = "100049" + addenda02.TransactionDate = "0612" + addenda02.AuthorizationCodeOrExpireDate = "123456" + addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalCity = "PHILADELPHIA" + addenda02.TerminalState = "PA" + addenda02.TraceNumber = 91012980000088 + return addenda02 +} + +// mockBatchPOSHeader creates a BatchPOS BatchHeader +func mockBatchPOSHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "POS" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH POS" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockPOSEntryDetail creates a BatchPOS EntryDetail +func mockPOSEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + return entry +} + +// mockBatchPOS creates a BatchPOS +func mockBatchPOS() *BatchPOS { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// mockBatchPOSHeaderCredit creates a BatchPOS BatchHeader +func mockBatchPOSHeaderCredit() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "POS" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "POS" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockPOSEntryDetailCredit creates a POS EntryDetail with a credit entry +func mockPOSEntryDetailCredit() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetCheckSerialNumber("123456789") + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) + entry.Category = CategoryForward + return entry +} + +// mockBatchPOSCredit creates a BatchPOS with a Credit entry +func mockBatchPOSCredit() *BatchPOS { + mockBatch := NewBatchPOS(mockBatchPOSHeaderCredit()) + mockBatch.AddEntry(mockPOSEntryDetailCredit()) + //mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + return mockBatch +} + +// testBatchPOSHeader creates a BatchPOS BatchHeader +func testBatchPOSHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchPOSHeader()) + err, ok := batch.(*BatchPOS) + if !ok { + t.Errorf("Expecting BatchPOS got %T", err) + } +} + +// TestBatchPOSHeader tests validating BatchPOS BatchHeader +func TestBatchPOSHeader(t *testing.T) { + testBatchPOSHeader(t) +} + +// BenchmarkBatchPOSHeader benchmarks validating BatchPOS BatchHeader +func BenchmarkBatchPOSHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSHeader(b) + } +} + +// testBatchPOSCreate validates BatchPOS create +func testBatchPOSCreate(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchPOSCreate tests validating BatchPOS create +func TestBatchPOSCreate(t *testing.T) { + testBatchPOSCreate(t) +} + +// BenchmarkBatchPOSCreate benchmarks validating BatchPOS create +func BenchmarkBatchPOSCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSCreate(b) + } +} + +// testBatchPOSStandardEntryClassCode validates BatchPOS create for an invalid StandardEntryClassCode +func testBatchPOSStandardEntryClassCode(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSStandardEntryClassCode tests validating BatchPOS create for an invalid StandardEntryClassCode +func TestBatchPOSStandardEntryClassCode(t *testing.T) { + testBatchPOSStandardEntryClassCode(t) +} + +// BenchmarkBatchPOSStandardEntryClassCode benchmarks validating BatchPOS create for an invalid StandardEntryClassCode +func BenchmarkBatchPOSStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSStandardEntryClassCode(b) + } +} + +// testBatchPOSServiceClassCodeEquality validates service class code equality +func testBatchPOSServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchPPD() + mockBatch.GetControl().ServiceClassCode = 220 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSServiceClassCodeEquality tests validating service class code equality +func TestBatchPOSServiceClassCodeEquality(t *testing.T) { + testBatchPOSServiceClassCodeEquality(t) +} + +// BenchmarkBatchPOSServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchPOSServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSServiceClassCodeEquality(b) + } +} + +/*// testBatchPOSTransactionCode validates BatchPOS TransactionCode is not a credit +func testBatchPOSTransactionCode(t testing.TB) { + mockBatch := mockBatchPOSCredit() + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSTransactionCode tests validating BatchPOS TransactionCode is not a credit +func TestBatchPOSTransactionCode(t *testing.T) { + testBatchPOSTransactionCode(t) +} + +// BenchmarkBatchPOSTransactionCode benchmarks validating BatchPOS TransactionCode is not a credit +func BenchmarkBatchPOSTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSTransactionCode(b) + } +}*/ + +// testBatchPOSAddendaCount validates BatchPOS Addendum count of 2 +func testBatchPOSAddendaCount(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSAddendaCount tests validating BatchPOS Addendum count of 2 +func TestBatchPOSAddendaCount(t *testing.T) { + testBatchPOSAddendaCount(t) +} + +// BenchmarkBatchPOSAddendaCount benchmarks validating BatchPOS Addendum count of 2 +func BenchmarkBatchPOSAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSAddendaCount(b) + } +} + +// testBatchPOSAddendaCountZero validates Addendum count of 0 +func testBatchPOSAddendaCountZero(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSAddendaCountZero tests validating Addendum count of 0 +func TestBatchPOSAddendaCountZero(t *testing.T) { + testBatchPOSAddendaCountZero(t) +} + +// BenchmarkBatchPOSAddendaCountZero benchmarks validating Addendum count of 0 +func BenchmarkBatchPOSAddendaCountZero(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSAddendaCountZero(b) + } +} + +// testBatchPOSInvalidAddendum validates Addendum must be Addenda02 +func testBatchPOSInvalidAddendum(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSInvalidAddendum tests validating Addendum must be Addenda02 +func TestBatchPOSInvalidAddendum(t *testing.T) { + testBatchPOSInvalidAddendum(t) +} + +// BenchmarkBatchPOSInvalidAddendum benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchPOSInvalidAddendum(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSInvalidAddendum(b) + } +} + +// testBatchPOSInvalidAddenda validates Addendum must be Addenda02 +func testBatchPOSInvalidAddenda(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + addenda02 := mockAddenda02() + addenda02.recordType = "63" + mockBatch.GetEntries()[0].AddAddenda(addenda02) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSInvalidAddenda tests validating Addendum must be Addenda02 +func TestBatchPOSInvalidAddenda(t *testing.T) { + testBatchPOSInvalidAddenda(t) +} + +// BenchmarkBatchPOSInvalidAddenda benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSInvalidAddenda(b) + } +} From fbb60afb27f8d8e5cb854a667a74b9d106df3371 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:37:03 -0400 Subject: [PATCH 0166/1694] build error build error --- batchPOS_test.go | 255 ----------------------------------------------- 1 file changed, 255 deletions(-) diff --git a/batchPOS_test.go b/batchPOS_test.go index f73f2d7b9..28f38d7fe 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -59,39 +59,7 @@ func mockBatchPOS() *BatchPOS { return mockBatch } -// mockBatchPOSHeaderCredit creates a BatchPOS BatchHeader -func mockBatchPOSHeaderCredit() *BatchHeader { - bh := NewBatchHeader() - bh.ServiceClassCode = 225 - bh.StandardEntryClassCode = "POS" - bh.CompanyName = "Payee Name" - bh.CompanyIdentification = "121042882" - bh.CompanyEntryDescription = "POS" - bh.ODFIIdentification = "12104288" - return bh -} - -// mockPOSEntryDetailCredit creates a POS EntryDetail with a credit entry -func mockPOSEntryDetailCredit() *EntryDetail { - entry := NewEntryDetail() - entry.TransactionCode = 22 - entry.SetRDFI("231380104") - entry.DFIAccountNumber = "744-5678-99" - entry.Amount = 25000 - entry.SetCheckSerialNumber("123456789") - entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) - entry.Category = CategoryForward - return entry -} -// mockBatchPOSCredit creates a BatchPOS with a Credit entry -func mockBatchPOSCredit() *BatchPOS { - mockBatch := NewBatchPOS(mockBatchPOSHeaderCredit()) - mockBatch.AddEntry(mockPOSEntryDetailCredit()) - //mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - return mockBatch -} // testBatchPOSHeader creates a BatchPOS BatchHeader func testBatchPOSHeader(t testing.TB) { @@ -114,226 +82,3 @@ func BenchmarkBatchPOSHeader(b *testing.B) { testBatchPOSHeader(b) } } - -// testBatchPOSCreate validates BatchPOS create -func testBatchPOSCreate(t testing.TB) { - mockBatch := mockBatchPOS() - mockBatch.Create() - if err := mockBatch.Validate(); err != nil { - t.Errorf("%T: %s", err, err) - } -} - -// TestBatchPOSCreate tests validating BatchPOS create -func TestBatchPOSCreate(t *testing.T) { - testBatchPOSCreate(t) -} - -// BenchmarkBatchPOSCreate benchmarks validating BatchPOS create -func BenchmarkBatchPOSCreate(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSCreate(b) - } -} - -// testBatchPOSStandardEntryClassCode validates BatchPOS create for an invalid StandardEntryClassCode -func testBatchPOSStandardEntryClassCode(t testing.TB) { - mockBatch := mockBatchPOS() - mockBatch.Header.StandardEntryClassCode = "WEB" - mockBatch.Create() - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "StandardEntryClassCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSStandardEntryClassCode tests validating BatchPOS create for an invalid StandardEntryClassCode -func TestBatchPOSStandardEntryClassCode(t *testing.T) { - testBatchPOSStandardEntryClassCode(t) -} - -// BenchmarkBatchPOSStandardEntryClassCode benchmarks validating BatchPOS create for an invalid StandardEntryClassCode -func BenchmarkBatchPOSStandardEntryClassCode(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSStandardEntryClassCode(b) - } -} - -// testBatchPOSServiceClassCodeEquality validates service class code equality -func testBatchPOSServiceClassCodeEquality(t testing.TB) { - mockBatch := mockBatchPPD() - mockBatch.GetControl().ServiceClassCode = 220 - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "ServiceClassCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSServiceClassCodeEquality tests validating service class code equality -func TestBatchPOSServiceClassCodeEquality(t *testing.T) { - testBatchPOSServiceClassCodeEquality(t) -} - -// BenchmarkBatchPOSServiceClassCodeEquality benchmarks validating service class code equality -func BenchmarkBatchPOSServiceClassCodeEquality(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSServiceClassCodeEquality(b) - } -} - -/*// testBatchPOSTransactionCode validates BatchPOS TransactionCode is not a credit -func testBatchPOSTransactionCode(t testing.TB) { - mockBatch := mockBatchPOSCredit() - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "TransactionCode" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSTransactionCode tests validating BatchPOS TransactionCode is not a credit -func TestBatchPOSTransactionCode(t *testing.T) { - testBatchPOSTransactionCode(t) -} - -// BenchmarkBatchPOSTransactionCode benchmarks validating BatchPOS TransactionCode is not a credit -func BenchmarkBatchPOSTransactionCode(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSTransactionCode(b) - } -}*/ - -// testBatchPOSAddendaCount validates BatchPOS Addendum count of 2 -func testBatchPOSAddendaCount(t testing.TB) { - mockBatch := mockBatchPOS() - mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - mockBatch.Create() - if err := mockBatch.Validate(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "Addendum" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSAddendaCount tests validating BatchPOS Addendum count of 2 -func TestBatchPOSAddendaCount(t *testing.T) { - testBatchPOSAddendaCount(t) -} - -// BenchmarkBatchPOSAddendaCount benchmarks validating BatchPOS Addendum count of 2 -func BenchmarkBatchPOSAddendaCount(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSAddendaCount(b) - } -} - -// testBatchPOSAddendaCountZero validates Addendum count of 0 -func testBatchPOSAddendaCountZero(t testing.TB) { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "Addendum" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSAddendaCountZero tests validating Addendum count of 0 -func TestBatchPOSAddendaCountZero(t *testing.T) { - testBatchPOSAddendaCountZero(t) -} - -// BenchmarkBatchPOSAddendaCountZero benchmarks validating Addendum count of 0 -func BenchmarkBatchPOSAddendaCountZero(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSAddendaCountZero(b) - } -} - -// testBatchPOSInvalidAddendum validates Addendum must be Addenda02 -func testBatchPOSInvalidAddendum(t testing.TB) { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "Addendum" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSInvalidAddendum tests validating Addendum must be Addenda02 -func TestBatchPOSInvalidAddendum(t *testing.T) { - testBatchPOSInvalidAddendum(t) -} - -// BenchmarkBatchPOSInvalidAddendum benchmarks validating Addendum must be Addenda02 -func BenchmarkBatchPOSInvalidAddendum(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSInvalidAddendum(b) - } -} - -// testBatchPOSInvalidAddenda validates Addendum must be Addenda02 -func testBatchPOSInvalidAddenda(t testing.TB) { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - addenda02 := mockAddenda02() - addenda02.recordType = "63" - mockBatch.GetEntries()[0].AddAddenda(addenda02) - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*BatchError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOSInvalidAddenda tests validating Addendum must be Addenda02 -func TestBatchPOSInvalidAddenda(t *testing.T) { - testBatchPOSInvalidAddenda(t) -} - -// BenchmarkBatchPOSInvalidAddenda benchmarks validating Addendum must be Addenda02 -func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOSInvalidAddenda(b) - } -} From 39774f4f523285db7ce1428ee2fb177539ca1a2c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:38:46 -0400 Subject: [PATCH 0167/1694] gofmt gofmt --- batchPOS_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/batchPOS_test.go b/batchPOS_test.go index 28f38d7fe..50a7c7e9c 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -59,8 +59,6 @@ func mockBatchPOS() *BatchPOS { return mockBatch } - - // testBatchPOSHeader creates a BatchPOS BatchHeader func testBatchPOSHeader(t testing.TB) { batch, _ := NewBatch(mockBatchPOSHeader()) From c24e9e29162d989afd2347f6fdf0813e9da9f252 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:43:20 -0400 Subject: [PATCH 0168/1694] build fail build fail --- batchPOS_test.go | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/batchPOS_test.go b/batchPOS_test.go index 50a7c7e9c..f97e31941 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -6,21 +6,6 @@ package ach import "testing" -func mockAddenda02() *Addenda02 { - addenda02 := NewAddenda02() - addenda02.ReferenceInformationOne = "REFONEA" - addenda02.ReferenceInformationTwo = "REF" - addenda02.TerminalIdentificationCode = "TERM02" - addenda02.TransactionSerialNumber = "100049" - addenda02.TransactionDate = "0612" - addenda02.AuthorizationCodeOrExpireDate = "123456" - addenda02.TerminalLocation = "Target Store 0049" - addenda02.TerminalCity = "PHILADELPHIA" - addenda02.TerminalState = "PA" - addenda02.TraceNumber = 91012980000088 - return addenda02 -} - // mockBatchPOSHeader creates a BatchPOS BatchHeader func mockBatchPOSHeader() *BatchHeader { bh := NewBatchHeader() @@ -33,32 +18,6 @@ func mockBatchPOSHeader() *BatchHeader { return bh } -// mockPOSEntryDetail creates a BatchPOS EntryDetail -func mockPOSEntryDetail() *EntryDetail { - entry := NewEntryDetail() - entry.TransactionCode = 27 - entry.SetRDFI("231380104") - entry.DFIAccountNumber = "744-5678-99" - entry.Amount = 25000 - entry.IdentificationNumber = "45689033" - entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) - entry.DiscretionaryData = "01" - entry.Category = CategoryForward - return entry -} - -// mockBatchPOS creates a BatchPOS -func mockBatchPOS() *BatchPOS { - mockBatch := NewBatchPOS(mockBatchPOSHeader()) - mockBatch.AddEntry(mockPOSEntryDetail()) - mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - if err := mockBatch.Create(); err != nil { - panic(err) - } - return mockBatch -} - // testBatchPOSHeader creates a BatchPOS BatchHeader func testBatchPOSHeader(t testing.TB) { batch, _ := NewBatch(mockBatchPOSHeader()) From f46e10556e573bfe34f0bb2ff9852f3f2394c7c8 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:48:33 -0400 Subject: [PATCH 0169/1694] add tests Add additional tests to batchPOS_test.go --- batchPOS_test.go | 237 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/batchPOS_test.go b/batchPOS_test.go index f97e31941..04622a531 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -18,6 +18,47 @@ func mockBatchPOSHeader() *BatchHeader { return bh } +// mockPOSEntryDetail creates a BatchPOS EntryDetail +func mockPOSEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetReceivingCompany("ABC Company") + entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + return entry +} + +func mockAddenda02() *Addenda02 { + addenda02 := NewAddenda02() + addenda02.ReferenceInformationOne = "REFONEA" + addenda02.ReferenceInformationTwo = "REF" + addenda02.TerminalIdentificationCode = "TERM02" + addenda02.TransactionSerialNumber = "100049" + addenda02.TransactionDate = "0612" + addenda02.AuthorizationCodeOrExpireDate = "123456" + addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalCity = "PHILADELPHIA" + addenda02.TerminalState = "PA" + addenda02.TraceNumber = 91012980000088 + return addenda02 +} + +// mockBatchPOS creates a BatchPOS +func mockBatchPOS() *BatchPOS { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + // testBatchPOSHeader creates a BatchPOS BatchHeader func testBatchPOSHeader(t testing.TB) { batch, _ := NewBatch(mockBatchPOSHeader()) @@ -39,3 +80,199 @@ func BenchmarkBatchPOSHeader(b *testing.B) { testBatchPOSHeader(b) } } + +// testBatchPOSCreate validates BatchPOS create +func testBatchPOSCreate(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchPOSCreate tests validating BatchPOS create +func TestBatchPOSCreate(t *testing.T) { + testBatchPOSCreate(t) +} + +// BenchmarkBatchPOSCreate benchmarks validating BatchPOS create +func BenchmarkBatchPOSCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSCreate(b) + } +} + +// testBatchPOSStandardEntryClassCode validates BatchPOS create for an invalid StandardEntryClassCode +func testBatchPOSStandardEntryClassCode(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSStandardEntryClassCode tests validating BatchPOS create for an invalid StandardEntryClassCode +func TestBatchPOSStandardEntryClassCode(t *testing.T) { + testBatchPOSStandardEntryClassCode(t) +} + +// BenchmarkBatchPOSStandardEntryClassCode benchmarks validating BatchPOS create for an invalid StandardEntryClassCode +func BenchmarkBatchPOSStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSStandardEntryClassCode(b) + } +} + +// testBatchPOSServiceClassCodeEquality validates service class code equality +func testBatchPOSServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchPPD() + mockBatch.GetControl().ServiceClassCode = 220 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSServiceClassCodeEquality tests validating service class code equality +func TestBatchPOSServiceClassCodeEquality(t *testing.T) { + testBatchPOSServiceClassCodeEquality(t) +} + +// BenchmarkBatchPOSServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchPOSServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSServiceClassCodeEquality(b) + } +} + +// testBatchPOSAddendaCount validates BatchPOS Addendum count of 2 +func testBatchPOSAddendaCount(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSAddendaCount tests validating BatchPOS Addendum count of 2 +func TestBatchPOSAddendaCount(t *testing.T) { + testBatchPOSAddendaCount(t) +} + +// BenchmarkBatchPOSAddendaCount benchmarks validating BatchPOS Addendum count of 2 +func BenchmarkBatchPOSAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSAddendaCount(b) + } +} + +// testBatchPOSAddendaCountZero validates Addendum count of 0 +func testBatchPOSAddendaCountZero(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSAddendaCountZero tests validating Addendum count of 0 +func TestBatchPOSAddendaCountZero(t *testing.T) { + testBatchPOSAddendaCountZero(t) +} + +// BenchmarkBatchPOSAddendaCountZero benchmarks validating Addendum count of 0 +func BenchmarkBatchPOSAddendaCountZero(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSAddendaCountZero(b) + } +} + +// testBatchPOSInvalidAddendum validates Addendum must be Addenda02 +func testBatchPOSInvalidAddendum(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSInvalidAddendum tests validating Addendum must be Addenda02 +func TestBatchPOSInvalidAddendum(t *testing.T) { + testBatchPOSInvalidAddendum(t) +} + +// BenchmarkBatchPOSInvalidAddendum benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchPOSInvalidAddendum(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSInvalidAddendum(b) + } +} + +// testBatchPOSInvalidAddenda validates Addendum must be Addenda02 +func testBatchPOSInvalidAddenda(t testing.TB) { + mockBatch := NewBatchPOS(mockBatchPOSHeader()) + mockBatch.AddEntry(mockPOSEntryDetail()) + addenda02 := mockAddenda02() + addenda02.recordType = "63" + mockBatch.GetEntries()[0].AddAddenda(addenda02) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSInvalidAddenda tests validating Addendum must be Addenda02 +func TestBatchPOSInvalidAddenda(t *testing.T) { + testBatchPOSInvalidAddenda(t) +} + +// BenchmarkBatchPOSInvalidAddenda benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSInvalidAddenda(b) + } +} From 3bf6b605c57c5e4f55378692ef8fe8633a684fd2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:51:39 -0400 Subject: [PATCH 0170/1694] Add Transaction Code Test Add Transaction Code Test --- batchPOS_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/batchPOS_test.go b/batchPOS_test.go index 04622a531..0b48b0793 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -160,6 +160,34 @@ func BenchmarkBatchPOSServiceClassCodeEquality(b *testing.B) { } } +// testBatchPOSTransactionCode validates BatchPOS TransactionCode is not a credit +func testBatchPOSTransactionCode(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.GetEntries()[0].TransactionCode = 22 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSTransactionCode tests validating BatchPOS TransactionCode is not a credit +func TestBatchPOSTransactionCode(t *testing.T) { + testBatchPOSTransactionCode(t) +} + +// BenchmarkBatchPOSTransactionCode benchmarks validating BatchPOS TransactionCode is not a credit +func BenchmarkBatchPOSTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSTransactionCode(b) + } +} + // testBatchPOSAddendaCount validates BatchPOS Addendum count of 2 func testBatchPOSAddendaCount(t testing.TB) { mockBatch := mockBatchPOS() From cef3a64d6142de956811c43cb7d8c9013fe0eea8 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 17:58:23 -0400 Subject: [PATCH 0171/1694] Additional tests Additional tests --- addenda02_test.go | 31 +++++++++++++++++++++++++++++++ batchPOS_test.go | 15 --------------- 2 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 addenda02_test.go diff --git a/addenda02_test.go b/addenda02_test.go new file mode 100644 index 000000000..915758eda --- /dev/null +++ b/addenda02_test.go @@ -0,0 +1,31 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +func mockAddenda02() *Addenda02 { + addenda02 := NewAddenda02() + addenda02.ReferenceInformationOne = "REFONEA" + addenda02.ReferenceInformationTwo = "REF" + addenda02.TerminalIdentificationCode = "TERM02" + addenda02.TransactionSerialNumber = "100049" + addenda02.TransactionDate = "0612" + addenda02.AuthorizationCodeOrExpireDate = "123456" + addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalCity = "PHILADELPHIA" + addenda02.TerminalState = "PA" + addenda02.TraceNumber = 91012980000088 + return addenda02 +} + +func TestMockAddenda02(t *testing.T) { + addenda02 := mockAddenda02() + if err := addenda02.Validate(); err != nil { + t.Error("mockAddenda02 does not validate and will break other tests") + } +} diff --git a/batchPOS_test.go b/batchPOS_test.go index 0b48b0793..0692a3ff0 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -33,21 +33,6 @@ func mockPOSEntryDetail() *EntryDetail { return entry } -func mockAddenda02() *Addenda02 { - addenda02 := NewAddenda02() - addenda02.ReferenceInformationOne = "REFONEA" - addenda02.ReferenceInformationTwo = "REF" - addenda02.TerminalIdentificationCode = "TERM02" - addenda02.TransactionSerialNumber = "100049" - addenda02.TransactionDate = "0612" - addenda02.AuthorizationCodeOrExpireDate = "123456" - addenda02.TerminalLocation = "Target Store 0049" - addenda02.TerminalCity = "PHILADELPHIA" - addenda02.TerminalState = "PA" - addenda02.TraceNumber = 91012980000088 - return addenda02 -} - // mockBatchPOS creates a BatchPOS func mockBatchPOS() *BatchPOS { mockBatch := NewBatchPOS(mockBatchPOSHeader()) From 10e645eb7d3e3bcb83c0daabdf692493dcda1d63 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 18:01:02 -0400 Subject: [PATCH 0172/1694] fieldInclusion tests fieldInclusion tests --- addenda02_test.go | 184 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/addenda02_test.go b/addenda02_test.go index 915758eda..4abab9cb9 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -29,3 +29,187 @@ func TestMockAddenda02(t *testing.T) { t.Error("mockAddenda02 does not validate and will break other tests") } } + +func testAddenda02RecordType(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.recordType = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02RecordType(t *testing.T) { + testAddenda02RecordType(t) +} + +func BenchmarkAddenda02FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02RecordType(b) + } +} + +func testAddenda02TypeCode(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.typeCode = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02TypeCode(t *testing.T) { + testAddenda02TypeCode(t) +} + +func BenchmarkAddenda02TypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TypeCode(b) + } +} + +func testAddenda02TerminalIdentificationCode(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalIdentificationCode = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02TerminalIdentificationCode(t *testing.T) { + testAddenda02TerminalIdentificationCode(t) +} + +func BenchmarkAddenda02TerminalIdentificationCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TerminalIdentificationCode(b) + } +} + +func testAddenda02TransactionSerialNumber(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionSerialNumber = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02TransactionSerialNumber(t *testing.T) { + testAddenda02TransactionSerialNumber(t) +} + +func BenchmarkAddenda02TransactionSerialNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionSerialNumber(b) + } +} + +func testAddenda02TransactionDate(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionDate = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02TransactionDate(t *testing.T) { + testAddenda02TransactionDate(t) +} + +func BenchmarkAddenda02TransactionDate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionDate(b) + } +} + +func testAddenda02TerminalLocation(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalLocation = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02TerminalLocation(t *testing.T) { + testAddenda02TerminalLocation(t) +} + +func BenchmarkAddenda02TerminalLocation(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TerminalLocation(b) + } +} + +func testAddenda02TerminalCity(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalCity = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02TerminalCity(t *testing.T) { + testAddenda02TerminalCity(t) +} + +func BenchmarkAddenda02TerminalCity(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TerminalCity(b) + } +} + +func testAddenda02TerminalState(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalState = "" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda02TerminalState(t *testing.T) { + testAddenda02TerminalState(t) +} + +func BenchmarkAddenda02TerminalState(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TerminalState(b) + } +} From b74acd50926a0fd7bb1266ae929b2a7507b183dd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 18:06:37 -0400 Subject: [PATCH 0173/1694] addenda02 RecordType and TypeCode tests addenda02 RecordType and TypeCode tests --- addenda02_test.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/addenda02_test.go b/addenda02_test.go index 4abab9cb9..39d2b0438 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -30,6 +30,78 @@ func TestMockAddenda02(t *testing.T) { } } +func testAddenda02ValidRecordType(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.recordType = "63" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} +func TestAddenda02ValidRecordType(t *testing.T) { + testAddenda02ValidRecordType(t) +} + +func BenchmarkAddenda02ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02ValidRecordType(b) + } +} + +func testAddenda02ValidTypeCode(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.typeCode = "65" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} +func TestAddenda02ValidTypeCode(t *testing.T) { + testAddenda02ValidTypeCode(t) +} + +func BenchmarkAddenda02ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02ValidTypeCode(b) + } +} + +func testAddenda02TypeCode02(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.typeCode = "05" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} +func TestAddenda02TypeCode02(t *testing.T) { + testAddenda02TypeCode02(t) +} + +func BenchmarkAddenda02TypeCode02(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TypeCode02(b) + } +} + func testAddenda02RecordType(t testing.TB) { addenda02 := mockAddenda02() addenda02.recordType = "" From 7bf907e42e04893e7b7d4c77f407e636f9bfa28a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 18:10:29 -0400 Subject: [PATCH 0174/1694] added ParseAddenda02 test added ParseAddenda02 test --- addenda02_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/addenda02_test.go b/addenda02_test.go index 39d2b0438..149833fa4 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -6,6 +6,7 @@ package ach import ( "testing" + "strings" ) func mockAddenda02() *Addenda02 { @@ -285,3 +286,73 @@ func BenchmarkAddenda02TerminalState(b *testing.B) { testAddenda02TerminalState(b) } } + +func testParseAddenda02(t testing.TB) { + addendaPOS := NewAddenda02() + var line = "702REFONEAREFTERM021000490612123456Target Store 0049 PHILADELPHIA PA091012980000088" + addendaPOS.Parse(line) + + r := NewReader(strings.NewReader(line)) + + //Add a new BatchPOS + r.addCurrentBatch(NewBatchPOS(mockBatchPOSHeader())) + + //Add a POS EntryDetail + entryDetail := mockPOSEntryDetail() + + //Add an addenda to the POS EntryDetail + entryDetail.AddAddenda(addendaPOS) + + // add the POSentry detail to the batch + r.currentBatch.AddEntry(entryDetail) + + record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda02) + + if record.recordType != "7" { + t.Errorf("RecordType Expected '7' got: %v", record.recordType) + } + if record.TypeCode() != "02" { + t.Errorf("TypeCode Expected 02 got: %v", record.TypeCode()) + } + if record.ReferenceInformationOne != "REFONEA" { + t.Errorf("ReferenceInformationOne Expected 'REFONEA' got: %v", record.ReferenceInformationOneField()) + } + if record.ReferenceInformationTwo != "REF" { + t.Errorf("ReferenceInformationTwo Expected 'REF' got: %v", record.ReferenceInformationTwoField()) + } + if record.TerminalIdentificationCode != "TERM02" { + t.Errorf("TerminalIdentificationCode Expected 'TERM02' got: %v", record.TerminalIdentificationCodeField()) + } + if record.TransactionSerialNumber != "100049" { + t.Errorf("TransactionSerialNumber Expected '100049' got: %v", record.TransactionSerialNumberField()) + } + if record.TransactionDate != "0612" { + t.Errorf("TransactionDate Expected '0612' got: %v", record.TransactionDateField()) + } + if record.AuthorizationCodeOrExpireDate != "123456" { + t.Errorf("AuthorizationCodeOrExpireDate Expected '123456' got: %v", record.AuthorizationCodeOrExpireDateField()) + } + if record.TerminalLocation != "Target Store 0049" { + t.Errorf("TerminalLocation Expected 'Target Store 0049' got: %v", record.TerminalLocationField()) + } + if record.TerminalCity != "PHILADELPHIA" { + t.Errorf("TerminalCity Expected '123456' got: %v", record.TerminalCityField()) + } + if record.TerminalState != "PA" { + t.Errorf("TerminalState Expected '123456' got: %v", record.TerminalStateField()) + } + if record.TraceNumber != 91012980000088 { + t.Errorf("TraceNumber Expected '91012980000088' got: %v", record.TraceNumberField()) + } +} + +func TestParseAddenda02(t *testing.T) { + testParseAddenda02(t) +} + +func BenchmarkParseAddenda02(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseAddenda02(b) + } +} \ No newline at end of file From 01b7be0555e2d3ae236176b708d914c90187438f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 18:13:49 -0400 Subject: [PATCH 0175/1694] Added addenda02 String test Added addenda02 String test --- addenda02_test.go | 72 ++++++++--------------------------------------- 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/addenda02_test.go b/addenda02_test.go index 149833fa4..dc51e2831 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -6,7 +6,6 @@ package ach import ( "testing" - "strings" ) func mockAddenda02() *Addenda02 { @@ -287,72 +286,23 @@ func BenchmarkAddenda02TerminalState(b *testing.B) { } } -func testParseAddenda02(t testing.TB) { - addendaPOS := NewAddenda02() +// TestAddenda02 String validates that a known parsed file can be return to a string of the same value +func testAddenda02String(t testing.TB) { + addenda02 := NewAddenda02() var line = "702REFONEAREFTERM021000490612123456Target Store 0049 PHILADELPHIA PA091012980000088" - addendaPOS.Parse(line) - - r := NewReader(strings.NewReader(line)) - - //Add a new BatchPOS - r.addCurrentBatch(NewBatchPOS(mockBatchPOSHeader())) - - //Add a POS EntryDetail - entryDetail := mockPOSEntryDetail() - - //Add an addenda to the POS EntryDetail - entryDetail.AddAddenda(addendaPOS) - - // add the POSentry detail to the batch - r.currentBatch.AddEntry(entryDetail) - - record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda02) - - if record.recordType != "7" { - t.Errorf("RecordType Expected '7' got: %v", record.recordType) - } - if record.TypeCode() != "02" { - t.Errorf("TypeCode Expected 02 got: %v", record.TypeCode()) - } - if record.ReferenceInformationOne != "REFONEA" { - t.Errorf("ReferenceInformationOne Expected 'REFONEA' got: %v", record.ReferenceInformationOneField()) - } - if record.ReferenceInformationTwo != "REF" { - t.Errorf("ReferenceInformationTwo Expected 'REF' got: %v", record.ReferenceInformationTwoField()) - } - if record.TerminalIdentificationCode != "TERM02" { - t.Errorf("TerminalIdentificationCode Expected 'TERM02' got: %v", record.TerminalIdentificationCodeField()) - } - if record.TransactionSerialNumber != "100049" { - t.Errorf("TransactionSerialNumber Expected '100049' got: %v", record.TransactionSerialNumberField()) - } - if record.TransactionDate != "0612" { - t.Errorf("TransactionDate Expected '0612' got: %v", record.TransactionDateField()) - } - if record.AuthorizationCodeOrExpireDate != "123456" { - t.Errorf("AuthorizationCodeOrExpireDate Expected '123456' got: %v", record.AuthorizationCodeOrExpireDateField()) - } - if record.TerminalLocation != "Target Store 0049" { - t.Errorf("TerminalLocation Expected 'Target Store 0049' got: %v", record.TerminalLocationField()) - } - if record.TerminalCity != "PHILADELPHIA" { - t.Errorf("TerminalCity Expected '123456' got: %v", record.TerminalCityField()) - } - if record.TerminalState != "PA" { - t.Errorf("TerminalState Expected '123456' got: %v", record.TerminalStateField()) - } - if record.TraceNumber != 91012980000088 { - t.Errorf("TraceNumber Expected '91012980000088' got: %v", record.TraceNumberField()) + addenda02.Parse(line) + if addenda02.String() != line { + t.Errorf("Strings do not match") } } -func TestParseAddenda02(t *testing.T) { - testParseAddenda02(t) +func TestAddenda02String(t *testing.T) { + testAddenda02String(t) } -func BenchmarkParseAddenda02(b *testing.B) { +func BenchmarkAddenda02String(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testParseAddenda02(b) + testAddenda02String(b) } -} \ No newline at end of file +} From 6f8786d37624177fca3095d02b7e94bbda9d46e0 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 19:01:45 -0400 Subject: [PATCH 0176/1694] ParseAddenda02 Add scaled down veriosn of ParseAddenda02 --- addenda02_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/addenda02_test.go b/addenda02_test.go index dc51e2831..0e1870fd7 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -306,3 +306,57 @@ func BenchmarkAddenda02String(b *testing.B) { testAddenda02String(b) } } + +func testParseAddenda02(t testing.TB) { + addendaPOS := NewAddenda02() + var line = "702REFONEAREFTERM021000490612123456Target Store 0049 PHILADELPHIA PA091012980000088" + addendaPOS.Parse(line) + + if addendaPOS.recordType != "7" { + t.Errorf("RecordType Expected '7' got: %v", addendaPOS.recordType) + } + if addendaPOS.TypeCode() != "02" { + t.Errorf("TypeCode Expected 02 got: %v", addendaPOS.TypeCode()) + } + if addendaPOS.ReferenceInformationOne != "REFONEA" { + t.Errorf("ReferenceInformationOne Expected 'REFONEA' got: %v", addendaPOS.ReferenceInformationOneField()) + } + if addendaPOS.ReferenceInformationTwo != "REF" { + t.Errorf("ReferenceInformationTwo Expected 'REF' got: %v", addendaPOS.ReferenceInformationTwoField()) + } + if addendaPOS.TerminalIdentificationCode != "TERM02" { + t.Errorf("TerminalIdentificationCode Expected 'TERM02' got: %v", addendaPOS.TerminalIdentificationCodeField()) + } + if addendaPOS.TransactionSerialNumber != "100049" { + t.Errorf("TransactionSerialNumber Expected '100049' got: %v", addendaPOS.TransactionSerialNumberField()) + } + if addendaPOS.TransactionDate != "0612" { + t.Errorf("TransactionDate Expected '0612' got: %v", addendaPOS.TransactionDateField()) + } + if addendaPOS.AuthorizationCodeOrExpireDate != "123456" { + t.Errorf("AuthorizationCodeOrExpireDate Expected '123456' got: %v", addendaPOS.AuthorizationCodeOrExpireDateField()) + } + if addendaPOS.TerminalLocation != "Target Store 0049" { + t.Errorf("TerminalLocation Expected 'Target Store 0049' got: %v", addendaPOS.TerminalLocationField()) + } + if addendaPOS.TerminalCity != "PHILADELPHIA" { + t.Errorf("TerminalCity Expected '123456' got: %v", addendaPOS.TerminalCityField()) + } + if addendaPOS.TerminalState != "PA" { + t.Errorf("TerminalState Expected '123456' got: %v", addendaPOS.TerminalStateField()) + } + if addendaPOS.TraceNumber != 91012980000088 { + t.Errorf("TraceNumber Expected '91012980000088' got: %v", addendaPOS.TraceNumberField()) + } +} + +func TestParseAddenda02(t *testing.T) { + testParseAddenda02(t) +} + +func BenchmarkParseAddenda02(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseAddenda02(b) + } +} \ No newline at end of file From a220af9b39c4096f5fa6391538ea08f8c9ab123d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 19:04:36 -0400 Subject: [PATCH 0177/1694] gofmt gofmt --- addenda02_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addenda02_test.go b/addenda02_test.go index 0e1870fd7..3b801198c 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -359,4 +359,4 @@ func BenchmarkParseAddenda02(b *testing.B) { for i := 0; i < b.N; i++ { testParseAddenda02(b) } -} \ No newline at end of file +} From 29a6ebdc4f6c3fe494e3bf43538e9733dd2b5c6b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 19:11:45 -0400 Subject: [PATCH 0178/1694] Remove ParseAddenda02 test Remove ParseAddenda02 test --- addenda02_test.go | 54 ----------------------------------------------- 1 file changed, 54 deletions(-) diff --git a/addenda02_test.go b/addenda02_test.go index 3b801198c..dc51e2831 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -306,57 +306,3 @@ func BenchmarkAddenda02String(b *testing.B) { testAddenda02String(b) } } - -func testParseAddenda02(t testing.TB) { - addendaPOS := NewAddenda02() - var line = "702REFONEAREFTERM021000490612123456Target Store 0049 PHILADELPHIA PA091012980000088" - addendaPOS.Parse(line) - - if addendaPOS.recordType != "7" { - t.Errorf("RecordType Expected '7' got: %v", addendaPOS.recordType) - } - if addendaPOS.TypeCode() != "02" { - t.Errorf("TypeCode Expected 02 got: %v", addendaPOS.TypeCode()) - } - if addendaPOS.ReferenceInformationOne != "REFONEA" { - t.Errorf("ReferenceInformationOne Expected 'REFONEA' got: %v", addendaPOS.ReferenceInformationOneField()) - } - if addendaPOS.ReferenceInformationTwo != "REF" { - t.Errorf("ReferenceInformationTwo Expected 'REF' got: %v", addendaPOS.ReferenceInformationTwoField()) - } - if addendaPOS.TerminalIdentificationCode != "TERM02" { - t.Errorf("TerminalIdentificationCode Expected 'TERM02' got: %v", addendaPOS.TerminalIdentificationCodeField()) - } - if addendaPOS.TransactionSerialNumber != "100049" { - t.Errorf("TransactionSerialNumber Expected '100049' got: %v", addendaPOS.TransactionSerialNumberField()) - } - if addendaPOS.TransactionDate != "0612" { - t.Errorf("TransactionDate Expected '0612' got: %v", addendaPOS.TransactionDateField()) - } - if addendaPOS.AuthorizationCodeOrExpireDate != "123456" { - t.Errorf("AuthorizationCodeOrExpireDate Expected '123456' got: %v", addendaPOS.AuthorizationCodeOrExpireDateField()) - } - if addendaPOS.TerminalLocation != "Target Store 0049" { - t.Errorf("TerminalLocation Expected 'Target Store 0049' got: %v", addendaPOS.TerminalLocationField()) - } - if addendaPOS.TerminalCity != "PHILADELPHIA" { - t.Errorf("TerminalCity Expected '123456' got: %v", addendaPOS.TerminalCityField()) - } - if addendaPOS.TerminalState != "PA" { - t.Errorf("TerminalState Expected '123456' got: %v", addendaPOS.TerminalStateField()) - } - if addendaPOS.TraceNumber != 91012980000088 { - t.Errorf("TraceNumber Expected '91012980000088' got: %v", addendaPOS.TraceNumberField()) - } -} - -func TestParseAddenda02(t *testing.T) { - testParseAddenda02(t) -} - -func BenchmarkParseAddenda02(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testParseAddenda02(b) - } -} From f96711ce1bc7c1247ff6e79199320b929c95f069 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 19:31:01 -0400 Subject: [PATCH 0179/1694] Support for SEC Code POS Added msgBatchPOSAddendaType --- batchPOS.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/batchPOS.go b/batchPOS.go index a1ace9c37..e6ea4fd91 100644 --- a/batchPOS.go +++ b/batchPOS.go @@ -24,7 +24,8 @@ type BatchPOS struct { batch } -var msgBatchPOSAddenda = "found and 1 Addenda02 is required for SEC Type POS" +var msgBatchPOSAddenda = "found and 1 Addenda02 is required for SEC code POS" +var msgBatchPOSAddendaType = "%T found where Addenda02 is required for SEC code POS" // NewBatchPOS returns a *BatchPOS func NewBatchPOS(bh *BatchHeader) *BatchPOS { @@ -46,7 +47,6 @@ func (batch *BatchPOS) Validate() error { if err := batch.isAddenda02(); err != nil { return err } - // Add type specific validation. if batch.Header.StandardEntryClassCode != "POS" { msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "POS") @@ -94,7 +94,7 @@ func (batch *BatchPOS) isAddenda02() error { // Addenda type assertion must be Addenda02 addenda02, ok := entry.Addendum[0].(*Addenda02) if !ok { - msg := fmt.Sprintf(msgBatchCORAddendaType, entry.Addendum[0]) + msg := fmt.Sprintf(msgBatchPOSAddendaType, entry.Addendum[0]) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} } // Addenda02 must be Validated From 021d855741d53f543eeb673e54001d3e1afebbe7 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 19:42:30 -0400 Subject: [PATCH 0180/1694] #280 Test coverage #280 Test coverage --- addenda02_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addenda02_test.go b/addenda02_test.go index dc51e2831..38217ea23 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -294,6 +294,9 @@ func testAddenda02String(t testing.TB) { if addenda02.String() != line { t.Errorf("Strings do not match") } + if addenda02.TypeCode() != "02" { + t.Errorf("TypeCode Expected 02 got: %v", addenda02.TypeCode()) + } } func TestAddenda02String(t *testing.T) { From 474b733c36724ac3fc141af9ef5aca87af1f44b0 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 21:07:34 -0400 Subject: [PATCH 0181/1694] #208 Update AddAddenda #208 Update AddAddenda to include Addenda02 --- entryDetail.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/entryDetail.go b/entryDetail.go index 1534a457b..80c0efdd1 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -231,7 +231,12 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum // default is current *Addenda05 - default: + case *Addenda02: + ed.Category = CategoryForward + ed.Addendum = nil + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum + default: ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum From 51096e3efbb8df046e64dd4b4a3ead85894d2ed1 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 21:49:31 -0400 Subject: [PATCH 0182/1694] #208 BatchPOS code coverage #208 BatchPOS code coverage --- batchPOS_test.go | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/batchPOS_test.go b/batchPOS_test.go index 0692a3ff0..c677f3428 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -119,8 +119,8 @@ func BenchmarkBatchPOSStandardEntryClassCode(b *testing.B) { // testBatchPOSServiceClassCodeEquality validates service class code equality func testBatchPOSServiceClassCodeEquality(t testing.TB) { - mockBatch := mockBatchPPD() - mockBatch.GetControl().ServiceClassCode = 220 + mockBatch := mockBatchPOS() + mockBatch.GetControl().ServiceClassCode = 200 if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "ServiceClassCode" { @@ -177,7 +177,7 @@ func BenchmarkBatchPOSTransactionCode(b *testing.B) { func testBatchPOSAddendaCount(t testing.TB) { mockBatch := mockBatchPOS() mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - mockBatch.Create() + //mockBatch.Create() if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "Addendum" { @@ -289,3 +289,31 @@ func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { testBatchPOSInvalidAddenda(b) } } + +// testBatchInvalidBuild validates an invalid batch build +func testBatchInvalidBuild(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchInvalidBuild tests validating an invalid batch build +func TestBatchInvalidBuild(t *testing.T) { + testBatchInvalidBuild(t) +} + +// BenchmarkBatchInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchInvalidBuild(b) + } +} From 9f1dd4dd18d8bae1da7f184ea596b057d6f0c53f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 21:53:38 -0400 Subject: [PATCH 0183/1694] Revert Revert --- batchPOS_test.go | 28 ---------------------------- entryDetail.go | 7 +------ 2 files changed, 1 insertion(+), 34 deletions(-) diff --git a/batchPOS_test.go b/batchPOS_test.go index c677f3428..4d4ff2a4f 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -289,31 +289,3 @@ func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { testBatchPOSInvalidAddenda(b) } } - -// testBatchInvalidBuild validates an invalid batch build -func testBatchInvalidBuild(t testing.TB) { - mockBatch := mockBatchPOS() - mockBatch.GetHeader().recordType = "3" - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchInvalidBuild tests validating an invalid batch build -func TestBatchInvalidBuild(t *testing.T) { - testBatchInvalidBuild(t) -} - -// BenchmarkBatchInvalidBuild benchmarks validating an invalid batch build -func BenchmarkBatchInvalidBuild(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchInvalidBuild(b) - } -} diff --git a/entryDetail.go b/entryDetail.go index 80c0efdd1..1534a457b 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -231,12 +231,7 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum // default is current *Addenda05 - case *Addenda02: - ed.Category = CategoryForward - ed.Addendum = nil - ed.Addendum = append(ed.Addendum, addenda) - return ed.Addendum - default: + default: ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum From 7a87e2c48a160a9a21a6015d18bc2b225d3953b7 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 21:55:45 -0400 Subject: [PATCH 0184/1694] Addendum Addendum --- batchPOS_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batchPOS_test.go b/batchPOS_test.go index 4d4ff2a4f..e6e1393e0 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -177,7 +177,7 @@ func BenchmarkBatchPOSTransactionCode(b *testing.B) { func testBatchPOSAddendaCount(t testing.TB) { mockBatch := mockBatchPOS() mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) - //mockBatch.Create() + mockBatch.Create() if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "Addendum" { From 78061fba0b1bb5ca06dcef406a71f8790ba3ace7 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 22:01:03 -0400 Subject: [PATCH 0185/1694] Addenda02 Addenda02 --- entryDetail.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/entryDetail.go b/entryDetail.go index 1534a457b..eb69fd740 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -230,7 +230,12 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum - // default is current *Addenda05 + case *Addenda02: + ed.Category = CategoryForward + ed.Addendum = nil + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum + // default is current *Addenda05 default: ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) From 6e721f9a31288efef9df027900e03c9ca7501238 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 13 Jun 2018 22:07:40 -0400 Subject: [PATCH 0186/1694] batchPOS Code Coverage batchPOS Code Coverage --- batchPOS_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/batchPOS_test.go b/batchPOS_test.go index e6e1393e0..1ab646250 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -289,3 +289,31 @@ func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { testBatchPOSInvalidAddenda(b) } } + +// testBatchInvalidBuild validates an invalid batch build +func testBatchInvalidBuild(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchInvalidBuild tests validating an invalid batch build +func TestBatchInvalidBuild(t *testing.T) { + testBatchInvalidBuild(t) +} + +// BenchmarkBatchInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchInvalidBuild(b) + } +} From d8a297530f835d31a71a34655b818b3f3d63d997 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:25:20 -0400 Subject: [PATCH 0187/1694] Batch Code Coverage Batch Code Coverage --- BatchARC_test.go | 28 ++++++++++++++++++++++++++++ batchBOC_test.go | 28 ++++++++++++++++++++++++++++ batchPOP_test.go | 28 ++++++++++++++++++++++++++++ batchPOS_test.go | 16 ++++++++-------- batchRCK_test.go | 28 ++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 8 deletions(-) diff --git a/BatchARC_test.go b/BatchARC_test.go index d1b372ed3..24cd1618d 100644 --- a/BatchARC_test.go +++ b/BatchARC_test.go @@ -350,3 +350,31 @@ func BenchmarkBatchARCAddendaCount(b *testing.B) { testBatchARCAddendaCount(b) } } + +// testBatchARCInvalidBuild validates an invalid batch build +func testBatchARCInvalidBuild(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCInvalidBuild tests validating an invalid batch build +func TestBatchARCInvalidBuild(t *testing.T) { + testBatchARCInvalidBuild(t) +} + +// BenchmarkBatchARCInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchARCInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCInvalidBuild(b) + } +} \ No newline at end of file diff --git a/batchBOC_test.go b/batchBOC_test.go index 2fc05a3ee..5033095f7 100644 --- a/batchBOC_test.go +++ b/batchBOC_test.go @@ -349,3 +349,31 @@ func BenchmarkBatchBOCAddendaCount(b *testing.B) { testBatchBOCAddendaCount(b) } } + +// testBatchBOCInvalidBuild validates an invalid batch build +func testBatchBOCInvalidBuild(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCInvalidBuild tests validating an invalid batch build +func TestBatchBOCInvalidBuild(t *testing.T) { + testBatchBOCInvalidBuild(t) +} + +// BenchmarkBatchBOCInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchBOCInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCInvalidBuild(b) + } +} \ No newline at end of file diff --git a/batchPOP_test.go b/batchPOP_test.go index 2935c5396..b688d6fc5 100644 --- a/batchPOP_test.go +++ b/batchPOP_test.go @@ -429,3 +429,31 @@ func BenchmarkBatchPOPAddendaCount(b *testing.B) { testBatchARCAddendaCount(b) } } + +// testBatchPOPInvalidBuild validates an invalid batch build +func testBatchPOPInvalidBuild(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPInvalidBuild tests validating an invalid batch build +func TestBatchPOPInvalidBuild(t *testing.T) { + testBatchPOPInvalidBuild(t) +} + +// BenchmarkBatchPOPInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchPOPInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPInvalidBuild(b) + } +} \ No newline at end of file diff --git a/batchPOS_test.go b/batchPOS_test.go index 1ab646250..c3c95f793 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -290,8 +290,8 @@ func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { } } -// testBatchInvalidBuild validates an invalid batch build -func testBatchInvalidBuild(t testing.TB) { +// testBatchPOSInvalidBuild validates an invalid batch build +func testBatchPOSInvalidBuild(t testing.TB) { mockBatch := mockBatchPOS() mockBatch.GetHeader().recordType = "3" if err := mockBatch.Create(); err != nil { @@ -305,15 +305,15 @@ func testBatchInvalidBuild(t testing.TB) { } } -// TestBatchInvalidBuild tests validating an invalid batch build -func TestBatchInvalidBuild(t *testing.T) { - testBatchInvalidBuild(t) +// TestBatchPOSInvalidBuild tests validating an invalid batch build +func TestBatchPOSInvalidBuild(t *testing.T) { + testBatchPOSInvalidBuild(t) } -// BenchmarkBatchInvalidBuild benchmarks validating an invalid batch build -func BenchmarkBatchInvalidBuild(b *testing.B) { +// BenchmarkBatchPOSInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchPOSInvalidBuild(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testBatchInvalidBuild(b) + testBatchPOSInvalidBuild(b) } } diff --git a/batchRCK_test.go b/batchRCK_test.go index cd4baf6cc..3aaf79515 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -406,3 +406,31 @@ func BenchmarkBatchRCKParseCheckSerialNumber(b *testing.B) { testBatchRCKParseCheckSerialNumber(b) } } + +// testBatchRCKInvalidBuild validates an invalid batch build +func testBatchRCKInvalidBuild(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKInvalidBuild tests validating an invalid batch build +func TestBatchRCKInvalidBuild(t *testing.T) { + testBatchRCKInvalidBuild(t) +} + +// BenchmarkBatchRCKInvalidBuild benchmarks validating an invalid batch build +func BenchmarkRCKBatchInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKInvalidBuild(b) + } +} \ No newline at end of file From a88fb5d57e9259e1a12941abf901b7c096938115 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:33:51 -0400 Subject: [PATCH 0188/1694] Revert Revert --- BatchARC_test.go | 28 ---------------------------- batchBOC_test.go | 28 ---------------------------- batchPOP_test.go | 28 ---------------------------- batchRCK_test.go | 28 ---------------------------- 4 files changed, 112 deletions(-) diff --git a/BatchARC_test.go b/BatchARC_test.go index 24cd1618d..d1b372ed3 100644 --- a/BatchARC_test.go +++ b/BatchARC_test.go @@ -350,31 +350,3 @@ func BenchmarkBatchARCAddendaCount(b *testing.B) { testBatchARCAddendaCount(b) } } - -// testBatchARCInvalidBuild validates an invalid batch build -func testBatchARCInvalidBuild(t testing.TB) { - mockBatch := mockBatchARC() - mockBatch.GetHeader().recordType = "3" - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchARCInvalidBuild tests validating an invalid batch build -func TestBatchARCInvalidBuild(t *testing.T) { - testBatchARCInvalidBuild(t) -} - -// BenchmarkBatchARCInvalidBuild benchmarks validating an invalid batch build -func BenchmarkBatchARCInvalidBuild(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchARCInvalidBuild(b) - } -} \ No newline at end of file diff --git a/batchBOC_test.go b/batchBOC_test.go index 5033095f7..2fc05a3ee 100644 --- a/batchBOC_test.go +++ b/batchBOC_test.go @@ -349,31 +349,3 @@ func BenchmarkBatchBOCAddendaCount(b *testing.B) { testBatchBOCAddendaCount(b) } } - -// testBatchBOCInvalidBuild validates an invalid batch build -func testBatchBOCInvalidBuild(t testing.TB) { - mockBatch := mockBatchBOC() - mockBatch.GetHeader().recordType = "3" - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchBOCInvalidBuild tests validating an invalid batch build -func TestBatchBOCInvalidBuild(t *testing.T) { - testBatchBOCInvalidBuild(t) -} - -// BenchmarkBatchBOCInvalidBuild benchmarks validating an invalid batch build -func BenchmarkBatchBOCInvalidBuild(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchBOCInvalidBuild(b) - } -} \ No newline at end of file diff --git a/batchPOP_test.go b/batchPOP_test.go index b688d6fc5..2935c5396 100644 --- a/batchPOP_test.go +++ b/batchPOP_test.go @@ -429,31 +429,3 @@ func BenchmarkBatchPOPAddendaCount(b *testing.B) { testBatchARCAddendaCount(b) } } - -// testBatchPOPInvalidBuild validates an invalid batch build -func testBatchPOPInvalidBuild(t testing.TB) { - mockBatch := mockBatchPOP() - mockBatch.GetHeader().recordType = "3" - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchPOPInvalidBuild tests validating an invalid batch build -func TestBatchPOPInvalidBuild(t *testing.T) { - testBatchPOPInvalidBuild(t) -} - -// BenchmarkBatchPOPInvalidBuild benchmarks validating an invalid batch build -func BenchmarkBatchPOPInvalidBuild(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchPOPInvalidBuild(b) - } -} \ No newline at end of file diff --git a/batchRCK_test.go b/batchRCK_test.go index 3aaf79515..cd4baf6cc 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -406,31 +406,3 @@ func BenchmarkBatchRCKParseCheckSerialNumber(b *testing.B) { testBatchRCKParseCheckSerialNumber(b) } } - -// testBatchRCKInvalidBuild validates an invalid batch build -func testBatchRCKInvalidBuild(t testing.TB) { - mockBatch := mockBatchRCK() - mockBatch.GetHeader().recordType = "3" - if err := mockBatch.Create(); err != nil { - if e, ok := err.(*FieldError); ok { - if e.FieldName != "recordType" { - t.Errorf("%T: %s", err, err) - } - } else { - t.Errorf("%T: %s", err, err) - } - } -} - -// TestBatchRCKInvalidBuild tests validating an invalid batch build -func TestBatchRCKInvalidBuild(t *testing.T) { - testBatchRCKInvalidBuild(t) -} - -// BenchmarkBatchRCKInvalidBuild benchmarks validating an invalid batch build -func BenchmarkRCKBatchInvalidBuild(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchRCKInvalidBuild(b) - } -} \ No newline at end of file From d30b66816f9bc03d47081da2a0e324ee8447e180 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:37:54 -0400 Subject: [PATCH 0189/1694] RCK Code Coverage RCK Code Coverage --- batchRCK_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/batchRCK_test.go b/batchRCK_test.go index cd4baf6cc..c8bb49d4d 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -406,3 +406,31 @@ func BenchmarkBatchRCKParseCheckSerialNumber(b *testing.B) { testBatchRCKParseCheckSerialNumber(b) } } + +// testBatchRCKInvalidBuild validates an invalid batch build +func testBatchRCKInvalidBuild(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKInvalidBuild tests validating an invalid batch build +func TestBatchRCKInvalidBuild(t *testing.T) { + testBatchRCKInvalidBuild(t) +} + +// BenchmarkBatchRCKInvalidBuild benchmarks validating an invalid batch build +func BenchmarkRCKBatchInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKInvalidBuild(b) + } +} From 6f252712e26a4caf73e9598db4f7a3546f5a06e8 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:40:29 -0400 Subject: [PATCH 0190/1694] BatchARC code coverage BatchARC code coverage --- BatchARC_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/BatchARC_test.go b/BatchARC_test.go index d1b372ed3..b2c370502 100644 --- a/BatchARC_test.go +++ b/BatchARC_test.go @@ -350,3 +350,31 @@ func BenchmarkBatchARCAddendaCount(b *testing.B) { testBatchARCAddendaCount(b) } } + +// testBatchARCInvalidBuild validates an invalid batch build +func testBatchARCInvalidBuild(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCInvalidBuild tests validating an invalid batch build +func TestBatchARCInvalidBuild(t *testing.T) { + testBatchARCInvalidBuild(t) +} + +// BenchmarkBatchARCInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchARCInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCInvalidBuild(b) + } +} From 7a61c6202c7939a1815333296451e37fbc454ee1 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:41:46 -0400 Subject: [PATCH 0191/1694] batchBOC code coverage batchBOC code coverage --- batchBOC_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/batchBOC_test.go b/batchBOC_test.go index 2fc05a3ee..b8c49df1d 100644 --- a/batchBOC_test.go +++ b/batchBOC_test.go @@ -349,3 +349,31 @@ func BenchmarkBatchBOCAddendaCount(b *testing.B) { testBatchBOCAddendaCount(b) } } + +// testBatchBOCInvalidBuild validates an invalid batch build +func testBatchBOCInvalidBuild(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCInvalidBuild tests validating an invalid batch build +func TestBatchBOCInvalidBuild(t *testing.T) { + testBatchBOCInvalidBuild(t) +} + +// BenchmarkBatchBOCInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchBOCInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCInvalidBuild(b) + } +} From e24d73a58138a8ffec5db240852f096ae7fe3acb Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:44:59 -0400 Subject: [PATCH 0192/1694] batchPOP code coverage batchPOP code coverage --- batchPOP_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/batchPOP_test.go b/batchPOP_test.go index 2935c5396..23f67379e 100644 --- a/batchPOP_test.go +++ b/batchPOP_test.go @@ -429,3 +429,31 @@ func BenchmarkBatchPOPAddendaCount(b *testing.B) { testBatchARCAddendaCount(b) } } + +// testBatchPOPInvalidBuild validates an invalid batch build +func testBatchPOPInvalidBuild(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPInvalidBuild tests validating an invalid batch build +func TestBatchPOPInvalidBuild(t *testing.T) { + testBatchPOPInvalidBuild(t) +} + +// BenchmarkBatchPOPInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchPOPInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPInvalidBuild(b) + } +} From d728776875fad9dcbe55b7500354e0fadbe2e1b8 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:53:09 -0400 Subject: [PATCH 0193/1694] BatchARC code coverage 100% BatchARC code coverage 100% --- BatchARC_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/BatchARC_test.go b/BatchARC_test.go index b2c370502..bb589d3b0 100644 --- a/BatchARC_test.go +++ b/BatchARC_test.go @@ -148,6 +148,34 @@ func BenchmarkBatchARCStandardEntryClassCode(b *testing.B) { } } +// testBatchARCServiceClassCodeEquality validates service class code equality +func testBatchARCServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchARC() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchARCServiceClassCodeEquality tests validating service class code equality +func TestBatchARCServiceClassCodeEquality(t *testing.T) { + testBatchARCServiceClassCodeEquality(t) +} + +// BenchmarkBatchARCServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchARCServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchARCServiceClassCodeEquality(b) + } +} + // testBatchARCServiceClass200 validates BatchARC create for an invalid ServiceClassCode 200 func testBatchARCServiceClass200(t testing.TB) { mockBatch := mockBatchARC() From 62ea5354f0e0654c95104eda3a6a5dbe4a9a2c56 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:55:28 -0400 Subject: [PATCH 0194/1694] BatchBOC code coverage 100% BatchBOC code coverage 100% --- batchBOC_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/batchBOC_test.go b/batchBOC_test.go index b8c49df1d..c2d352a5b 100644 --- a/batchBOC_test.go +++ b/batchBOC_test.go @@ -148,6 +148,34 @@ func BenchmarkBatchBOCStandardEntryClassCode(b *testing.B) { } } +// testBatchBOCServiceClassCodeEquality validates service class code equality +func testBatchBOCServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchBOC() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchBOCServiceClassCodeEquality tests validating service class code equality +func TestBatchBOCServiceClassCodeEquality(t *testing.T) { + testBatchBOCServiceClassCodeEquality(t) +} + +// BenchmarkBatchBOCServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchBOCServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchBOCServiceClassCodeEquality(b) + } +} + // testBatchBOCServiceClass200 validates BatchBOC create for an invalid ServiceClassCode 200 func testBatchBOCServiceClass200(t testing.TB) { mockBatch := mockBatchBOC() From fef3542df71e23d8c29f4b50b95c1712177440f9 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 09:59:20 -0400 Subject: [PATCH 0195/1694] BatchPOP code coverage 100% BatchPOP code coverage 100% --- batchPOP_test.go | 28 + cover.out | 853 +++++++++ coverage.html | 4528 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 5409 insertions(+) create mode 100644 cover.out create mode 100644 coverage.html diff --git a/batchPOP_test.go b/batchPOP_test.go index 23f67379e..f16e7218c 100644 --- a/batchPOP_test.go +++ b/batchPOP_test.go @@ -152,6 +152,34 @@ func BenchmarkBatchPOPStandardEntryClassCode(b *testing.B) { } } +// testBatchPOPServiceClassCodeEquality validates service class code equality +func testBatchPOPServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchPOP() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOPServiceClassCodeEquality tests validating service class code equality +func TestBatchPOPServiceClassCodeEquality(t *testing.T) { + testBatchPOPServiceClassCodeEquality(t) +} + +// BenchmarkBatchPOPServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchPOPServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOPServiceClassCodeEquality(b) + } +} + // testBatchPOPServiceClass200 validates BatchPOP create for an invalid ServiceClassCode 200 func testBatchPOPServiceClass200(t testing.TB) { mockBatch := mockBatchPOP() diff --git a/cover.out b/cover.out new file mode 100644 index 000000000..586f25205 --- /dev/null +++ b/cover.out @@ -0,0 +1,853 @@ +mode: set +github.com\bkmoovio\ach\batchRCK.go:18.45,23.2 4 1 +github.com\bkmoovio\ach\batchRCK.go:26.41,28.39 1 1 +github.com\bkmoovio\ach\batchRCK.go:33.2,33.48 1 1 +github.com\bkmoovio\ach\batchRCK.go:38.2,38.50 1 1 +github.com\bkmoovio\ach\batchRCK.go:44.2,44.39 1 1 +github.com\bkmoovio\ach\batchRCK.go:51.2,51.58 1 1 +github.com\bkmoovio\ach\batchRCK.go:56.2,56.38 1 1 +github.com\bkmoovio\ach\batchRCK.go:75.2,75.12 1 1 +github.com\bkmoovio\ach\batchRCK.go:28.39,30.3 1 0 +github.com\bkmoovio\ach\batchRCK.go:33.48,35.3 1 1 +github.com\bkmoovio\ach\batchRCK.go:38.50,41.3 2 1 +github.com\bkmoovio\ach\batchRCK.go:45.21,47.101 2 1 +github.com\bkmoovio\ach\batchRCK.go:51.58,54.3 2 1 +github.com\bkmoovio\ach\batchRCK.go:56.38,58.35 1 1 +github.com\bkmoovio\ach\batchRCK.go:64.3,64.28 1 1 +github.com\bkmoovio\ach\batchRCK.go:70.3,70.39 1 1 +github.com\bkmoovio\ach\batchRCK.go:58.35,61.4 2 1 +github.com\bkmoovio\ach\batchRCK.go:64.28,67.4 2 1 +github.com\bkmoovio\ach\batchRCK.go:70.39,73.4 2 1 +github.com\bkmoovio\ach\batchRCK.go:79.39,81.38 1 1 +github.com\bkmoovio\ach\batchRCK.go:87.2,87.25 1 1 +github.com\bkmoovio\ach\batchRCK.go:81.38,83.3 1 1 +github.com\bkmoovio\ach\batchTEL.go:17.45,22.2 4 1 +github.com\bkmoovio\ach\batchTEL.go:25.41,27.39 1 1 +github.com\bkmoovio\ach\batchTEL.go:32.2,32.48 1 1 +github.com\bkmoovio\ach\batchTEL.go:37.2,37.50 1 1 +github.com\bkmoovio\ach\batchTEL.go:42.2,42.38 1 1 +github.com\bkmoovio\ach\batchTEL.go:49.2,49.34 1 1 +github.com\bkmoovio\ach\batchTEL.go:27.39,29.3 1 1 +github.com\bkmoovio\ach\batchTEL.go:32.48,34.3 1 1 +github.com\bkmoovio\ach\batchTEL.go:37.50,40.3 2 1 +github.com\bkmoovio\ach\batchTEL.go:42.38,43.35 1 1 +github.com\bkmoovio\ach\batchTEL.go:43.35,46.4 2 1 +github.com\bkmoovio\ach\batchTEL.go:53.39,55.38 1 1 +github.com\bkmoovio\ach\batchTEL.go:59.2,59.25 1 1 +github.com\bkmoovio\ach\batchTEL.go:55.38,57.3 1 1 +github.com\bkmoovio\ach\batcher.go:39.37,41.2 1 1 +github.com\bkmoovio\ach\converters.go:16.54,19.2 2 1 +github.com\bkmoovio\ach\converters.go:21.60,24.2 2 1 +github.com\bkmoovio\ach\converters.go:27.59,29.2 1 1 +github.com\bkmoovio\ach\converters.go:32.58,35.2 2 1 +github.com\bkmoovio\ach\converters.go:38.59,40.2 1 1 +github.com\bkmoovio\ach\converters.go:43.58,46.2 2 1 +github.com\bkmoovio\ach\converters.go:49.60,51.14 2 1 +github.com\bkmoovio\ach\converters.go:54.2,55.10 2 1 +github.com\bkmoovio\ach\converters.go:51.14,53.3 1 1 +github.com\bkmoovio\ach\converters.go:59.59,62.14 3 1 +github.com\bkmoovio\ach\converters.go:65.2,66.10 2 1 +github.com\bkmoovio\ach\converters.go:62.14,64.3 1 1 +github.com\bkmoovio\ach\converters.go:70.64,72.14 2 1 +github.com\bkmoovio\ach\converters.go:75.2,76.10 2 1 +github.com\bkmoovio\ach\converters.go:72.14,74.3 1 1 +github.com\bkmoovio\ach\addenda05.go:39.32,44.2 4 1 +github.com\bkmoovio\ach\addenda05.go:47.50,59.2 5 1 +github.com\bkmoovio\ach\addenda05.go:62.45,69.2 1 1 +github.com\bkmoovio\ach\addenda05.go:73.46,74.51 1 1 +github.com\bkmoovio\ach\addenda05.go:77.2,77.33 1 1 +github.com\bkmoovio\ach\addenda05.go:81.2,81.65 1 1 +github.com\bkmoovio\ach\addenda05.go:84.2,84.86 1 1 +github.com\bkmoovio\ach\addenda05.go:88.2,88.12 1 1 +github.com\bkmoovio\ach\addenda05.go:74.51,76.3 1 1 +github.com\bkmoovio\ach\addenda05.go:77.33,80.3 2 1 +github.com\bkmoovio\ach\addenda05.go:81.65,83.3 1 1 +github.com\bkmoovio\ach\addenda05.go:84.86,86.3 1 1 +github.com\bkmoovio\ach\addenda05.go:93.52,94.32 1 1 +github.com\bkmoovio\ach\addenda05.go:97.2,97.30 1 1 +github.com\bkmoovio\ach\addenda05.go:100.2,100.35 1 1 +github.com\bkmoovio\ach\addenda05.go:103.2,103.46 1 1 +github.com\bkmoovio\ach\addenda05.go:106.2,106.12 1 1 +github.com\bkmoovio\ach\addenda05.go:94.32,96.3 1 1 +github.com\bkmoovio\ach\addenda05.go:97.30,99.3 1 1 +github.com\bkmoovio\ach\addenda05.go:100.35,102.3 1 1 +github.com\bkmoovio\ach\addenda05.go:103.46,105.3 1 1 +github.com\bkmoovio\ach\addenda05.go:110.69,112.2 1 1 +github.com\bkmoovio\ach\addenda05.go:115.58,117.2 1 1 +github.com\bkmoovio\ach\addenda05.go:120.69,122.2 1 1 +github.com\bkmoovio\ach\addenda05.go:125.47,127.2 1 1 +github.com\bkmoovio\ach\batchARC.go:27.45,32.2 4 1 +github.com\bkmoovio\ach\batchARC.go:35.41,37.39 1 1 +github.com\bkmoovio\ach\batchARC.go:43.2,43.48 1 1 +github.com\bkmoovio\ach\batchARC.go:48.2,48.50 1 1 +github.com\bkmoovio\ach\batchARC.go:54.2,54.39 1 1 +github.com\bkmoovio\ach\batchARC.go:60.2,60.38 1 1 +github.com\bkmoovio\ach\batchARC.go:79.2,79.12 1 1 +github.com\bkmoovio\ach\batchARC.go:37.39,39.3 1 1 +github.com\bkmoovio\ach\batchARC.go:43.48,45.3 1 1 +github.com\bkmoovio\ach\batchARC.go:48.50,51.3 2 1 +github.com\bkmoovio\ach\batchARC.go:55.21,57.101 2 1 +github.com\bkmoovio\ach\batchARC.go:60.38,62.35 1 1 +github.com\bkmoovio\ach\batchARC.go:68.3,68.29 1 1 +github.com\bkmoovio\ach\batchARC.go:74.3,74.39 1 1 +github.com\bkmoovio\ach\batchARC.go:62.35,65.4 2 1 +github.com\bkmoovio\ach\batchARC.go:68.29,71.4 2 1 +github.com\bkmoovio\ach\batchARC.go:74.39,77.4 2 1 +github.com\bkmoovio\ach\batchARC.go:83.39,85.38 1 1 +github.com\bkmoovio\ach\batchARC.go:91.2,91.25 1 1 +github.com\bkmoovio\ach\batchARC.go:85.38,87.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:110.36,117.2 2 1 +github.com\bkmoovio\ach\batchHeader.go:120.45,155.2 13 1 +github.com\bkmoovio\ach\batchHeader.go:158.40,174.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:178.41,179.44 1 1 +github.com\bkmoovio\ach\batchHeader.go:182.2,182.26 1 1 +github.com\bkmoovio\ach\batchHeader.go:186.2,186.63 1 1 +github.com\bkmoovio\ach\batchHeader.go:189.2,189.64 1 1 +github.com\bkmoovio\ach\batchHeader.go:192.2,192.75 1 1 +github.com\bkmoovio\ach\batchHeader.go:195.2,195.58 1 1 +github.com\bkmoovio\ach\batchHeader.go:198.2,198.71 1 1 +github.com\bkmoovio\ach\batchHeader.go:201.2,201.68 1 1 +github.com\bkmoovio\ach\batchHeader.go:204.2,204.70 1 1 +github.com\bkmoovio\ach\batchHeader.go:207.2,207.12 1 1 +github.com\bkmoovio\ach\batchHeader.go:179.44,181.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:182.26,185.3 2 1 +github.com\bkmoovio\ach\batchHeader.go:186.63,188.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:189.64,191.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:192.75,194.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:195.58,197.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:198.71,200.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:201.68,203.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:204.70,206.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:212.47,213.25 1 1 +github.com\bkmoovio\ach\batchHeader.go:216.2,216.30 1 1 +github.com\bkmoovio\ach\batchHeader.go:219.2,219.26 1 1 +github.com\bkmoovio\ach\batchHeader.go:222.2,222.36 1 1 +github.com\bkmoovio\ach\batchHeader.go:225.2,225.37 1 1 +github.com\bkmoovio\ach\batchHeader.go:228.2,228.38 1 1 +github.com\bkmoovio\ach\batchHeader.go:231.2,231.33 1 1 +github.com\bkmoovio\ach\batchHeader.go:234.2,234.12 1 1 +github.com\bkmoovio\ach\batchHeader.go:213.25,215.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:216.30,218.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:219.26,221.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:222.36,224.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:225.37,227.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:228.38,230.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:231.33,233.3 1 1 +github.com\bkmoovio\ach\batchHeader.go:238.50,240.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:243.63,245.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:248.60,250.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:253.62,255.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:258.61,260.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:263.57,265.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:268.57,270.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:273.50,275.2 1 1 +github.com\bkmoovio\ach\batchHeader.go:277.53,279.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:96.33,106.2 2 1 +github.com\bkmoovio\ach\fileHeader.go:109.44,137.2 13 1 +github.com\bkmoovio\ach\fileHeader.go:140.39,157.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:161.40,163.44 1 1 +github.com\bkmoovio\ach\fileHeader.go:166.2,166.26 1 1 +github.com\bkmoovio\ach\fileHeader.go:170.2,170.66 1 1 +github.com\bkmoovio\ach\fileHeader.go:173.2,173.33 1 1 +github.com\bkmoovio\ach\fileHeader.go:177.2,177.28 1 1 +github.com\bkmoovio\ach\fileHeader.go:180.2,180.31 1 1 +github.com\bkmoovio\ach\fileHeader.go:183.2,183.26 1 1 +github.com\bkmoovio\ach\fileHeader.go:186.2,186.71 1 1 +github.com\bkmoovio\ach\fileHeader.go:189.2,189.39 1 1 +github.com\bkmoovio\ach\fileHeader.go:192.2,192.44 1 1 +github.com\bkmoovio\ach\fileHeader.go:195.2,195.66 1 1 +github.com\bkmoovio\ach\fileHeader.go:198.2,198.60 1 1 +github.com\bkmoovio\ach\fileHeader.go:208.2,208.12 1 1 +github.com\bkmoovio\ach\fileHeader.go:163.44,165.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:166.26,169.3 2 1 +github.com\bkmoovio\ach\fileHeader.go:170.66,172.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:173.33,176.3 2 1 +github.com\bkmoovio\ach\fileHeader.go:177.28,179.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:180.31,182.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:183.26,185.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:186.71,188.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:189.39,191.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:192.44,194.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:195.66,197.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:198.60,200.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:213.46,214.25 1 1 +github.com\bkmoovio\ach\fileHeader.go:217.2,217.35 1 1 +github.com\bkmoovio\ach\fileHeader.go:220.2,220.30 1 1 +github.com\bkmoovio\ach\fileHeader.go:223.2,223.34 1 1 +github.com\bkmoovio\ach\fileHeader.go:226.2,226.29 1 1 +github.com\bkmoovio\ach\fileHeader.go:229.2,229.25 1 1 +github.com\bkmoovio\ach\fileHeader.go:232.2,232.29 1 1 +github.com\bkmoovio\ach\fileHeader.go:235.2,235.25 1 1 +github.com\bkmoovio\ach\fileHeader.go:238.2,238.12 1 1 +github.com\bkmoovio\ach\fileHeader.go:214.25,216.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:217.35,219.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:220.30,222.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:223.34,225.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:226.29,228.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:229.25,231.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:232.29,234.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:235.25,237.3 1 1 +github.com\bkmoovio\ach\fileHeader.go:242.58,244.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:247.53,249.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:252.54,254.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:257.54,259.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:262.62,264.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:267.57,269.2 1 1 +github.com\bkmoovio\ach\fileHeader.go:272.51,274.2 1 1 +github.com\bkmoovio\ach\writer.go:24.37,28.2 1 1 +github.com\bkmoovio\ach\writer.go:31.42,32.40 1 1 +github.com\bkmoovio\ach\writer.go:36.2,38.72 2 1 +github.com\bkmoovio\ach\writer.go:41.2,43.37 2 1 +github.com\bkmoovio\ach\writer.go:65.2,65.73 1 1 +github.com\bkmoovio\ach\writer.go:68.2,71.64 2 1 +github.com\bkmoovio\ach\writer.go:77.2,77.12 1 1 +github.com\bkmoovio\ach\writer.go:32.40,34.3 1 0 +github.com\bkmoovio\ach\writer.go:38.72,40.3 1 0 +github.com\bkmoovio\ach\writer.go:43.37,44.79 1 1 +github.com\bkmoovio\ach\writer.go:47.3,48.44 2 1 +github.com\bkmoovio\ach\writer.go:60.3,60.80 1 1 +github.com\bkmoovio\ach\writer.go:63.3,63.14 1 1 +github.com\bkmoovio\ach\writer.go:44.79,46.4 1 0 +github.com\bkmoovio\ach\writer.go:48.44,49.68 1 1 +github.com\bkmoovio\ach\writer.go:52.4,53.43 2 1 +github.com\bkmoovio\ach\writer.go:49.68,51.5 1 0 +github.com\bkmoovio\ach\writer.go:53.43,54.71 1 1 +github.com\bkmoovio\ach\writer.go:57.5,57.16 1 1 +github.com\bkmoovio\ach\writer.go:54.71,56.6 1 0 +github.com\bkmoovio\ach\writer.go:60.80,62.4 1 0 +github.com\bkmoovio\ach\writer.go:65.73,67.3 1 0 +github.com\bkmoovio\ach\writer.go:71.64,72.76 1 1 +github.com\bkmoovio\ach\writer.go:72.76,74.4 1 0 +github.com\bkmoovio\ach\writer.go:82.26,84.2 1 1 +github.com\bkmoovio\ach\writer.go:87.32,90.2 2 0 +github.com\bkmoovio\ach\writer.go:93.48,94.29 1 1 +github.com\bkmoovio\ach\writer.go:102.2,102.20 1 1 +github.com\bkmoovio\ach\writer.go:94.29,98.17 2 1 +github.com\bkmoovio\ach\writer.go:98.17,100.4 1 0 +github.com\bkmoovio\ach\batchBOC.go:32.45,37.2 4 1 +github.com\bkmoovio\ach\batchBOC.go:40.41,42.39 1 1 +github.com\bkmoovio\ach\batchBOC.go:48.2,48.48 1 1 +github.com\bkmoovio\ach\batchBOC.go:53.2,53.50 1 1 +github.com\bkmoovio\ach\batchBOC.go:59.2,59.39 1 1 +github.com\bkmoovio\ach\batchBOC.go:65.2,65.38 1 1 +github.com\bkmoovio\ach\batchBOC.go:84.2,84.12 1 1 +github.com\bkmoovio\ach\batchBOC.go:42.39,44.3 1 1 +github.com\bkmoovio\ach\batchBOC.go:48.48,50.3 1 1 +github.com\bkmoovio\ach\batchBOC.go:53.50,56.3 2 1 +github.com\bkmoovio\ach\batchBOC.go:60.21,62.101 2 1 +github.com\bkmoovio\ach\batchBOC.go:65.38,67.35 1 1 +github.com\bkmoovio\ach\batchBOC.go:73.3,73.29 1 1 +github.com\bkmoovio\ach\batchBOC.go:79.3,79.39 1 1 +github.com\bkmoovio\ach\batchBOC.go:67.35,70.4 2 1 +github.com\bkmoovio\ach\batchBOC.go:73.29,76.4 2 1 +github.com\bkmoovio\ach\batchBOC.go:79.39,82.4 2 1 +github.com\bkmoovio\ach\batchBOC.go:88.39,90.38 1 1 +github.com\bkmoovio\ach\batchBOC.go:96.2,96.25 1 1 +github.com\bkmoovio\ach\batchBOC.go:90.38,92.3 1 1 +github.com\bkmoovio\ach\batchCCD.go:19.45,24.2 4 1 +github.com\bkmoovio\ach\batchCCD.go:27.41,29.39 1 1 +github.com\bkmoovio\ach\batchCCD.go:34.2,34.48 1 1 +github.com\bkmoovio\ach\batchCCD.go:37.2,37.47 1 1 +github.com\bkmoovio\ach\batchCCD.go:42.2,42.50 1 1 +github.com\bkmoovio\ach\batchCCD.go:47.2,47.12 1 1 +github.com\bkmoovio\ach\batchCCD.go:29.39,31.3 1 1 +github.com\bkmoovio\ach\batchCCD.go:34.48,36.3 1 1 +github.com\bkmoovio\ach\batchCCD.go:37.47,39.3 1 1 +github.com\bkmoovio\ach\batchCCD.go:42.50,45.3 2 1 +github.com\bkmoovio\ach\batchCCD.go:51.39,53.38 1 1 +github.com\bkmoovio\ach\batchCCD.go:57.2,57.25 1 1 +github.com\bkmoovio\ach\batchCCD.go:53.38,55.3 1 1 +github.com\bkmoovio\ach\batchPPD.go:13.45,18.2 4 1 +github.com\bkmoovio\ach\batchPPD.go:21.41,23.39 1 1 +github.com\bkmoovio\ach\batchPPD.go:29.2,29.48 1 1 +github.com\bkmoovio\ach\batchPPD.go:32.2,32.47 1 1 +github.com\bkmoovio\ach\batchPPD.go:38.2,38.12 1 1 +github.com\bkmoovio\ach\batchPPD.go:23.39,25.3 1 1 +github.com\bkmoovio\ach\batchPPD.go:29.48,31.3 1 0 +github.com\bkmoovio\ach\batchPPD.go:32.47,34.3 1 1 +github.com\bkmoovio\ach\batchPPD.go:42.39,44.38 1 1 +github.com\bkmoovio\ach\batchPPD.go:50.2,50.25 1 1 +github.com\bkmoovio\ach\batchPPD.go:44.38,46.3 1 1 +github.com\bkmoovio\ach\file.go:50.36,52.2 1 1 +github.com\bkmoovio\ach\file.go:70.22,75.2 1 1 +github.com\bkmoovio\ach\file.go:78.31,80.44 1 1 +github.com\bkmoovio\ach\file.go:84.2,84.25 1 1 +github.com\bkmoovio\ach\file.go:88.2,94.34 7 1 +github.com\bkmoovio\ach\file.go:110.2,113.36 3 1 +github.com\bkmoovio\ach\file.go:118.2,124.12 6 1 +github.com\bkmoovio\ach\file.go:80.44,82.3 1 1 +github.com\bkmoovio\ach\file.go:84.25,86.3 1 1 +github.com\bkmoovio\ach\file.go:94.34,108.3 8 1 +github.com\bkmoovio\ach\file.go:113.36,115.3 1 1 +github.com\bkmoovio\ach\file.go:115.3,117.3 1 1 +github.com\bkmoovio\ach\file.go:128.50,129.22 1 1 +github.com\bkmoovio\ach\file.go:133.2,133.40 1 1 +github.com\bkmoovio\ach\file.go:136.2,137.18 2 1 +github.com\bkmoovio\ach\file.go:130.17,131.77 1 1 +github.com\bkmoovio\ach\file.go:133.40,135.3 1 1 +github.com\bkmoovio\ach\file.go:141.46,144.2 2 1 +github.com\bkmoovio\ach\file.go:147.33,149.44 1 1 +github.com\bkmoovio\ach\file.go:154.2,154.48 1 1 +github.com\bkmoovio\ach\file.go:158.2,158.41 1 1 +github.com\bkmoovio\ach\file.go:162.2,162.24 1 1 +github.com\bkmoovio\ach\file.go:149.44,152.3 2 1 +github.com\bkmoovio\ach\file.go:154.48,156.3 1 1 +github.com\bkmoovio\ach\file.go:158.41,160.3 1 1 +github.com\bkmoovio\ach\file.go:167.44,170.34 2 1 +github.com\bkmoovio\ach\file.go:173.2,173.42 1 1 +github.com\bkmoovio\ach\file.go:177.2,177.12 1 1 +github.com\bkmoovio\ach\file.go:170.34,172.3 1 1 +github.com\bkmoovio\ach\file.go:173.42,176.3 2 1 +github.com\bkmoovio\ach\file.go:182.37,185.34 3 1 +github.com\bkmoovio\ach\file.go:189.2,189.58 1 1 +github.com\bkmoovio\ach\file.go:193.2,193.60 1 1 +github.com\bkmoovio\ach\file.go:197.2,197.12 1 1 +github.com\bkmoovio\ach\file.go:185.34,188.3 2 1 +github.com\bkmoovio\ach\file.go:189.58,192.3 2 1 +github.com\bkmoovio\ach\file.go:193.60,196.3 2 1 +github.com\bkmoovio\ach\file.go:201.36,203.45 2 1 +github.com\bkmoovio\ach\file.go:207.2,207.12 1 1 +github.com\bkmoovio\ach\file.go:203.45,206.3 2 1 +github.com\bkmoovio\ach\file.go:212.44,214.34 2 1 +github.com\bkmoovio\ach\file.go:217.2,217.33 1 1 +github.com\bkmoovio\ach\file.go:214.34,216.3 1 1 +github.com\bkmoovio\ach\fileControl.go:44.45,62.2 8 1 +github.com\bkmoovio\ach\fileControl.go:65.35,70.2 1 1 +github.com\bkmoovio\ach\fileControl.go:73.40,84.2 1 1 +github.com\bkmoovio\ach\fileControl.go:88.41,89.44 1 1 +github.com\bkmoovio\ach\fileControl.go:92.2,92.26 1 1 +github.com\bkmoovio\ach\fileControl.go:96.2,96.12 1 1 +github.com\bkmoovio\ach\fileControl.go:89.44,91.3 1 1 +github.com\bkmoovio\ach\fileControl.go:92.26,95.3 2 1 +github.com\bkmoovio\ach\fileControl.go:101.47,102.25 1 1 +github.com\bkmoovio\ach\fileControl.go:105.2,105.24 1 1 +github.com\bkmoovio\ach\fileControl.go:108.2,108.24 1 1 +github.com\bkmoovio\ach\fileControl.go:111.2,111.31 1 1 +github.com\bkmoovio\ach\fileControl.go:114.2,114.23 1 1 +github.com\bkmoovio\ach\fileControl.go:117.2,117.12 1 1 +github.com\bkmoovio\ach\fileControl.go:102.25,104.3 1 1 +github.com\bkmoovio\ach\fileControl.go:105.24,107.3 1 1 +github.com\bkmoovio\ach\fileControl.go:108.24,110.3 1 1 +github.com\bkmoovio\ach\fileControl.go:111.31,113.3 1 1 +github.com\bkmoovio\ach\fileControl.go:114.23,116.3 1 1 +github.com\bkmoovio\ach\fileControl.go:121.49,123.2 1 1 +github.com\bkmoovio\ach\fileControl.go:126.49,128.2 1 1 +github.com\bkmoovio\ach\fileControl.go:131.56,133.2 1 1 +github.com\bkmoovio\ach\fileControl.go:136.48,138.2 1 1 +github.com\bkmoovio\ach\fileControl.go:141.72,143.2 1 1 +github.com\bkmoovio\ach\fileControl.go:146.73,148.2 1 1 +github.com\bkmoovio\ach\reader.go:22.37,23.20 1 1 +github.com\bkmoovio\ach\reader.go:26.2,26.79 1 1 +github.com\bkmoovio\ach\reader.go:23.20,25.3 1 1 +github.com\bkmoovio\ach\reader.go:46.41,52.2 1 1 +github.com\bkmoovio\ach\reader.go:56.49,58.2 1 1 +github.com\bkmoovio\ach\reader.go:61.37,65.2 1 1 +github.com\bkmoovio\ach\reader.go:70.39,73.23 2 1 +github.com\bkmoovio\ach\reader.go:94.2,94.37 1 1 +github.com\bkmoovio\ach\reader.go:99.2,99.39 1 1 +github.com\bkmoovio\ach\reader.go:105.2,105.20 1 1 +github.com\bkmoovio\ach\reader.go:73.23,78.10 4 1 +github.com\bkmoovio\ach\reader.go:79.84,80.57 1 1 +github.com\bkmoovio\ach\reader.go:83.35,86.31 3 1 +github.com\bkmoovio\ach\reader.go:87.11,89.40 2 1 +github.com\bkmoovio\ach\reader.go:80.57,82.5 1 1 +github.com\bkmoovio\ach\reader.go:89.40,91.5 1 1 +github.com\bkmoovio\ach\reader.go:94.37,98.3 2 1 +github.com\bkmoovio\ach\reader.go:99.39,103.3 2 1 +github.com\bkmoovio\ach\reader.go:108.60,111.26 2 1 +github.com\bkmoovio\ach\reader.go:121.2,121.12 1 1 +github.com\bkmoovio\ach\reader.go:111.26,113.39 2 1 +github.com\bkmoovio\ach\reader.go:113.39,115.40 2 1 +github.com\bkmoovio\ach\reader.go:118.4,118.15 1 1 +github.com\bkmoovio\ach\reader.go:115.40,117.5 1 1 +github.com\bkmoovio\ach\reader.go:124.36,125.20 1 1 +github.com\bkmoovio\ach\reader.go:164.2,164.12 1 1 +github.com\bkmoovio\ach\reader.go:126.21,127.45 1 1 +github.com\bkmoovio\ach\reader.go:130.22,131.46 1 1 +github.com\bkmoovio\ach\reader.go:134.22,135.46 1 1 +github.com\bkmoovio\ach\reader.go:138.23,139.42 1 1 +github.com\bkmoovio\ach\reader.go:142.23,143.47 1 1 +github.com\bkmoovio\ach\reader.go:146.3,146.51 1 1 +github.com\bkmoovio\ach\reader.go:150.3,151.23 2 1 +github.com\bkmoovio\ach\reader.go:152.22,153.25 1 1 +github.com\bkmoovio\ach\reader.go:157.3,157.46 1 1 +github.com\bkmoovio\ach\reader.go:160.10,162.83 2 1 +github.com\bkmoovio\ach\reader.go:127.45,129.4 1 1 +github.com\bkmoovio\ach\reader.go:131.46,133.4 1 1 +github.com\bkmoovio\ach\reader.go:135.46,137.4 1 1 +github.com\bkmoovio\ach\reader.go:139.42,141.4 1 1 +github.com\bkmoovio\ach\reader.go:143.47,145.4 1 1 +github.com\bkmoovio\ach\reader.go:146.51,149.4 2 1 +github.com\bkmoovio\ach\reader.go:153.25,155.9 1 1 +github.com\bkmoovio\ach\reader.go:157.46,159.4 1 1 +github.com\bkmoovio\ach\reader.go:168.42,170.37 2 1 +github.com\bkmoovio\ach\reader.go:174.2,176.49 2 1 +github.com\bkmoovio\ach\reader.go:179.2,179.12 1 1 +github.com\bkmoovio\ach\reader.go:170.37,173.3 1 1 +github.com\bkmoovio\ach\reader.go:176.49,178.3 1 1 +github.com\bkmoovio\ach\reader.go:183.43,185.27 2 1 +github.com\bkmoovio\ach\reader.go:191.2,193.38 3 1 +github.com\bkmoovio\ach\reader.go:198.2,199.16 2 1 +github.com\bkmoovio\ach\reader.go:203.2,204.12 2 1 +github.com\bkmoovio\ach\reader.go:185.27,188.3 1 1 +github.com\bkmoovio\ach\reader.go:193.38,195.3 1 1 +github.com\bkmoovio\ach\reader.go:199.16,201.3 1 0 +github.com\bkmoovio\ach\reader.go:208.43,210.27 2 1 +github.com\bkmoovio\ach\reader.go:213.2,215.38 3 1 +github.com\bkmoovio\ach\reader.go:218.2,219.12 2 1 +github.com\bkmoovio\ach\reader.go:210.27,212.3 1 1 +github.com\bkmoovio\ach\reader.go:215.38,217.3 1 0 +github.com\bkmoovio\ach\reader.go:223.39,226.27 2 1 +github.com\bkmoovio\ach\reader.go:230.2,230.43 1 1 +github.com\bkmoovio\ach\reader.go:233.2,236.39 3 1 +github.com\bkmoovio\ach\reader.go:265.2,265.12 1 1 +github.com\bkmoovio\ach\reader.go:226.27,229.3 2 1 +github.com\bkmoovio\ach\reader.go:230.43,232.3 1 1 +github.com\bkmoovio\ach\reader.go:236.39,237.22 1 1 +github.com\bkmoovio\ach\reader.go:238.13,241.47 3 1 +github.com\bkmoovio\ach\reader.go:244.4,244.65 1 1 +github.com\bkmoovio\ach\reader.go:245.13,248.47 3 1 +github.com\bkmoovio\ach\reader.go:251.4,251.65 1 1 +github.com\bkmoovio\ach\reader.go:252.13,255.47 3 1 +github.com\bkmoovio\ach\reader.go:258.4,258.65 1 1 +github.com\bkmoovio\ach\reader.go:241.47,243.5 1 1 +github.com\bkmoovio\ach\reader.go:248.47,250.5 1 0 +github.com\bkmoovio\ach\reader.go:255.47,257.5 1 0 +github.com\bkmoovio\ach\reader.go:260.3,263.3 2 1 +github.com\bkmoovio\ach\reader.go:269.44,271.27 2 1 +github.com\bkmoovio\ach\reader.go:275.2,276.63 2 1 +github.com\bkmoovio\ach\reader.go:279.2,279.12 1 1 +github.com\bkmoovio\ach\reader.go:271.27,274.3 1 1 +github.com\bkmoovio\ach\reader.go:276.63,278.3 1 1 +github.com\bkmoovio\ach\reader.go:283.43,285.39 2 1 +github.com\bkmoovio\ach\reader.go:289.2,290.50 2 1 +github.com\bkmoovio\ach\reader.go:293.2,293.12 1 1 +github.com\bkmoovio\ach\reader.go:285.39,288.3 1 1 +github.com\bkmoovio\ach\reader.go:290.50,292.3 1 1 +github.com\bkmoovio\ach\validators.go:34.37,36.2 1 1 +github.com\bkmoovio\ach\validators.go:53.52,54.14 1 1 +github.com\bkmoovio\ach\validators.go:66.2,66.36 1 1 +github.com\bkmoovio\ach\validators.go:63.7,64.13 1 1 +github.com\bkmoovio\ach\validators.go:70.50,71.14 1 1 +github.com\bkmoovio\ach\validators.go:77.2,77.31 1 1 +github.com\bkmoovio\ach\validators.go:74.86,75.13 1 1 +github.com\bkmoovio\ach\validators.go:83.51,84.14 1 1 +github.com\bkmoovio\ach\validators.go:100.2,100.39 1 1 +github.com\bkmoovio\ach\validators.go:97.8,98.13 1 1 +github.com\bkmoovio\ach\validators.go:121.55,122.14 1 1 +github.com\bkmoovio\ach\validators.go:229.2,229.39 1 1 +github.com\bkmoovio\ach\validators.go:226.6,227.13 1 1 +github.com\bkmoovio\ach\validators.go:233.60,234.14 1 1 +github.com\bkmoovio\ach\validators.go:244.2,244.38 1 1 +github.com\bkmoovio\ach\validators.go:241.5,242.13 1 1 +github.com\bkmoovio\ach\validators.go:248.57,249.43 1 1 +github.com\bkmoovio\ach\validators.go:252.2,252.12 1 1 +github.com\bkmoovio\ach\validators.go:249.43,251.3 1 1 +github.com\bkmoovio\ach\validators.go:256.52,257.38 1 1 +github.com\bkmoovio\ach\validators.go:261.2,261.12 1 1 +github.com\bkmoovio\ach\validators.go:257.38,260.3 1 1 +github.com\bkmoovio\ach\validators.go:271.67,273.25 2 1 +github.com\bkmoovio\ach\validators.go:276.2,293.31 17 1 +github.com\bkmoovio\ach\validators.go:273.25,275.3 1 1 +github.com\bkmoovio\ach\validators.go:297.42,299.2 1 1 +github.com\bkmoovio\ach\batch.go:28.49,29.35 1 1 +github.com\bkmoovio\ach\batch.go:52.2,53.71 2 1 +github.com\bkmoovio\ach\batch.go:30.13,31.30 1 1 +github.com\bkmoovio\ach\batch.go:32.13,33.30 1 1 +github.com\bkmoovio\ach\batch.go:34.13,35.30 1 1 +github.com\bkmoovio\ach\batch.go:36.13,37.30 1 1 +github.com\bkmoovio\ach\batch.go:38.13,39.30 1 1 +github.com\bkmoovio\ach\batch.go:40.13,41.30 1 1 +github.com\bkmoovio\ach\batch.go:42.13,43.30 1 1 +github.com\bkmoovio\ach\batch.go:44.13,45.30 1 1 +github.com\bkmoovio\ach\batch.go:46.13,47.30 1 1 +github.com\bkmoovio\ach\batch.go:48.13,49.30 1 1 +github.com\bkmoovio\ach\batch.go:50.10,50.10 0 1 +github.com\bkmoovio\ach\batch.go:57.36,61.49 2 1 +github.com\bkmoovio\ach\batch.go:69.2,69.69 1 1 +github.com\bkmoovio\ach\batch.go:74.2,74.79 1 1 +github.com\bkmoovio\ach\batch.go:79.2,79.73 1 1 +github.com\bkmoovio\ach\batch.go:84.2,84.59 1 1 +github.com\bkmoovio\ach\batch.go:89.2,89.50 1 1 +github.com\bkmoovio\ach\batch.go:93.2,93.52 1 1 +github.com\bkmoovio\ach\batch.go:97.2,97.46 1 1 +github.com\bkmoovio\ach\batch.go:101.2,101.44 1 1 +github.com\bkmoovio\ach\batch.go:105.2,105.48 1 1 +github.com\bkmoovio\ach\batch.go:109.2,109.50 1 1 +github.com\bkmoovio\ach\batch.go:113.2,113.50 1 1 +github.com\bkmoovio\ach\batch.go:116.2,116.27 1 1 +github.com\bkmoovio\ach\batch.go:61.49,63.37 1 1 +github.com\bkmoovio\ach\batch.go:66.3,66.90 1 0 +github.com\bkmoovio\ach\batch.go:63.37,65.4 1 1 +github.com\bkmoovio\ach\batch.go:69.69,72.3 2 1 +github.com\bkmoovio\ach\batch.go:74.79,77.3 2 1 +github.com\bkmoovio\ach\batch.go:79.73,82.3 2 1 +github.com\bkmoovio\ach\batch.go:84.59,87.3 2 1 +github.com\bkmoovio\ach\batch.go:89.50,91.3 1 1 +github.com\bkmoovio\ach\batch.go:93.52,95.3 1 1 +github.com\bkmoovio\ach\batch.go:97.46,99.3 1 1 +github.com\bkmoovio\ach\batch.go:101.44,103.3 1 1 +github.com\bkmoovio\ach\batch.go:105.48,107.3 1 1 +github.com\bkmoovio\ach\batch.go:109.50,111.3 1 1 +github.com\bkmoovio\ach\batch.go:113.50,115.3 1 1 +github.com\bkmoovio\ach\batch.go:121.35,123.48 1 1 +github.com\bkmoovio\ach\batch.go:126.2,126.29 1 1 +github.com\bkmoovio\ach\batch.go:130.2,132.38 3 1 +github.com\bkmoovio\ach\batch.go:161.2,171.12 10 1 +github.com\bkmoovio\ach\batch.go:123.48,125.3 1 1 +github.com\bkmoovio\ach\batch.go:126.29,128.3 1 1 +github.com\bkmoovio\ach\batch.go:132.38,135.17 3 1 +github.com\bkmoovio\ach\batch.go:139.3,140.17 2 1 +github.com\bkmoovio\ach\batch.go:145.3,145.48 1 1 +github.com\bkmoovio\ach\batch.go:148.3,150.33 3 1 +github.com\bkmoovio\ach\batch.go:135.17,137.4 1 0 +github.com\bkmoovio\ach\batch.go:140.17,142.4 1 0 +github.com\bkmoovio\ach\batch.go:145.48,147.4 1 1 +github.com\bkmoovio\ach\batch.go:150.33,152.62 1 1 +github.com\bkmoovio\ach\batch.go:156.4,156.16 1 1 +github.com\bkmoovio\ach\batch.go:152.62,155.5 2 1 +github.com\bkmoovio\ach\batch.go:175.57,177.2 1 1 +github.com\bkmoovio\ach\batch.go:180.46,182.2 1 1 +github.com\bkmoovio\ach\batch.go:185.60,187.2 1 1 +github.com\bkmoovio\ach\batch.go:190.48,192.2 1 1 +github.com\bkmoovio\ach\batch.go:195.49,197.2 1 1 +github.com\bkmoovio\ach\batch.go:200.50,203.2 2 1 +github.com\bkmoovio\ach\batch.go:206.39,208.2 1 1 +github.com\bkmoovio\ach\batch.go:211.46,212.48 1 1 +github.com\bkmoovio\ach\batch.go:215.2,215.38 1 1 +github.com\bkmoovio\ach\batch.go:225.2,225.33 1 1 +github.com\bkmoovio\ach\batch.go:212.48,214.3 1 1 +github.com\bkmoovio\ach\batch.go:215.38,216.42 1 1 +github.com\bkmoovio\ach\batch.go:219.3,219.42 1 1 +github.com\bkmoovio\ach\batch.go:216.42,218.4 1 1 +github.com\bkmoovio\ach\batch.go:219.42,220.45 1 1 +github.com\bkmoovio\ach\batch.go:220.45,222.5 1 1 +github.com\bkmoovio\ach\batch.go:231.47,233.38 2 1 +github.com\bkmoovio\ach\batch.go:236.2,236.51 1 1 +github.com\bkmoovio\ach\batch.go:240.2,240.12 1 1 +github.com\bkmoovio\ach\batch.go:233.38,235.3 1 1 +github.com\bkmoovio\ach\batch.go:236.51,239.3 2 1 +github.com\bkmoovio\ach\batch.go:246.43,248.56 2 1 +github.com\bkmoovio\ach\batch.go:253.2,253.58 1 1 +github.com\bkmoovio\ach\batch.go:257.2,257.12 1 1 +github.com\bkmoovio\ach\batch.go:248.56,251.3 2 1 +github.com\bkmoovio\ach\batch.go:253.58,256.3 2 1 +github.com\bkmoovio\ach\batch.go:260.69,261.38 1 1 +github.com\bkmoovio\ach\batch.go:269.2,269.22 1 1 +github.com\bkmoovio\ach\batch.go:261.38,262.158 1 1 +github.com\bkmoovio\ach\batch.go:265.3,265.189 1 1 +github.com\bkmoovio\ach\batch.go:262.158,264.4 1 1 +github.com\bkmoovio\ach\batch.go:265.189,267.4 1 1 +github.com\bkmoovio\ach\batch.go:274.49,276.38 2 1 +github.com\bkmoovio\ach\batch.go:283.2,283.12 1 1 +github.com\bkmoovio\ach\batch.go:276.38,277.35 1 1 +github.com\bkmoovio\ach\batch.go:281.3,281.30 1 1 +github.com\bkmoovio\ach\batch.go:277.35,280.4 2 1 +github.com\bkmoovio\ach\batch.go:287.41,289.49 2 1 +github.com\bkmoovio\ach\batch.go:293.2,293.12 1 1 +github.com\bkmoovio\ach\batch.go:289.49,292.3 2 1 +github.com\bkmoovio\ach\batch.go:298.49,300.38 2 1 +github.com\bkmoovio\ach\batch.go:306.2,306.37 1 1 +github.com\bkmoovio\ach\batch.go:300.38,305.3 2 1 +github.com\bkmoovio\ach\batch.go:310.45,311.44 1 1 +github.com\bkmoovio\ach\batch.go:319.2,319.12 1 1 +github.com\bkmoovio\ach\batch.go:311.44,312.39 1 1 +github.com\bkmoovio\ach\batch.go:312.39,313.66 1 1 +github.com\bkmoovio\ach\batch.go:313.66,316.5 2 1 +github.com\bkmoovio\ach\batch.go:324.47,325.38 1 1 +github.com\bkmoovio\ach\batch.go:332.2,332.12 1 1 +github.com\bkmoovio\ach\batch.go:325.38,326.77 1 1 +github.com\bkmoovio\ach\batch.go:326.77,329.4 2 1 +github.com\bkmoovio\ach\batch.go:336.47,337.38 1 1 +github.com\bkmoovio\ach\batch.go:363.2,363.12 1 1 +github.com\bkmoovio\ach\batch.go:337.38,338.30 1 1 +github.com\bkmoovio\ach\batch.go:338.30,340.41 1 1 +github.com\bkmoovio\ach\batch.go:343.4,345.43 2 1 +github.com\bkmoovio\ach\batch.go:340.41,342.5 1 1 +github.com\bkmoovio\ach\batch.go:345.43,347.42 1 1 +github.com\bkmoovio\ach\batch.go:347.42,349.36 1 1 +github.com\bkmoovio\ach\batch.go:353.6,355.79 2 1 +github.com\bkmoovio\ach\batch.go:349.36,352.7 2 1 +github.com\bkmoovio\ach\batch.go:355.79,358.7 2 1 +github.com\bkmoovio\ach\batch.go:369.53,370.38 1 1 +github.com\bkmoovio\ach\batch.go:377.2,377.12 1 1 +github.com\bkmoovio\ach\batch.go:370.38,371.34 1 1 +github.com\bkmoovio\ach\batch.go:371.34,374.4 2 1 +github.com\bkmoovio\ach\batch.go:381.55,382.38 1 1 +github.com\bkmoovio\ach\batch.go:390.2,390.12 1 1 +github.com\bkmoovio\ach\batch.go:382.38,383.42 1 1 +github.com\bkmoovio\ach\batch.go:383.42,384.38 1 1 +github.com\bkmoovio\ach\batch.go:384.38,387.5 2 1 +github.com\bkmoovio\ach\batch.go:394.40,396.28 2 1 +github.com\bkmoovio\ach\batch.go:406.2,406.12 1 1 +github.com\bkmoovio\ach\batch.go:396.28,397.43 1 1 +github.com\bkmoovio\ach\batch.go:397.43,398.48 1 1 +github.com\bkmoovio\ach\batch.go:401.4,401.45 1 1 +github.com\bkmoovio\ach\batch.go:398.48,399.13 1 0 +github.com\bkmoovio\ach\batch.go:401.45,403.5 1 1 +github.com\bkmoovio\ach\batch.go:412.47,413.38 1 1 +github.com\bkmoovio\ach\batch.go:420.2,420.12 1 1 +github.com\bkmoovio\ach\batch.go:413.38,414.141 1 1 +github.com\bkmoovio\ach\batch.go:414.141,418.4 2 0 +github.com\bkmoovio\ach\batchCOR.go:23.45,28.2 4 1 +github.com\bkmoovio\ach\batchCOR.go:31.41,33.39 1 1 +github.com\bkmoovio\ach\batchCOR.go:38.2,38.44 1 1 +github.com\bkmoovio\ach\batchCOR.go:43.2,43.50 1 1 +github.com\bkmoovio\ach\batchCOR.go:50.2,50.103 1 1 +github.com\bkmoovio\ach\batchCOR.go:55.2,55.38 1 1 +github.com\bkmoovio\ach\batchCOR.go:66.2,66.12 1 1 +github.com\bkmoovio\ach\batchCOR.go:33.39,35.3 1 0 +github.com\bkmoovio\ach\batchCOR.go:38.44,40.3 1 1 +github.com\bkmoovio\ach\batchCOR.go:43.50,46.3 2 0 +github.com\bkmoovio\ach\batchCOR.go:50.103,53.3 2 1 +github.com\bkmoovio\ach\batchCOR.go:55.38,59.56 1 1 +github.com\bkmoovio\ach\batchCOR.go:59.56,62.4 2 1 +github.com\bkmoovio\ach\batchCOR.go:70.39,72.38 1 1 +github.com\bkmoovio\ach\batchCOR.go:76.2,76.25 1 1 +github.com\bkmoovio\ach\batchCOR.go:72.38,74.3 1 1 +github.com\bkmoovio\ach\batchCOR.go:80.44,81.38 1 1 +github.com\bkmoovio\ach\batchCOR.go:100.2,100.12 1 1 +github.com\bkmoovio\ach\batchCOR.go:81.38,83.31 1 1 +github.com\bkmoovio\ach\batchCOR.go:87.3,88.10 2 1 +github.com\bkmoovio\ach\batchCOR.go:93.3,93.46 1 1 +github.com\bkmoovio\ach\batchCOR.go:83.31,85.4 1 1 +github.com\bkmoovio\ach\batchCOR.go:88.10,91.4 2 1 +github.com\bkmoovio\ach\batchCOR.go:93.46,95.38 1 1 +github.com\bkmoovio\ach\batchCOR.go:95.38,97.5 1 1 +github.com\bkmoovio\ach\batchPOS.go:31.45,36.2 4 1 +github.com\bkmoovio\ach\batchPOS.go:39.41,41.39 1 1 +github.com\bkmoovio\ach\batchPOS.go:47.2,47.44 1 1 +github.com\bkmoovio\ach\batchPOS.go:51.2,51.50 1 1 +github.com\bkmoovio\ach\batchPOS.go:64.2,64.38 1 1 +github.com\bkmoovio\ach\batchPOS.go:72.2,72.12 1 1 +github.com\bkmoovio\ach\batchPOS.go:41.39,43.3 1 1 +github.com\bkmoovio\ach\batchPOS.go:47.44,49.3 1 1 +github.com\bkmoovio\ach\batchPOS.go:51.50,54.3 2 1 +github.com\bkmoovio\ach\batchPOS.go:64.38,66.35 1 1 +github.com\bkmoovio\ach\batchPOS.go:66.35,69.4 2 1 +github.com\bkmoovio\ach\batchPOS.go:76.39,78.38 1 1 +github.com\bkmoovio\ach\batchPOS.go:84.2,84.25 1 1 +github.com\bkmoovio\ach\batchPOS.go:78.38,80.3 1 1 +github.com\bkmoovio\ach\batchPOS.go:88.44,89.38 1 1 +github.com\bkmoovio\ach\batchPOS.go:108.2,108.12 1 1 +github.com\bkmoovio\ach\batchPOS.go:89.38,91.31 1 1 +github.com\bkmoovio\ach\batchPOS.go:95.3,96.10 2 1 +github.com\bkmoovio\ach\batchPOS.go:101.3,101.46 1 1 +github.com\bkmoovio\ach\batchPOS.go:91.31,93.4 1 1 +github.com\bkmoovio\ach\batchPOS.go:96.10,99.4 2 1 +github.com\bkmoovio\ach\batchPOS.go:101.46,103.38 1 1 +github.com\bkmoovio\ach\batchPOS.go:103.38,105.5 1 1 +github.com\bkmoovio\ach\addenda99.go:28.13,31.2 1 1 +github.com\bkmoovio\ach\addenda99.go:71.32,77.2 2 1 +github.com\bkmoovio\ach\addenda99.go:80.50,97.2 8 1 +github.com\bkmoovio\ach\addenda99.go:100.45,111.2 1 1 +github.com\bkmoovio\ach\addenda99.go:114.46,116.33 1 1 +github.com\bkmoovio\ach\addenda99.go:122.2,123.9 2 1 +github.com\bkmoovio\ach\addenda99.go:127.2,127.12 1 1 +github.com\bkmoovio\ach\addenda99.go:116.33,119.3 2 0 +github.com\bkmoovio\ach\addenda99.go:123.9,126.3 1 1 +github.com\bkmoovio\ach\addenda99.go:131.47,133.2 1 1 +github.com\bkmoovio\ach\addenda99.go:136.57,138.2 1 1 +github.com\bkmoovio\ach\addenda99.go:141.55,143.36 1 1 +github.com\bkmoovio\ach\addenda99.go:147.2,147.58 1 1 +github.com\bkmoovio\ach\addenda99.go:143.36,145.3 1 1 +github.com\bkmoovio\ach\addenda99.go:151.55,153.2 1 1 +github.com\bkmoovio\ach\addenda99.go:156.62,158.2 1 1 +github.com\bkmoovio\ach\addenda99.go:161.55,163.2 1 1 +github.com\bkmoovio\ach\addenda99.go:165.50,217.29 3 1 +github.com\bkmoovio\ach\addenda99.go:220.2,220.13 1 1 +github.com\bkmoovio\ach\addenda99.go:217.29,219.3 1 1 +github.com\bkmoovio\ach\batchControl.go:74.46,98.2 11 1 +github.com\bkmoovio\ach\batchControl.go:101.38,108.2 1 1 +github.com\bkmoovio\ach\batchControl.go:111.41,125.2 1 1 +github.com\bkmoovio\ach\batchControl.go:129.42,130.44 1 1 +github.com\bkmoovio\ach\batchControl.go:133.2,133.26 1 1 +github.com\bkmoovio\ach\batchControl.go:137.2,137.63 1 1 +github.com\bkmoovio\ach\batchControl.go:141.2,141.68 1 1 +github.com\bkmoovio\ach\batchControl.go:145.2,145.72 1 1 +github.com\bkmoovio\ach\batchControl.go:149.2,149.12 1 1 +github.com\bkmoovio\ach\batchControl.go:130.44,132.3 1 1 +github.com\bkmoovio\ach\batchControl.go:133.26,136.3 2 1 +github.com\bkmoovio\ach\batchControl.go:137.63,139.3 1 1 +github.com\bkmoovio\ach\batchControl.go:141.68,143.3 1 1 +github.com\bkmoovio\ach\batchControl.go:145.72,147.3 1 1 +github.com\bkmoovio\ach\batchControl.go:154.48,155.25 1 1 +github.com\bkmoovio\ach\batchControl.go:158.2,158.30 1 1 +github.com\bkmoovio\ach\batchControl.go:161.2,161.42 1 1 +github.com\bkmoovio\ach\batchControl.go:164.2,164.12 1 1 +github.com\bkmoovio\ach\batchControl.go:155.25,157.3 1 1 +github.com\bkmoovio\ach\batchControl.go:158.30,160.3 1 1 +github.com\bkmoovio\ach\batchControl.go:161.42,163.3 1 1 +github.com\bkmoovio\ach\batchControl.go:168.57,170.2 1 1 +github.com\bkmoovio\ach\batchControl.go:173.49,175.2 1 1 +github.com\bkmoovio\ach\batchControl.go:178.67,180.2 1 1 +github.com\bkmoovio\ach\batchControl.go:183.68,185.2 1 1 +github.com\bkmoovio\ach\batchControl.go:188.61,190.2 1 1 +github.com\bkmoovio\ach\batchControl.go:193.65,195.2 1 1 +github.com\bkmoovio\ach\batchControl.go:198.58,200.2 1 1 +github.com\bkmoovio\ach\batchControl.go:203.51,205.2 1 1 +github.com\bkmoovio\ach\batchWeb.go:19.45,24.2 4 1 +github.com\bkmoovio\ach\batchWeb.go:27.41,29.39 1 1 +github.com\bkmoovio\ach\batchWeb.go:34.2,34.48 1 1 +github.com\bkmoovio\ach\batchWeb.go:37.2,37.47 1 1 +github.com\bkmoovio\ach\batchWeb.go:42.2,42.50 1 1 +github.com\bkmoovio\ach\batchWeb.go:47.2,47.34 1 1 +github.com\bkmoovio\ach\batchWeb.go:29.39,31.3 1 1 +github.com\bkmoovio\ach\batchWeb.go:34.48,36.3 1 1 +github.com\bkmoovio\ach\batchWeb.go:37.47,39.3 1 1 +github.com\bkmoovio\ach\batchWeb.go:42.50,45.3 2 1 +github.com\bkmoovio\ach\batchWeb.go:51.39,53.38 1 1 +github.com\bkmoovio\ach\batchWeb.go:57.2,57.25 1 1 +github.com\bkmoovio\ach\batchWeb.go:53.38,55.3 1 1 +github.com\bkmoovio\ach\addenda02.go:51.32,56.2 4 1 +github.com\bkmoovio\ach\addenda02.go:59.50,84.2 12 1 +github.com\bkmoovio\ach\addenda02.go:87.45,103.2 1 1 +github.com\bkmoovio\ach\addenda02.go:107.46,108.51 1 1 +github.com\bkmoovio\ach\addenda02.go:111.2,111.33 1 1 +github.com\bkmoovio\ach\addenda02.go:115.2,115.65 1 1 +github.com\bkmoovio\ach\addenda02.go:120.2,120.32 1 1 +github.com\bkmoovio\ach\addenda02.go:124.2,124.12 1 1 +github.com\bkmoovio\ach\addenda02.go:108.51,110.3 1 1 +github.com\bkmoovio\ach\addenda02.go:111.33,114.3 2 1 +github.com\bkmoovio\ach\addenda02.go:115.65,117.3 1 1 +github.com\bkmoovio\ach\addenda02.go:120.32,122.3 1 1 +github.com\bkmoovio\ach\addenda02.go:132.52,133.32 1 1 +github.com\bkmoovio\ach\addenda02.go:136.2,136.30 1 1 +github.com\bkmoovio\ach\addenda02.go:139.2,139.48 1 1 +github.com\bkmoovio\ach\addenda02.go:142.2,142.45 1 1 +github.com\bkmoovio\ach\addenda02.go:145.2,145.37 1 1 +github.com\bkmoovio\ach\addenda02.go:148.2,148.38 1 1 +github.com\bkmoovio\ach\addenda02.go:151.2,151.34 1 1 +github.com\bkmoovio\ach\addenda02.go:154.2,154.35 1 1 +github.com\bkmoovio\ach\addenda02.go:158.2,158.12 1 1 +github.com\bkmoovio\ach\addenda02.go:133.32,135.3 1 1 +github.com\bkmoovio\ach\addenda02.go:136.30,138.3 1 1 +github.com\bkmoovio\ach\addenda02.go:139.48,141.3 1 1 +github.com\bkmoovio\ach\addenda02.go:142.45,144.3 1 1 +github.com\bkmoovio\ach\addenda02.go:145.37,147.3 1 1 +github.com\bkmoovio\ach\addenda02.go:148.38,150.3 1 1 +github.com\bkmoovio\ach\addenda02.go:151.34,153.3 1 1 +github.com\bkmoovio\ach\addenda02.go:154.35,156.3 1 1 +github.com\bkmoovio\ach\addenda02.go:162.47,164.2 1 1 +github.com\bkmoovio\ach\addenda02.go:167.67,169.2 1 1 +github.com\bkmoovio\ach\addenda02.go:172.67,174.2 1 1 +github.com\bkmoovio\ach\addenda02.go:177.70,179.2 1 1 +github.com\bkmoovio\ach\addenda02.go:182.67,184.2 1 1 +github.com\bkmoovio\ach\addenda02.go:187.59,190.2 1 1 +github.com\bkmoovio\ach\addenda02.go:193.73,195.2 1 1 +github.com\bkmoovio\ach\addenda02.go:198.60,200.2 1 1 +github.com\bkmoovio\ach\addenda02.go:203.56,205.2 1 1 +github.com\bkmoovio\ach\addenda02.go:208.57,210.2 1 1 +github.com\bkmoovio\ach\addenda02.go:213.55,215.2 1 1 +github.com\bkmoovio\ach\addenda98.go:49.13,52.2 1 1 +github.com\bkmoovio\ach\addenda98.go:61.32,67.2 2 1 +github.com\bkmoovio\ach\addenda98.go:70.50,85.2 7 1 +github.com\bkmoovio\ach\addenda98.go:88.45,100.2 1 1 +github.com\bkmoovio\ach\addenda98.go:103.46,104.33 1 1 +github.com\bkmoovio\ach\addenda98.go:109.2,109.32 1 1 +github.com\bkmoovio\ach\addenda98.go:114.2,115.9 2 1 +github.com\bkmoovio\ach\addenda98.go:120.2,120.35 1 1 +github.com\bkmoovio\ach\addenda98.go:124.2,124.12 1 1 +github.com\bkmoovio\ach\addenda98.go:104.33,107.3 2 1 +github.com\bkmoovio\ach\addenda98.go:109.32,111.3 1 1 +github.com\bkmoovio\ach\addenda98.go:115.9,117.3 1 1 +github.com\bkmoovio\ach\addenda98.go:120.35,122.3 1 1 +github.com\bkmoovio\ach\addenda98.go:128.47,130.2 1 1 +github.com\bkmoovio\ach\addenda98.go:133.57,135.2 1 1 +github.com\bkmoovio\ach\addenda98.go:138.55,140.2 1 1 +github.com\bkmoovio\ach\addenda98.go:143.57,145.2 1 1 +github.com\bkmoovio\ach\addenda98.go:148.55,150.2 1 1 +github.com\bkmoovio\ach\addenda98.go:152.50,169.29 3 1 +github.com\bkmoovio\ach\addenda98.go:172.2,172.13 1 1 +github.com\bkmoovio\ach\addenda98.go:169.29,171.3 1 1 +github.com\bkmoovio\ach\batchPOP.go:29.45,34.2 4 1 +github.com\bkmoovio\ach\batchPOP.go:37.41,39.39 1 1 +github.com\bkmoovio\ach\batchPOP.go:45.2,45.48 1 1 +github.com\bkmoovio\ach\batchPOP.go:50.2,50.50 1 1 +github.com\bkmoovio\ach\batchPOP.go:56.2,56.39 1 1 +github.com\bkmoovio\ach\batchPOP.go:62.2,62.38 1 1 +github.com\bkmoovio\ach\batchPOP.go:82.2,82.12 1 1 +github.com\bkmoovio\ach\batchPOP.go:39.39,41.3 1 1 +github.com\bkmoovio\ach\batchPOP.go:45.48,47.3 1 1 +github.com\bkmoovio\ach\batchPOP.go:50.50,53.3 2 1 +github.com\bkmoovio\ach\batchPOP.go:57.21,59.101 2 1 +github.com\bkmoovio\ach\batchPOP.go:62.38,64.35 1 1 +github.com\bkmoovio\ach\batchPOP.go:70.3,70.29 1 1 +github.com\bkmoovio\ach\batchPOP.go:76.3,76.39 1 1 +github.com\bkmoovio\ach\batchPOP.go:64.35,67.4 2 1 +github.com\bkmoovio\ach\batchPOP.go:70.29,73.4 2 1 +github.com\bkmoovio\ach\batchPOP.go:76.39,79.4 2 1 +github.com\bkmoovio\ach\batchPOP.go:86.39,88.38 1 1 +github.com\bkmoovio\ach\batchPOP.go:94.2,94.25 1 1 +github.com\bkmoovio\ach\batchPOP.go:88.38,90.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:101.36,107.2 2 1 +github.com\bkmoovio\ach\entryDetail.go:110.45,136.2 11 1 +github.com\bkmoovio\ach\entryDetail.go:139.40,152.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:156.41,157.44 1 1 +github.com\bkmoovio\ach\entryDetail.go:160.2,160.26 1 1 +github.com\bkmoovio\ach\entryDetail.go:164.2,164.65 1 1 +github.com\bkmoovio\ach\entryDetail.go:167.2,167.63 1 1 +github.com\bkmoovio\ach\entryDetail.go:170.2,170.67 1 1 +github.com\bkmoovio\ach\entryDetail.go:173.2,173.61 1 1 +github.com\bkmoovio\ach\entryDetail.go:176.2,176.64 1 1 +github.com\bkmoovio\ach\entryDetail.go:180.2,183.16 3 1 +github.com\bkmoovio\ach\entryDetail.go:187.2,187.32 1 1 +github.com\bkmoovio\ach\entryDetail.go:191.2,191.12 1 1 +github.com\bkmoovio\ach\entryDetail.go:157.44,159.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:160.26,163.3 2 1 +github.com\bkmoovio\ach\entryDetail.go:164.65,166.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:167.63,169.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:170.67,172.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:173.61,175.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:176.64,178.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:183.16,185.3 1 0 +github.com\bkmoovio\ach\entryDetail.go:187.32,190.3 2 1 +github.com\bkmoovio\ach\entryDetail.go:196.47,197.25 1 1 +github.com\bkmoovio\ach\entryDetail.go:200.2,200.29 1 1 +github.com\bkmoovio\ach\entryDetail.go:203.2,203.33 1 1 +github.com\bkmoovio\ach\entryDetail.go:206.2,206.31 1 1 +github.com\bkmoovio\ach\entryDetail.go:209.2,209.29 1 1 +github.com\bkmoovio\ach\entryDetail.go:212.2,212.25 1 1 +github.com\bkmoovio\ach\entryDetail.go:215.2,215.12 1 1 +github.com\bkmoovio\ach\entryDetail.go:197.25,199.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:200.29,202.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:203.33,205.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:206.31,208.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:209.29,211.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:212.25,214.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:219.68,222.24 2 1 +github.com\bkmoovio\ach\entryDetail.go:223.18,227.21 4 1 +github.com\bkmoovio\ach\entryDetail.go:228.18,232.21 4 1 +github.com\bkmoovio\ach\entryDetail.go:233.18,237.21 4 1 +github.com\bkmoovio\ach\entryDetail.go:239.10,242.21 3 1 +github.com\bkmoovio\ach\entryDetail.go:247.58,252.2 4 1 +github.com\bkmoovio\ach\entryDetail.go:255.75,258.2 2 1 +github.com\bkmoovio\ach\entryDetail.go:261.57,263.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:266.55,268.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:271.45,273.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:276.59,278.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:282.56,284.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:288.55,290.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:294.58,296.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:300.53,302.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:306.54,308.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:312.59,315.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:319.54,322.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:326.55,329.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:332.53,334.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:337.55,339.2 1 0 +github.com\bkmoovio\ach\entryDetail.go:342.54,344.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:347.56,349.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:352.50,356.2 2 1 +github.com\bkmoovio\ach\entryDetail.go:359.49,361.14 2 1 +github.com\bkmoovio\ach\entryDetail.go:361.14,363.3 1 0 +github.com\bkmoovio\ach\entryDetail.go:363.3,365.3 1 1 +github.com\bkmoovio\ach\entryDetail.go:369.50,371.2 1 1 +github.com\bkmoovio\ach\entryDetail.go:374.47,377.17 2 1 +github.com\bkmoovio\ach\entryDetail.go:383.2,383.11 1 0 +github.com\bkmoovio\ach\entryDetail.go:378.26,379.13 1 1 +github.com\bkmoovio\ach\entryDetail.go:380.31,381.13 1 1 +github.com\bkmoovio\ach\entryDetail.go:388.60,391.17 2 1 +github.com\bkmoovio\ach\entryDetail.go:397.2,397.11 1 1 +github.com\bkmoovio\ach\entryDetail.go:393.16,394.21 1 1 +github.com\bkmoovio\ach\entryDetail.go:395.10,395.10 0 1 diff --git a/coverage.html b/coverage.html new file mode 100644 index 000000000..f338a47c8 --- /dev/null +++ b/coverage.html @@ -0,0 +1,4528 @@ + + + + + + + + +
+ +
+ not tracked + + not covered + covered + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + From c1df4b052952fcc84243478712370b65e461f07f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 10:02:02 -0400 Subject: [PATCH 0196/1694] batchRCK_test.go code coverage 100% batchRCK_test.go code coverage 100% --- batchRCK_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/batchRCK_test.go b/batchRCK_test.go index c8bb49d4d..ff7ccc711 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -148,6 +148,34 @@ func BenchmarkBatchRCKStandardEntryClassCode(b *testing.B) { } } +// testBatchRCKServiceClassCodeEquality validates service class code equality +func testBatchRCKServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchRCK() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchRCKServiceClassCodeEquality tests validating service class code equality +func TestBatchRCKServiceClassCodeEquality(t *testing.T) { + testBatchRCKServiceClassCodeEquality(t) +} + +// BenchmarkBatchRCKServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchRCKServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchRCKServiceClassCodeEquality(b) + } +} + // testBatchRCKServiceClass200 validates BatchRCK create for an invalid ServiceClassCode 200 func testBatchRCKServiceClass200(t testing.TB) { mockBatch := mockBatchRCK() From 2e7fe4851366b8cf2c849a464869f6f0bc87d370 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 10:08:30 -0400 Subject: [PATCH 0197/1694] batchCOR code coverage 100% batchCOR code coverage 100% --- batchCOR_test.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/batchCOR_test.go b/batchCOR_test.go index f756a9bec..55cc226e9 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -73,7 +73,7 @@ func BenchmarkBatchCORHeader(b *testing.B) { // testBatchCORSEC validates BatchCOR SEC code func testBatchCORSEC(t testing.TB) { mockBatch := mockBatchCOR() - mockBatch.Header.StandardEntryClassCode = "COR" + mockBatch.Header.StandardEntryClassCode = "WEB" if err := mockBatch.Validate(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "StandardEntryClassCode" { @@ -325,3 +325,31 @@ func BenchmarkBatchCORCreate(b *testing.B) { testBatchCORCreate(b) } } + +// testBatchCORServiceClassCodeEquality validates service class code equality +func testBatchCORServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchCOR() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCORServiceClassCodeEquality tests validating service class code equality +func TestBatchCORServiceClassCodeEquality(t *testing.T) { + testBatchCORServiceClassCodeEquality(t) +} + +// BenchmarkBatchCORServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchCORServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCORServiceClassCodeEquality(b) + } +} From 676a4a75428aa24d857c9574954624ea0e1735c9 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 10:15:35 -0400 Subject: [PATCH 0198/1694] batchPPD code coverage 100% batchPPD code coverage 100% --- batchPPD_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/batchPPD_test.go b/batchPPD_test.go index 1c27dd1a6..8efa88b4c 100644 --- a/batchPPD_test.go +++ b/batchPPD_test.go @@ -264,3 +264,33 @@ func BenchmarkBatchBuild(b *testing.B) { testBatchBuild(b) } } + +// testBatchPPDAddendaCount validates BatchPPD Addendum count of 2 +func testBatchPPDAddendaCount(t testing.TB) { + mockBatch := mockBatchPPD() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPPDAddendaCount tests validating BatchPPD Addendum count of 2 +func TestBatchPPDAddendaCount(t *testing.T) { + testBatchPPDAddendaCount(t) +} + +// BenchmarkBatchPPDAddendaCount benchmarks validating BatchPPD Addendum count of 2 +func BenchmarkBatchPPDAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPPDAddendaCount(b) + } +} From 6490273d3e6879052a433d46e545735c75f115d2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 10:28:01 -0400 Subject: [PATCH 0199/1694] addenda99 code coverage 100% addenda99 code coverage 100% --- addenda99_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/addenda99_test.go b/addenda99_test.go index 961ab4744..0b6340e79 100644 --- a/addenda99_test.go +++ b/addenda99_test.go @@ -257,3 +257,27 @@ func BenchmarkAddenda99TraceNumberField(b *testing.B) { testAddenda99TraceNumberField(b) } } + +func testAddenda99ValidRecordType(t testing.TB) { + addenda99 := mockAddenda99() + addenda99.recordType = "63" + if err := addenda99.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} +func TestAddenda99ValidRecordType(t *testing.T) { + testAddenda99ValidRecordType(t) +} + +func BenchmarkAddenda99ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99ValidRecordType(b) + } +} From 992a2619dea969dcbf007beb21979ce1cd8bf4f6 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 10:46:17 -0400 Subject: [PATCH 0200/1694] EntryDetail CheckDigit error EntryDetail CheckDigit error --- entryDetail.go | 2 +- entryDetail_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/entryDetail.go b/entryDetail.go index eb69fd740..6d82726c5 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -181,7 +181,7 @@ func (ed *EntryDetail) Validate() error { edCheckDigit, err := strconv.Atoi(ed.CheckDigit) if err != nil { - return err + return &FieldError{FieldName: "CheckDigit", Value: ed.CheckDigit, Msg: err.Error()} } if calculated != edCheckDigit { diff --git a/entryDetail_test.go b/entryDetail_test.go index e362508af..3a572056e 100644 --- a/entryDetail_test.go +++ b/entryDetail_test.go @@ -620,3 +620,29 @@ func BenchmarkEDCreditOrDebit(b *testing.B) { testEDCreditOrDebit(b) } } + +// testValidateEDCheckDigit validates CheckDigit error +func testValidateEDCheckDigit(t testing.TB) { + ed := mockEntryDetail() + ed.CheckDigit = "XYZ" + if err := ed.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CheckDigit" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateEDCheckDigit tests validating validates CheckDigit error +func TestValidateEDCheckDigit(t *testing.T) { + testValidateEDCheckDigit(t) +} + +// BenchmarkValidateEDCheckDigit benchmarks validating CheckDigit error +func BenchmarkValidateEDCheckDigit(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateEDCheckDigit(b) + } +} From 52df23905eef38592a49e1a09af7c86a40a81c2f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 11:20:15 -0400 Subject: [PATCH 0201/1694] #208 CardTransactiontype validator #208 CardTransactiontype validator --- batchPOS.go | 11 ++++- batchPOS_test.go | 30 ++++++++++++++ batcher.go | 1 + validators.go | 103 +++++++++++++++++++++++++++++------------------ 4 files changed, 103 insertions(+), 42 deletions(-) diff --git a/batchPOS.go b/batchPOS.go index e6ea4fd91..0cd37e9dc 100644 --- a/batchPOS.go +++ b/batchPOS.go @@ -4,7 +4,9 @@ package ach -import "fmt" +import ( + "fmt" +) // BatchPOS holds the BatchHeader and BatchControl and all EntryDetail for POS Entries. // @@ -67,7 +69,12 @@ func (batch *BatchPOS) Validate() error { msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} } - //ToDo; Additional validations + if err := entry.isCardTransactionType(entry.DiscretionaryData); err != nil { + msg := fmt.Sprintf(msgBatchCardTransactionType, entry.DiscretionaryData) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CardTransactionType", Msg: msg} + } + + //ToDo; Additional validations - move isAddenda02 logic so we only range through a batch of entries once } return nil } diff --git a/batchPOS_test.go b/batchPOS_test.go index c3c95f793..b070b08ed 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -290,6 +290,8 @@ func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { } } +// ToDo: Using a FieldError may need to add a BatchError and use *BatchError + // testBatchPOSInvalidBuild validates an invalid batch build func testBatchPOSInvalidBuild(t testing.TB) { mockBatch := mockBatchPOS() @@ -317,3 +319,31 @@ func BenchmarkBatchPOSInvalidBuild(b *testing.B) { testBatchPOSInvalidBuild(b) } } + +// testBatchPOSCardTransactionType validates BatchPOS create for an invalid CardTransactionType +func testBatchPOSCardTransactionType(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.GetEntries()[0].DiscretionaryData = "555" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CardTransactionType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSCardTransactionType tests validating BatchPOS create for an invalid CardTransactionType +func TestBatchPOSCardTransactionType(t *testing.T) { + testBatchPOSCardTransactionType(t) +} + +// BenchmarkBatchPOSCardTransactionType benchmarks validating BatchPOS create for an invalid CardTransactionType +func BenchmarkBatchPOSCardTransactionType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSCardTransactionType(b) + } +} diff --git a/batcher.go b/batcher.go index 7c1c34956..a68894d22 100644 --- a/batcher.go +++ b/batcher.go @@ -62,4 +62,5 @@ var ( msgBatchAmount = "Amount must be less than %v for SEC code %v" msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" msgBatchTransactionCode = "Transaction code %v is not allowed for batch type %v" + msgBatchCardTransactionType = "Card Transaction Type %v is invalid" ) diff --git a/validators.go b/validators.go index 487f25a6b..a647fc4ba 100644 --- a/validators.go +++ b/validators.go @@ -15,9 +15,21 @@ import ( var ( upperAlphanumericRegex = regexp.MustCompile(`[^ A-Z0-9!"#$%&'()*+,-.\\/:;<>=?@\[\]^_{}|~]+`) alphanumericRegex = regexp.MustCompile(`[^ \w!"#$%&'()*+,-.\\/:;<>=?@\[\]^_{}|~]+`) + // Errors specific to validation + msgAlphanumeric = "has non alphanumeric characters" + msgUpperAlpha = "is not uppercase A-Z or 0-9" + msgFieldInclusion = "is a mandatory field and has a default value" + msgValidFieldLength = "is not length %d" + msgServiceClass = "is an invalid Service Class Code" + msgSECCode = "is an invalid Standard Entry Class Code" + msgOrigStatusCode = "is an invalid Originator Status Code" + msgAddendaTypeCode = "is an invalid Addenda Type Code" + msgTransactionCode = "is an invalid Transaction Code" + msgValidCheckDigit = "does not match calculated check digit %d" + msgCardTransactionType = "is an invalid Card Transaction Type" ) -// validator is common validation and formating of golang types to ach type strings +// validator is common validation and formatting of golang types to ach type strings type validator struct{} // FieldError is returned for errors at a field level in a record @@ -35,35 +47,44 @@ func (e *FieldError) Error() string { return fmt.Sprintf("%s %s %s", e.FieldName, e.Value, e.Msg) } -// Errors specific to validation -var ( - msgAlphanumeric = "has non alphanumeric characters" - msgUpperAlpha = "is not uppercase A-Z or 0-9" - msgFieldInclusion = "is a mandatory field and has a default value" - msgValidFieldLength = "is not length %d" - msgServiceClass = "is an invalid Service Class Code" - msgSECCode = "is an invalid Standard Entry Class Code" - msgOrigStatusCode = "is an invalid Originator Status Code" - msgAddendaTypeCode = "is an invalid Addenda Type Code" - msgTransactionCode = "is an invalid Transaction Code" - msgValidCheckDigit = "does not match calculated check digit %d" -) +// isCardTransactionType ensures card transaction type of a batchPOS is valid +func (v *validator) isCardTransactionType(code string) error { + switch code { + case + // Purchase of goods or services + "01", + // Cash + "02", + // Return Reversal + "03", + // Purchase Reversal + "11", + // Cash Reversal + "12", + // Return + "13", + // Adjustment + "21", + // Miscellaneous Transaction + "99": + return nil + } + return errors.New(msgCardTransactionType) +} -// iServiceClass returns true if a valid service class code of a batch is found -func (v *validator) isServiceClass(code int) error { +// isOriginatorStatusCode ensures status code of a batch is valid +func (v *validator) isOriginatorStatusCode(code int) error { switch code { case - // ACH Mixed Debits and Credits - 200, - // ACH Credits Only - 220, - // ACH Debits Only - 225, - // ACH Automated Accounting Advices - 280: + // ADV file - prepared by an ACH Operator + 0, + //Originator is a financial institution + 1, + // Originator is a Government Agency or other agency not subject to ACH Rules + 2: return nil } - return errors.New(msgServiceClass) + return errors.New(msgOrigStatusCode) } // isSECCode returns true if a SEC Code of a Batch is found @@ -77,6 +98,23 @@ func (v *validator) isSECCode(code string) error { return errors.New(msgSECCode) } +// iServiceClass returns true if a valid service class code of a batch is found +func (v *validator) isServiceClass(code int) error { + switch code { + case + // ACH Mixed Debits and Credits + 200, + // ACH Credits Only + 220, + // ACH Debits Only + 225, + // ACH Automated Accounting Advices + 280: + return nil + } + return errors.New(msgServiceClass) +} + // isTypeCode returns true if a valid type code of an Addendum is found // // The Addenda Type Code defines the specific interpretation and format for the addenda information contained in the Entry. @@ -229,21 +267,6 @@ func (v *validator) isTransactionCode(code int) error { return errors.New(msgTransactionCode) } -// isOriginatorStatusCode ensures status code of a batch is valid -func (v *validator) isOriginatorStatusCode(code int) error { - switch code { - case - // ADV file - prepared by an ACH Operator - 0, - //Originator is a financial institution - 1, - // Originator is a Government Agency or other agency not subject to ACH Rules - 2: - return nil - } - return errors.New(msgOrigStatusCode) -} - // isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters func (v *validator) isUpperAlphanumeric(s string) error { if upperAlphanumericRegex.MatchString(s) { From 7cc9dc5f11b89d96e566846bac9a9d2509dabd6d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 11:30:58 -0400 Subject: [PATCH 0202/1694] batchPOS Removed isAddenda02 Add Addenda02 logic to Validate to not range through entries twice --- batchPOS.go | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/batchPOS.go b/batchPOS.go index 0cd37e9dc..627d8513a 100644 --- a/batchPOS.go +++ b/batchPOS.go @@ -45,10 +45,6 @@ func (batch *BatchPOS) Validate() error { } // Add configuration based validation for this type. - // POS Addenda must be Addenda02 - if err := batch.isAddenda02(); err != nil { - return err - } // Add type specific validation. if batch.Header.StandardEntryClassCode != "POS" { msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "POS") @@ -73,31 +69,13 @@ func (batch *BatchPOS) Validate() error { msg := fmt.Sprintf(msgBatchCardTransactionType, entry.DiscretionaryData) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CardTransactionType", Msg: msg} } - - //ToDo; Additional validations - move isAddenda02 logic so we only range through a batch of entries once - } - return nil -} - -// Create takes Batch Header and Entries and builds a valid batch -func (batch *BatchPOS) Create() error { - // generates sequence numbers and batch control - if err := batch.build(); err != nil { - return err - } - // Additional steps specific to batch type - // ... - - return batch.Validate() -} - -// isAddenda02 verifies that a Addenda02 exists for each EntryDetail and is Validated -func (batch *BatchPOS) isAddenda02() error { - for _, entry := range batch.Entries { - // Addenda type must be equal to 1 + // Addendum must be equal to 1 if len(entry.Addendum) != 1 { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchPOSAddenda} } + + // POS Addenda must be Addenda02 + // Addenda type assertion must be Addenda02 addenda02, ok := entry.Addendum[0].(*Addenda02) if !ok { @@ -111,6 +89,19 @@ func (batch *BatchPOS) isAddenda02() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} } } + //ToDo; Additional validations } return nil } + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchPOS) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + return batch.Validate() +} From 13c0fca9524b5f5f812c2408115d2f112d8b7d2b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 12:46:17 -0400 Subject: [PATCH 0203/1694] #208 Add isMonth validator #208 Add isMonth validator --- addenda02.go | 4 +++- addenda02_test.go | 30 ++++++++++++++++++++++++++++++ validators.go | 14 +++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/addenda02.go b/addenda02.go index 3ec93c598..6d2ca362e 100644 --- a/addenda02.go +++ b/addenda02.go @@ -120,7 +120,9 @@ func (addenda02 *Addenda02) Validate() error { if addenda02.typeCode != "02" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgAddendaTypeCode} } - // ToDo: Validation for TransactionDate MMDD + if err := addenda02.isMonth(addenda02.parseStringField(addenda02.TransactionDate[0:2])); err != nil { + return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidMonth} + } return nil } diff --git a/addenda02_test.go b/addenda02_test.go index 38217ea23..f74e66b59 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -299,13 +299,43 @@ func testAddenda02String(t testing.TB) { } } +// TestAddenda02String tests validating that a known parsed file can be return to a string of the same value func TestAddenda02String(t *testing.T) { testAddenda02String(t) } +// BenchmarkAddenda02String benchmarks validating that a known parsed file can be return to a string of the same value func BenchmarkAddenda02String(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { testAddenda02String(b) } } + +// testAddenda02TransactionDateMonth validates the month is valid for transactionDate +func testAddenda02TransactionDateMonth(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionDate = "1306" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda02TransactionDateMonth tests validating the month is valid for transactionDate +func TestAddenda02TransactionDateMonth(t *testing.T) { + testAddenda02TransactionDateMonth(t) +} + +// BenchmarkAddenda02TransactionDateMonth test validating the month is valid for transactionDate +func BenchmarkAddenda02TransactionDateMonth(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionDateMonth(b) + } +} diff --git a/validators.go b/validators.go index a647fc4ba..796808c26 100644 --- a/validators.go +++ b/validators.go @@ -27,6 +27,7 @@ var ( msgTransactionCode = "is an invalid Transaction Code" msgValidCheckDigit = "does not match calculated check digit %d" msgCardTransactionType = "is an invalid Card Transaction Type" + msgValidMonth = "is an invalid month" ) // validator is common validation and formatting of golang types to ach type strings @@ -72,6 +73,17 @@ func (v *validator) isCardTransactionType(code string) error { return errors.New(msgCardTransactionType) } +// isMonth +func (v *validator) isMonth(s string) error { + switch s { + case + "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12": + return nil + } + return errors.New(msgValidMonth) +} + // isOriginatorStatusCode ensures status code of a batch is valid func (v *validator) isOriginatorStatusCode(code int) error { switch code { @@ -297,7 +309,7 @@ func (v *validator) CalculateCheckDigit(routingNumber string) int { routeIndex[i] = string(routingNumber[i]) } n, _ := strconv.Atoi(routeIndex[0]) - sum := (n * 3) + sum := n * 3 n, _ = strconv.Atoi(routeIndex[1]) sum = sum + (n * 7) n, _ = strconv.Atoi(routeIndex[2]) From 529d1d826c217761aa49fb268f0de16681f1c3e3 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 13:18:16 -0400 Subject: [PATCH 0204/1694] #208 Add isDay validator #208 Add isDay validator --- addenda02.go | 6 ++ addenda02_test.go | 140 ++++++++++++++++++++++++++++++++++++++++++++++ validators.go | 45 ++++++++++++++- 3 files changed, 190 insertions(+), 1 deletion(-) diff --git a/addenda02.go b/addenda02.go index 6d2ca362e..7526608d4 100644 --- a/addenda02.go +++ b/addenda02.go @@ -120,9 +120,15 @@ func (addenda02 *Addenda02) Validate() error { if addenda02.typeCode != "02" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgAddendaTypeCode} } + // TransactionDate Addenda02 ACH File format is MMDD. Validate MM is 01-12. if err := addenda02.isMonth(addenda02.parseStringField(addenda02.TransactionDate[0:2])); err != nil { return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidMonth} } + // TransactionDate Addenda02 ACH File format is MMDD. If the month is valid, validate the day for the + // month 01-31 depending on month. + if err := addenda02.isDay(addenda02.parseStringField(addenda02.TransactionDate[0:2]), addenda02.parseStringField(addenda02.TransactionDate[2:4])); err != nil { + return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidDay} + } return nil } diff --git a/addenda02_test.go b/addenda02_test.go index f74e66b59..ef0ebe10c 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -339,3 +339,143 @@ func BenchmarkAddenda02TransactionDateMonth(b *testing.B) { testAddenda02TransactionDateMonth(b) } } + +// testAddenda02TransactionDateDay validates the day is valid for transactionDate +func testAddenda02TransactionDateDay(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionDate = "0205" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda02TransactionDateDay tests validating the day is valid for transactionDate +func TestAddenda02TransactionDateDay(t *testing.T) { + testAddenda02TransactionDateDay(t) +} + +// BenchmarkAddenda02TransactionDateDay test validating the day is valid for transactionDate +func BenchmarkAddenda02TransactionDateDay(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionDateDay(b) + } +} + +// testAddenda02TransactionDateFeb validates the day is valid for transactionDate +func testAddenda02TransactionDateFeb(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionDate = "0230" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda02TransactionDateFeb tests validating the day is valid for transactionDate +func TestAddenda02TransactionDateFeb(t *testing.T) { + testAddenda02TransactionDateFeb(t) +} + +// BenchmarkAddenda02TransactionDateFeb test validating the day is valid for transactionDate +func BenchmarkAddenda02TransactionDateFeb(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionDateFeb(b) + } +} + +// testAddenda02TransactionDate30Day validates the day is valid for transactionDate +func testAddenda02TransactionDate30Day(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionDate = "0630" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda02TransactionDate30Day tests validating the day is valid for transactionDate +func TestAddenda02TransactionDate30Day(t *testing.T) { + testAddenda02TransactionDate30Day(t) +} + +// BenchmarkAddenda02TransactionDate30Day test validating the day is valid for transactionDate +func BenchmarkAddenda02TransactionDate30Day(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionDate30Day(b) + } +} + +// testAddenda02TransactionDate31Day validates the day is valid for transactionDate +func testAddenda02TransactionDate31Day(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionDate = "0131" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda02TransactionDate31Day tests validating the day is valid for transactionDate +func TestAddenda02TransactionDate31Day(t *testing.T) { + testAddenda02TransactionDate31Day(t) +} + +// BenchmarkAddenda02TransactionDate31Day test validating the day is valid for transactionDate +func BenchmarkAddenda02TransactionDate31Day(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionDate31Day(b) + } +} + +// testAddenda02TransactionDateInvalidDay validates the day is invalid for transactionDate +func testAddenda02TransactionDateInvalidDay(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionDate = "1039" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda02TransactionDateInvalidDay tests validating the day is invalid for transactionDate +func TestAddenda02TransactionDateInvalidDay(t *testing.T) { + testAddenda02TransactionDateInvalidDay(t) +} + +// BenchmarkAddenda02TransactionDateInvalidDay test validating the day is invalid for transactionDate +func BenchmarkAddenda02TransactionDateInvalidDay(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda02TransactionDateInvalidDay(b) + } +} diff --git a/validators.go b/validators.go index 796808c26..3f375eea8 100644 --- a/validators.go +++ b/validators.go @@ -28,6 +28,7 @@ var ( msgValidCheckDigit = "does not match calculated check digit %d" msgCardTransactionType = "is an invalid Card Transaction Type" msgValidMonth = "is an invalid month" + msgValidDay = "is an invalid day" ) // validator is common validation and formatting of golang types to ach type strings @@ -73,7 +74,7 @@ func (v *validator) isCardTransactionType(code string) error { return errors.New(msgCardTransactionType) } -// isMonth +// isMonth validates a 2 digit month 01-12 func (v *validator) isMonth(s string) error { switch s { case @@ -84,6 +85,48 @@ func (v *validator) isMonth(s string) error { return errors.New(msgValidMonth) } +// isDay validates a 2 digit day based on a 2 digit month +// month 01-12 day 01-31 based on month +func (v *validator) isDay(m string, d string) error { + // ToDo: Future Consideration Leap Year - not sure if cards actually have 0229 + switch m { + // February + case "02": + switch d { + case + "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12", + "13", "14", "15", "16", "17", "18", + "19", "20", "21", "22", "23", "24", + "25", "26", "27", "28", "29": + return nil + } + // April, June, September, November + case "04", "06", "09", "11": + switch d { + case + "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12", + "13", "14", "15", "16", "17", "18", + "19", "20", "21", "22", "23", "24", + "25", "26", "27", "28", "29", "30": + return nil + } + // January, March, May, July, August, October, December + case "01", "03", "05", "07", "08", "10", "12": + switch d { + case + "01", "02", "03", "04", "05", "06", + "07", "08", "09", "10", "11", "12", + "13", "14", "15", "16", "17", "18", + "19", "20", "21", "22", "23", "24", + "25", "26", "27", "28", "29", "30", "31": + return nil + } + } + return errors.New(msgValidDay) +} + // isOriginatorStatusCode ensures status code of a batch is valid func (v *validator) isOriginatorStatusCode(code int) error { switch code { From f2ac586927bbdfd9da60e702bf49fc8516ec48f6 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 13:29:49 -0400 Subject: [PATCH 0205/1694] #208 Update copyright #208 Update copyright --- entryDetail_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entryDetail_test.go b/entryDetail_test.go index 3a572056e..96ffb8e6a 100644 --- a/entryDetail_test.go +++ b/entryDetail_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. From 90506b3030bbb323e5e0916d37fe06c9f58db339 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 15:16:15 -0400 Subject: [PATCH 0206/1694] #208 Copyright update #208 Copyright update --- file.go | 4 ++-- file_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/file.go b/file.go index 23c4ad30c..74123019d 100644 --- a/file.go +++ b/file.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. @@ -60,7 +60,7 @@ type File struct { // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches NotificationOfChange []*BatchCOR - // ReturnEntries is a slice of references to file.Batches that contain return entires + // ReturnEntries is a slice of references to file.Batches that contain return entries ReturnEntries []Batcher converters diff --git a/file_test.go b/file_test.go index 1b3bc5b24..5e0f86ea3 100644 --- a/file_test.go +++ b/file_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. From 86f122e3f35887e050bcf4deb7dad3f6437e729d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 15:22:20 -0400 Subject: [PATCH 0207/1694] #208 copyright update copyright update typo fixes --- converters.go | 4 ++-- converters_test.go | 2 +- fileControl_test.go | 14 +++++++------- fileHeader_test.go | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/converters.go b/converters.go index b89180af9..dfd451fc2 100644 --- a/converters.go +++ b/converters.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. @@ -55,7 +55,7 @@ func (c *converters) alphaField(s string, max uint) string { return s } -// numericField right-justified, unisigned, and zero filled +// numericField right-justified, unsigned, and zero filled func (c *converters) numericField(n int, max uint) string { s := strconv.Itoa(n) ln := uint(len(s)) diff --git a/converters_test.go b/converters_test.go index 34ea860ef..2627d580e 100644 --- a/converters_test.go +++ b/converters_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. diff --git a/fileControl_test.go b/fileControl_test.go index 1a3e7c1b6..4b71bfdc2 100644 --- a/fileControl_test.go +++ b/fileControl_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE file. @@ -26,16 +26,16 @@ func testMockFileControl(t testing.TB) { t.Error("mockFileControl does not validate and will break other tests") } if fc.BatchCount != 1 { - t.Error("BatchCount depedendent default value has changed") + t.Error("BatchCount dependent default value has changed") } if fc.BlockCount != 1 { - t.Error("BlockCount depedendent default value has changed") + t.Error("BlockCount dependent default value has changed") } if fc.EntryAddendaCount != 1 { - t.Error("EntryAddendaCount depedendent default value has changed") + t.Error("EntryAddendaCount dependent default value has changed") } if fc.EntryHash != 5320001 { - t.Error("EntryHash depedendent default value has changed") + t.Error("EntryHash dependent default value has changed") } } @@ -227,8 +227,8 @@ func TestFCFieldInclusionBlockCount(t *testing.T) { testFCFieldInclusionBlockCount(t) } -// BenchmarkFCFieldInclusionBlockCoun benchmarks validating file control block count field inclusion -func BenchmarkFCFieldInclusionBlockCoun(b *testing.B) { +// BenchmarkFCFieldInclusionBlockCount benchmarks validating file control block count field inclusion +func BenchmarkFCFieldInclusionBlockCount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { testFCFieldInclusionBlockCount(b) diff --git a/fileHeader_test.go b/fileHeader_test.go index 9c9610bbf..5d2c53a75 100644 --- a/fileHeader_test.go +++ b/fileHeader_test.go @@ -1,4 +1,4 @@ -// Copyright 2017 The ACH Authors +// Copyright 2018 The ACH Authors // Use of this source code is governed by an Apache License // license that can be found in the LICENSE File. @@ -28,16 +28,16 @@ func testMockFileHeader(t testing.TB) { t.Error("mockFileHeader does not validate and will break other tests") } if fh.ImmediateDestination != "9876543210" { - t.Error("ImmediateDestination depedendent default value has changed") + t.Error("ImmediateDestination dependent default value has changed") } if fh.ImmediateOrigin != "1234567890" { - t.Error("ImmediateOrigin depedendent default value has changed") + t.Error("ImmediateOrigin dependent default value has changed") } if fh.ImmediateDestinationName != "Federal Reserve Bank" { - t.Error("ImmediateDestinationName depedendent default value has changed") + t.Error("ImmediateDestinationName dependent default value has changed") } if fh.ImmediateOriginName != "My Bank Name" { - t.Error("ImmediateOriginName depedendent default value has changed") + t.Error("ImmediateOriginName dependent default value has changed") } } @@ -100,7 +100,7 @@ func parseFileHeader(t testing.TB) { t.Errorf("ImmediateDestinationName Expected 'achdestname ' got:'%v'", record.ImmediateDestinationNameField()) } if record.ImmediateOriginNameField() != "companyname " { - t.Errorf("ImmidiateOriginName Expected 'companyname ' got: '%v'", record.ImmediateOriginNameField()) + t.Errorf("ImmediateOriginName Expected 'companyname ' got: '%v'", record.ImmediateOriginNameField()) } if record.ReferenceCodeField() != " " { t.Errorf("ReferenceCode Expected ' ' got:'%v'", record.ReferenceCodeField()) From e72c3aceb5f918b09008c1181050ebeb91956f8e Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 15:30:32 -0400 Subject: [PATCH 0208/1694] #208 batchPos ServiceClassCode check #208 batchPos ServiceClassCode check --- batchPOS.go | 22 ++++++------ batchPOS_test.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/batchPOS.go b/batchPOS.go index 627d8513a..6e8637885 100644 --- a/batchPOS.go +++ b/batchPOS.go @@ -46,18 +46,18 @@ func (batch *BatchPOS) Validate() error { // Add configuration based validation for this type. // Add type specific validation. + if batch.Header.StandardEntryClassCode != "POS" { msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "POS") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} } - //ToDo; Determine if this is needed - /* // POS detail entries can only be a debit, ServiceClassCode must allow debits - switch batch.Header.ServiceClassCode { - case 200, 220, 280: - msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "POS") - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} - }*/ + // POS detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 220, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "POS") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + } for _, entry := range batch.Entries { // POS detail entries must be a debit @@ -69,19 +69,21 @@ func (batch *BatchPOS) Validate() error { msg := fmt.Sprintf(msgBatchCardTransactionType, entry.DiscretionaryData) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CardTransactionType", Msg: msg} } + + // Addenda validations - POS Addenda must be Addenda02 + // Addendum must be equal to 1 if len(entry.Addendum) != 1 { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchPOSAddenda} } - // POS Addenda must be Addenda02 - // Addenda type assertion must be Addenda02 addenda02, ok := entry.Addendum[0].(*Addenda02) if !ok { msg := fmt.Sprintf(msgBatchPOSAddendaType, entry.Addendum[0]) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} } + // Addenda02 must be Validated if err := addenda02.Validate(); err != nil { // convert the field error in to a batch error for a consistent api @@ -89,7 +91,6 @@ func (batch *BatchPOS) Validate() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} } } - //ToDo; Additional validations } return nil } @@ -102,6 +103,5 @@ func (batch *BatchPOS) Create() error { } // Additional steps specific to batch type // ... - return batch.Validate() } diff --git a/batchPOS_test.go b/batchPOS_test.go index b070b08ed..575472fe2 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -145,6 +145,93 @@ func BenchmarkBatchPOSServiceClassCodeEquality(b *testing.B) { } } +// testBatchPOSServiceClass200 validates BatchPOS create for an invalid ServiceClassCode 200 +func testBatchPOSServiceClass200(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Header.ServiceClassCode = 200 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSServiceClass200 tests validating BatchPOS create for an invalid ServiceClassCode 200 +func TestBatchPOSServiceClass200(t *testing.T) { + testBatchPOSServiceClass200(t) +} + +// BenchmarkBatchPOSServiceClass200 benchmarks validating BatchPOS create for an invalid ServiceClassCode 200 +func BenchmarkBatchPOSServiceClass200(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSServiceClass200(b) + } +} + +// testBatchPOSServiceClass220 validates BatchPOS create for an invalid ServiceClassCode 220 +func testBatchPOSServiceClass220(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Header.ServiceClassCode = 220 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSServiceClass220 tests validating BatchPOS create for an invalid ServiceClassCode 220 +func TestBatchPOSServiceClass220(t *testing.T) { + testBatchPOSServiceClass220(t) +} + +// BenchmarkBatchPOSServiceClass220 benchmarks validating BatchPOS create for an invalid ServiceClassCode 220 +func BenchmarkBatchPOSServiceClass220(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSServiceClass220(b) + } +} + +// testBatchPOSServiceClass280 validates BatchPOS create for an invalid ServiceClassCode 280 +func testBatchPOSServiceClass280(t testing.TB) { + mockBatch := mockBatchPOS() + mockBatch.Header.ServiceClassCode = 280 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchPOSServiceClass280 tests validating BatchPOS create for an invalid ServiceClassCode 280 +func TestBatchPOSServiceClass280(t *testing.T) { + testBatchPOSServiceClass280(t) +} + +// BenchmarkBatchPOSServiceClass280 benchmarks validating BatchPOS create for an invalid ServiceClassCode 280 +func BenchmarkBatchPOSServiceClass280(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchPOSServiceClass280(b) + } +} + // testBatchPOSTransactionCode validates BatchPOS TransactionCode is not a credit func testBatchPOSTransactionCode(t testing.TB) { mockBatch := mockBatchPOS() From bff4affb5d39b9304322efa061f42b8d60a5a574 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 15:46:50 -0400 Subject: [PATCH 0209/1694] #208 addenda02.go and addenda02_test.go Add more detailed comments for addenda02 properties Modify TraceNumber in addenda02 tests --- addenda02.go | 31 ++++++++++++++++++------------- addenda02_test.go | 4 ++-- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/addenda02.go b/addenda02.go index 7526608d4..afba34577 100644 --- a/addenda02.go +++ b/addenda02.go @@ -13,33 +13,38 @@ import ( // Code 02 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. type Addenda02 struct { //ToDo: Verify which fields should be omitempty - //ToDo: Add mor descriptive comments // ID is a client defined string used as a reference to this record. ID string `json:"id"` // RecordType defines the type of record in the block. entryAddenda02 Pos 7 recordType string - // TypeCode Addenda02 types code '02' + // TypeCode Addenda02 type code '02' typeCode string - // ReferenceInformationOne + // ReferenceInformationOne may be used for additional reference numbers, identification numbers, + // or codes that the merchant needs to identify the particular transaction or customer. ReferenceInformationOne string `json:"referenceInformationOne"` - // ReferenceInformationTwo + // ReferenceInformationTwo may be used for additional reference numbers, identification numbers, + // or codes that the merchant needs to identify the particular transaction or customer. ReferenceInformationTwo string `json:"referenceInformationTwo"` - // TerminalIdentificationCode + // TerminalIdentificationCode identifies an Electronic terminal with a unique code that allows + // a terminal owner and/or switching network to identify the terminal at which an Entry originated. TerminalIdentificationCode string `json:"terminalIdentificationCode"` - // TransactionSerialNumber + // TransactionSerialNumber is assigned by the terminal at the time the transaction is originated. The + // number, with the Terminal Identification Code, serves as an audit trail for the transaction and is + // usually assigned in ascending sequence. TransactionSerialNumber string `json:"transactionSerialNumber"` - // TransactionDate MMDD + // TransactionDate expressed MMDD identifies the date on which the transaction occurred. TransactionDate string `json:"transactionDate"` - // TraceNumber matches the Entry Detail Trace Number of the entry being returned. - // AuthorizationCodeOrExpireDate + // AuthorizationCodeOrExpireDate indicates the code that a card authorization center has + // furnished to the merchant. AuthorizationCodeOrExpireDate string `json:"authorizationCodeOrExpireDate"` - // Terminal Location + // Terminal Location identifies the specific location of a terminal (i.e., street names of an + // intersection, address, etc.) in accordance with the requirements of Regulation E. TerminalLocation string `json:"terminalLocation"` - // TerminalCity + // TerminalCity Identifies the city in which the electronic terminal is located. TerminalCity string `json:"terminalCity"` - // TerminalState + // TerminalState Identifies the state in which the electronic terminal is located TerminalState string `json:"terminalState"` - // TraceNumber matches the Entry Detail Trace Number of the entry being returned. + // TraceNumber Standard Entry Detail Trace Number TraceNumber int `json:"traceNumber,omitempty"` // validator is composed for data validation validator diff --git a/addenda02_test.go b/addenda02_test.go index ef0ebe10c..cf3032092 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -19,7 +19,7 @@ func mockAddenda02() *Addenda02 { addenda02.TerminalLocation = "Target Store 0049" addenda02.TerminalCity = "PHILADELPHIA" addenda02.TerminalState = "PA" - addenda02.TraceNumber = 91012980000088 + addenda02.TraceNumber = 121042880000123 return addenda02 } @@ -289,7 +289,7 @@ func BenchmarkAddenda02TerminalState(b *testing.B) { // TestAddenda02 String validates that a known parsed file can be return to a string of the same value func testAddenda02String(t testing.TB) { addenda02 := NewAddenda02() - var line = "702REFONEAREFTERM021000490612123456Target Store 0049 PHILADELPHIA PA091012980000088" + var line = "702REFONEAREFTERM021000490612123456Target Store 0049 PHILADELPHIA PA121042880000123" addenda02.Parse(line) if addenda02.String() != line { t.Errorf("Strings do not match") From 8c43b9cbed51d4f67a77471e77ca92dd8822b584 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 21:27:54 -0400 Subject: [PATCH 0210/1694] #208 Add addenda02 logic to reader --- .gitignore | 1 + reader.go | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index c3142be78..953f8d73e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ _testmain.go *.prof .vscode/launch.json +test/ach-pos-read/pos-debit.ach diff --git a/reader.go b/reader.go index be88b70c3..86ba9e39f 100644 --- a/reader.go +++ b/reader.go @@ -235,6 +235,14 @@ func (r *Reader) parseAddenda() error { if entry.AddendaRecordIndicator == 1 { switch r.line[1:3] { + case "02": + addenda02 := NewAddenda02() + addenda02.Parse(r.line) + if err := addenda02.Validate(); err != nil { + return r.error(err) + } + r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda02) + case "05": addenda05 := NewAddenda05() addenda05.Parse(r.line) From 2a04ad475144085e60f207cfc371d7d87867c4cd Mon Sep 17 00:00:00 2001 From: Brooke Kline Date: Thu, 14 Jun 2018 21:31:35 -0400 Subject: [PATCH 0211/1694] removed ignoring pos-debit.ach --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 953f8d73e..c3142be78 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,3 @@ _testmain.go *.prof .vscode/launch.json -test/ach-pos-read/pos-debit.ach From 018caadba893272cefb6fbbd75f86558d6253d7a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 14 Jun 2018 21:41:13 -0400 Subject: [PATCH 0212/1694] #208 Typos in comments #208 Typos in comments --- reader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reader.go b/reader.go index 86ba9e39f..0df668bab 100644 --- a/reader.go +++ b/reader.go @@ -14,7 +14,7 @@ import ( // ParseError is returned for parsing reader errors. // The first line is 1. type ParseError struct { - Line int // Line number where the error accurd + Line int // Line number where the error occurd Record string // Name of the record type being parsed Err error // The actual error } @@ -65,7 +65,7 @@ func NewReader(r io.Reader) *Reader { } // Read reads each line of the ACH file and defines which parser to use based -// on the first character of each line. It also enforces ACH formating rules and returns +// on the first character of each line. It also enforces ACH formatting rules and returns // the appropriate error if issues are found. func (r *Reader) Read() (File, error) { r.lineNum = 0 From db4bf25eed0afc1492158dc6c0e89b49bb792e46 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 15 Jun 2018 10:52:19 -0400 Subject: [PATCH 0213/1694] #208 Add pos ach file write test Add pos ach file read test Code comment modifications in reader.go and addenda02.go Increase test coverage in reader_test.go --- addenda02.go | 3 +- reader.go | 5 +- reader_test.go | 171 +++++++++++++++++++++++++++++--- test/ach-pos-read/main.go | 34 +++++++ test/ach-pos-read/main_test.go | 7 ++ test/ach-pos-read/pos-debit.ach | 10 ++ test/ach-pos-write/main.go | 79 +++++++++++++++ test/ach-pos-write/main_test.go | 7 ++ 8 files changed, 299 insertions(+), 17 deletions(-) create mode 100644 test/ach-pos-read/main.go create mode 100644 test/ach-pos-read/main_test.go create mode 100644 test/ach-pos-read/pos-debit.ach create mode 100644 test/ach-pos-write/main.go create mode 100644 test/ach-pos-write/main_test.go diff --git a/addenda02.go b/addenda02.go index afba34577..d9faa9395 100644 --- a/addenda02.go +++ b/addenda02.go @@ -149,6 +149,8 @@ func (addenda02 *Addenda02) fieldInclusion() error { if addenda02.typeCode == "" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgFieldInclusion} } + + // ToDo: These are not mandatory fields should be outside of fieldInclusion if addenda02.TerminalIdentificationCode == "" { return &FieldError{FieldName: "TerminalIdentificationCode", Value: addenda02.TerminalIdentificationCode, Msg: msgFieldInclusion} } @@ -198,7 +200,6 @@ func (addenda02 *Addenda02) TransactionSerialNumberField() string { // TransactionDateField returns TransactionDate MMDD string func (addenda02 *Addenda02) TransactionDateField() string { - //ToDo: see about padding return addenda02.TransactionDate } diff --git a/reader.go b/reader.go index 0df668bab..e3f7c2202 100644 --- a/reader.go +++ b/reader.go @@ -14,7 +14,7 @@ import ( // ParseError is returned for parsing reader errors. // The first line is 1. type ParseError struct { - Line int // Line number where the error occurd + Line int // Line number where the error occurred Record string // Name of the record type being parsed Err error // The actual error } @@ -194,7 +194,7 @@ func (r *Reader) parseBatchHeader() error { return r.error(err) } - // Passing SEC type into NewBatch creates a Batcher of SEC code type. + // Passing BatchHeader into NewBatch creates a Batcher of SEC code type. batch, err := NewBatch(bh) if err != nil { return r.error(err) @@ -242,7 +242,6 @@ func (r *Reader) parseAddenda() error { return r.error(err) } r.currentBatch.GetEntries()[entryIndex].AddAddenda(addenda02) - case "05": addenda05 := NewAddenda05() addenda05.Parse(r.line) diff --git a/reader_test.go b/reader_test.go index 125b2b24d..35fe6b2b5 100644 --- a/reader_test.go +++ b/reader_test.go @@ -15,11 +15,11 @@ func testParseError(t testing.TB) { e := &FieldError{FieldName: "testField", Value: "nil", Msg: "could not parse"} err := &ParseError{Line: 63, Err: e} if err.Error() != "line:63 *ach.FieldError testField nil could not parse" { - t.Error("ParseError error string formating has changed") + t.Error("ParseError error string formatting has changed") } err.Record = "TestRecord" if err.Error() != "line:63 record:TestRecord *ach.FieldError testField nil could not parse" { - t.Error("ParseError error string formating has changed") + t.Error("ParseError error string formatting has changed") } } @@ -347,7 +347,7 @@ func testFileBatchHeaderDuplicate(t testing.TB) { // create a new Batch header string bh := mockBatchPPDHeader() r := NewReader(strings.NewReader(bh.String())) - // instantitate a batch header in the reader + // instantiate a batch header in the reader r.addCurrentBatch(NewBatchPPD(bh)) // read should fail because it is parsing a second batch header and there can only be one. _, err := r.Read() @@ -437,7 +437,7 @@ func BenchmarkFileEntryDetail(b *testing.B) { } } -// testFileAddenda05 validates addenda 05 +// testFileAddenda05 validates error for an invalid addenda05 func testFileAddenda05(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() @@ -460,12 +460,12 @@ func testFileAddenda05(t testing.TB) { } } -// TestFileAddenda05 tests validating addenda 05 +// TestFileAddenda05 tests validating error for an invalid addenda05 func TestFileAddenda05(t *testing.T) { testFileAddenda05(t) } -// BenchmarkFileAddenda05 benchmarks validating addenda 05 +// BenchmarkFileAddenda05 benchmarks validating error for an invalid addenda05 func BenchmarkFileAddenda05(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -473,12 +473,120 @@ func BenchmarkFileAddenda05(b *testing.B) { } } -// testFileAddenda98 validates addenda 98 +// testFileAddenda02invalid validates error for an invalid addenda02 +func testFileAddenda02invalid(t testing.TB) { + bh := mockBatchPOSHeader() + ed := mockPOSEntryDetail() + addenda := mockAddenda02() + addenda.TransactionDate = "0000" + ed.AddAddenda(addenda) + line := bh.String() + "\n" + ed.String() + "\n" + ed.Addendum[0].String() + r := NewReader(strings.NewReader(line)) + _, err := r.Read() + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestFileAddenda02invalid tests validating error for an invalid addenda02 +func TestFileAddenda02invalid(t *testing.T) { + testFileAddenda02invalid(t) +} + +// BenchmarkFileAddenda02invalid benchmarks validating error for an invalid addenda02 +func BenchmarkFileAddenda02invalid(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddenda02invalid(b) + } +} + +// testFileAddenda02 validates a valid addenda02 +func testFileAddenda02(t testing.TB) { + bh := mockBatchPOSHeader() + ed := mockPOSEntryDetail() + addenda := mockAddenda02() + ed.AddAddenda(addenda) + line := bh.String() + "\n" + ed.String() + "\n" + ed.Addendum[0].String() + r := NewReader(strings.NewReader(line)) + _, err := r.Read() + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.FieldName != "TransactionDate" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestFileAddenda02invalid tests validating a valid addenda02 +func TestFileAddenda02(t *testing.T) { + testFileAddenda02(t) +} + +// BenchmarkFileAddenda02 benchmarks validating a valid addenda02 +func BenchmarkFileAddenda02(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddenda02(b) + } +} + +// testFileAddenda98 validates error for an invalid addenda98 +func testFileAddenda98invalid(t testing.TB) { + bh := mockBatchPPDHeader() + ed := mockPPDEntryDetail() + addenda := mockAddenda98() + addenda.TraceNumber = 0000001 + addenda.ChangeCode = "C50" + addenda.CorrectedData = "ACME One Corporation" + ed.AddAddenda(addenda) + line := bh.String() + "\n" + ed.String() + "\n" + ed.Addendum[0].String() + r := NewReader(strings.NewReader(line)) + _, err := r.Read() + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.FieldName != "ChangeCode" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestFileAddenda98 tests validating error for an invalid addenda98 +func TestFileAddenda98invalid(t *testing.T) { + testFileAddenda98invalid(t) +} + +// BenchmarkFileAddenda98 benchmarks validating error for an invalid addenda98 +func BenchmarkFileAddenda98invalid(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddenda98invalid(b) + } +} + +// testFileAddenda98 validates a valid addenda98 func testFileAddenda98(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() addenda := mockAddenda98() - addenda.TraceNumber = 0000001 addenda.ChangeCode = "C10" addenda.CorrectedData = "ACME One Corporation" @@ -499,12 +607,12 @@ func testFileAddenda98(t testing.TB) { } } -// TestFileAddenda98 tests validating addenda 98 +// TestFileAddenda98 tests validating a valid addenda98 func TestFileAddenda98(t *testing.T) { testFileAddenda98(t) } -// BenchmarkFileAddenda98 benchmarks validating addenda 98 +// BenchmarkFileAddenda98 benchmarks validating a valid addenda98 func BenchmarkFileAddenda98(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -512,7 +620,44 @@ func BenchmarkFileAddenda98(b *testing.B) { } } -// testFileAddenda99 validates addenda 99 +// testFileAddenda99invalid validates error for an invalid addenda99 +func testFileAddenda99invalid(t testing.TB) { + bh := mockBatchPPDHeader() + ed := mockPPDEntryDetail() + addenda := mockAddenda99() + addenda.TraceNumber = 0000001 + addenda.ReturnCode = "100" + ed.AddAddenda(addenda) + line := bh.String() + "\n" + ed.String() + "\n" + ed.Addendum[0].String() + r := NewReader(strings.NewReader(line)) + _, err := r.Read() + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.FieldName != "ReturnCode" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestFileAddenda99invalid tests validating error for an invalid addenda99 +func TestFileAddenda99invalid(t *testing.T) { + testFileAddenda99invalid(t) +} + +// BenchmarkFileAddenda99invalid benchmarks validating error for an invalid addenda99 +func BenchmarkFileAddenda99invalid(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileAddenda99invalid(b) + } +} + +// testFileAddenda99 validates a valid addenda99 func testFileAddenda99(t testing.TB) { bh := mockBatchHeader() ed := mockEntryDetail() @@ -536,12 +681,12 @@ func testFileAddenda99(t testing.TB) { } } -// TestFileAddenda99 tests validating addenda 99 +// TestFileAddenda99 tests validating a valid addenda99 func TestFileAddenda99(t *testing.T) { testFileAddenda99(t) } -// BenchmarkFileAddenda99 benchmarks validating addenda 99 +// BenchmarkFileAddenda99 benchmarks validating a valid addenda99 func BenchmarkFileAddenda99(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/test/ach-pos-read/main.go b/test/ach-pos-read/main.go new file mode 100644 index 000000000..f32c1d10b --- /dev/null +++ b/test/ach-pos-read/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "github.com/bkmoovio/ach" + "log" + "os" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("pos-debit.ach") + if err != nil { + log.Fatal(err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("POS Card Transaction Type : %v \n", achFile.Batches[0].GetEntries()[0].DiscretionaryDataField()) + fmt.Printf("POS Trace Number: %v \n", achFile.Batches[0].GetEntries()[0].TraceNumberField()) +} diff --git a/test/ach-pos-read/main_test.go b/test/ach-pos-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-pos-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-pos-read/pos-debit.ach b/test/ach-pos-read/pos-debit.ach new file mode 100644 index 000000000..2326cd44d --- /dev/null +++ b/test/ach-pos-read/pos-debit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821806140000A094101Federal Reserve Bank My Bank Name +5225Name on Account 121042882 POSREG.SALARY 180615 0121042880000001 +62723138010412345678 0100000000 Receiver Account Name 011121042880000001 +702REFONEAREFTERM021000490614123456Target Store 0049 PHILADELPHIA PA121042880000001 +82250000020023138010000100000000000000000000121042882 121042880000001 +9000001000001000000020023138010000100000000000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-pos-write/main.go b/test/ach-pos-write/main.go new file mode 100644 index 000000000..83e7cfb4d --- /dev/null +++ b/test/ach-pos-write/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "github.com/bkmoovio/ach" + "log" + "os" + "time" +) + +func main() { + // Example transfer to write an ACH POS file to send/credit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + // Set originator bank ODFI and destination Operator for the financial institution + // this is the funding/receiving source of the transfer + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "POS" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "REG.SALARY" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 22: Debit (withdrawal) from checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.IndividualName = "Receiver Account Name" // Identifies the receiver of the transaction + entry.DiscretionaryData = "01" + + addenda02 := ach.NewAddenda02() + addenda02.ReferenceInformationOne = "REFONEA" + addenda02.ReferenceInformationTwo = "REF" + addenda02.TerminalIdentificationCode = "TERM02" + addenda02.TransactionSerialNumber = "100049" + addenda02.TransactionDate = "0614" + addenda02.AuthorizationCodeOrExpireDate = "123456" + addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalCity = "PHILADELPHIA" + addenda02.TerminalState = "PA" + addenda02.TraceNumber = 121042880000001 + + // build the batch + batch := ach.NewBatchPOS(bh) + batch.AddEntry(entry) + batch.GetEntries()[0].AddAddenda(addenda02) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.WriteAll([]*ach.File{file}); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/test/ach-pos-write/main_test.go b/test/ach-pos-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-pos-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From 5719e154a98ada5c20bb8cad6e9bc04d2fa0f82d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 15 Jun 2018 11:13:57 -0400 Subject: [PATCH 0214/1694] #208 Moved Required (vs Mandatory) checks from Addenda02.fieldInclusion to Addenda02.Validate --- addenda02.go | 44 ++++++++++++++++++++------------------------ addenda02_test.go | 12 ++++++------ validators.go | 1 + 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/addenda02.go b/addenda02.go index d9faa9395..2d7f97264 100644 --- a/addenda02.go +++ b/addenda02.go @@ -125,6 +125,25 @@ func (addenda02 *Addenda02) Validate() error { if addenda02.typeCode != "02" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgAddendaTypeCode} } + if addenda02.TerminalIdentificationCode == "" { + return &FieldError{FieldName: "TerminalIdentificationCode", Value: addenda02.TerminalIdentificationCode, Msg: msgFieldRequired} + } + if addenda02.TransactionSerialNumber == "" { + return &FieldError{FieldName: "TransactionSerialNumber", Value: addenda02.TransactionSerialNumber, Msg: msgFieldRequired} + } + if addenda02.TransactionDate == "" { + return &FieldError{FieldName: "TransactionDate", Value: addenda02.TransactionDate, Msg: msgFieldRequired} + } + if addenda02.TerminalLocation == "" { + return &FieldError{FieldName: "TerminalLocation", Value: addenda02.TerminalLocation, Msg: msgFieldRequired} + } + if addenda02.TerminalCity == "" { + return &FieldError{FieldName: "TerminalCity", Value: addenda02.TerminalCity, Msg: msgFieldRequired} + } + if addenda02.TerminalState == "" { + return &FieldError{FieldName: "TerminalState", Value: addenda02.TerminalState, Msg: msgFieldRequired} + } + // ToDo: Determine if TraceNumber fieldInclusion is necessary // TransactionDate Addenda02 ACH File format is MMDD. Validate MM is 01-12. if err := addenda02.isMonth(addenda02.parseStringField(addenda02.TransactionDate[0:2])); err != nil { return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidMonth} @@ -132,7 +151,7 @@ func (addenda02 *Addenda02) Validate() error { // TransactionDate Addenda02 ACH File format is MMDD. If the month is valid, validate the day for the // month 01-31 depending on month. if err := addenda02.isDay(addenda02.parseStringField(addenda02.TransactionDate[0:2]), addenda02.parseStringField(addenda02.TransactionDate[2:4])); err != nil { - return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidDay} + return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[2:4]), Msg: msgValidDay} } return nil } @@ -140,8 +159,6 @@ func (addenda02 *Addenda02) Validate() error { // fieldInclusion validate mandatory fields are not default values. If fields are // invalid the ACH transfer will be returned. -// ToDo: check if we should do fieldInclusion or validate on required fields - func (addenda02 *Addenda02) fieldInclusion() error { if addenda02.recordType == "" { return &FieldError{FieldName: "recordType", Value: addenda02.recordType, Msg: msgFieldInclusion} @@ -149,27 +166,6 @@ func (addenda02 *Addenda02) fieldInclusion() error { if addenda02.typeCode == "" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgFieldInclusion} } - - // ToDo: These are not mandatory fields should be outside of fieldInclusion - if addenda02.TerminalIdentificationCode == "" { - return &FieldError{FieldName: "TerminalIdentificationCode", Value: addenda02.TerminalIdentificationCode, Msg: msgFieldInclusion} - } - if addenda02.TransactionSerialNumber == "" { - return &FieldError{FieldName: "TransactionSerialNumber", Value: addenda02.TransactionSerialNumber, Msg: msgFieldInclusion} - } - if addenda02.TransactionDate == "" { - return &FieldError{FieldName: "TransactionDate", Value: addenda02.TransactionDate, Msg: msgFieldInclusion} - } - if addenda02.TerminalLocation == "" { - return &FieldError{FieldName: "TerminalLocation", Value: addenda02.TerminalLocation, Msg: msgFieldInclusion} - } - if addenda02.TerminalCity == "" { - return &FieldError{FieldName: "TerminalCity", Value: addenda02.TerminalCity, Msg: msgFieldInclusion} - } - if addenda02.TerminalState == "" { - return &FieldError{FieldName: "TerminalState", Value: addenda02.TerminalState, Msg: msgFieldInclusion} - } - // ToDo: Determine if TraceNumber fieldInclusion is necessary return nil } diff --git a/addenda02_test.go b/addenda02_test.go index cf3032092..9ba6727e7 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -153,7 +153,7 @@ func testAddenda02TerminalIdentificationCode(t testing.TB) { addenda02.TerminalIdentificationCode = "" if err := addenda02.Validate(); err != nil { if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { + if e.Msg != msgFieldRequired { t.Errorf("%T: %s", err, err) } } @@ -176,7 +176,7 @@ func testAddenda02TransactionSerialNumber(t testing.TB) { addenda02.TransactionSerialNumber = "" if err := addenda02.Validate(); err != nil { if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { + if e.Msg != msgFieldRequired { t.Errorf("%T: %s", err, err) } } @@ -199,7 +199,7 @@ func testAddenda02TransactionDate(t testing.TB) { addenda02.TransactionDate = "" if err := addenda02.Validate(); err != nil { if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { + if e.Msg != msgFieldRequired { t.Errorf("%T: %s", err, err) } } @@ -222,7 +222,7 @@ func testAddenda02TerminalLocation(t testing.TB) { addenda02.TerminalLocation = "" if err := addenda02.Validate(); err != nil { if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { + if e.Msg != msgFieldRequired { t.Errorf("%T: %s", err, err) } } @@ -245,7 +245,7 @@ func testAddenda02TerminalCity(t testing.TB) { addenda02.TerminalCity = "" if err := addenda02.Validate(); err != nil { if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { + if e.Msg != msgFieldRequired { t.Errorf("%T: %s", err, err) } } @@ -268,7 +268,7 @@ func testAddenda02TerminalState(t testing.TB) { addenda02.TerminalState = "" if err := addenda02.Validate(); err != nil { if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldInclusion { + if e.Msg != msgFieldRequired { t.Errorf("%T: %s", err, err) } } diff --git a/validators.go b/validators.go index 3f375eea8..ca13e000c 100644 --- a/validators.go +++ b/validators.go @@ -19,6 +19,7 @@ var ( msgAlphanumeric = "has non alphanumeric characters" msgUpperAlpha = "is not uppercase A-Z or 0-9" msgFieldInclusion = "is a mandatory field and has a default value" + msgFieldRequired = "is a required field" msgValidFieldLength = "is not length %d" msgServiceClass = "is an invalid Service Class Code" msgSECCode = "is an invalid Standard Entry Class Code" From 1357d626378f30b38327bcd5e462fa0c4cb15090 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 09:47:23 -0400 Subject: [PATCH 0215/1694] #208 msgFieldRequired #208 msgFieldRequired --- addenda02.go | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/addenda02.go b/addenda02.go index 2d7f97264..eb05c576e 100644 --- a/addenda02.go +++ b/addenda02.go @@ -21,10 +21,10 @@ type Addenda02 struct { typeCode string // ReferenceInformationOne may be used for additional reference numbers, identification numbers, // or codes that the merchant needs to identify the particular transaction or customer. - ReferenceInformationOne string `json:"referenceInformationOne"` + ReferenceInformationOne string `json:"referenceInformationOne, omitempty"` // ReferenceInformationTwo may be used for additional reference numbers, identification numbers, // or codes that the merchant needs to identify the particular transaction or customer. - ReferenceInformationTwo string `json:"referenceInformationTwo"` + ReferenceInformationTwo string `json:"referenceInformationTwo, omitempty"` // TerminalIdentificationCode identifies an Electronic terminal with a unique code that allows // a terminal owner and/or switching network to identify the terminal at which an Entry originated. TerminalIdentificationCode string `json:"terminalIdentificationCode"` @@ -36,7 +36,7 @@ type Addenda02 struct { TransactionDate string `json:"transactionDate"` // AuthorizationCodeOrExpireDate indicates the code that a card authorization center has // furnished to the merchant. - AuthorizationCodeOrExpireDate string `json:"authorizationCodeOrExpireDate"` + AuthorizationCodeOrExpireDate string `json:"authorizationCodeOrExpireDate, omitempty"` // Terminal Location identifies the specific location of a terminal (i.e., street names of an // intersection, address, etc.) in accordance with the requirements of Regulation E. TerminalLocation string `json:"terminalLocation"` @@ -125,25 +125,6 @@ func (addenda02 *Addenda02) Validate() error { if addenda02.typeCode != "02" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgAddendaTypeCode} } - if addenda02.TerminalIdentificationCode == "" { - return &FieldError{FieldName: "TerminalIdentificationCode", Value: addenda02.TerminalIdentificationCode, Msg: msgFieldRequired} - } - if addenda02.TransactionSerialNumber == "" { - return &FieldError{FieldName: "TransactionSerialNumber", Value: addenda02.TransactionSerialNumber, Msg: msgFieldRequired} - } - if addenda02.TransactionDate == "" { - return &FieldError{FieldName: "TransactionDate", Value: addenda02.TransactionDate, Msg: msgFieldRequired} - } - if addenda02.TerminalLocation == "" { - return &FieldError{FieldName: "TerminalLocation", Value: addenda02.TerminalLocation, Msg: msgFieldRequired} - } - if addenda02.TerminalCity == "" { - return &FieldError{FieldName: "TerminalCity", Value: addenda02.TerminalCity, Msg: msgFieldRequired} - } - if addenda02.TerminalState == "" { - return &FieldError{FieldName: "TerminalState", Value: addenda02.TerminalState, Msg: msgFieldRequired} - } - // ToDo: Determine if TraceNumber fieldInclusion is necessary // TransactionDate Addenda02 ACH File format is MMDD. Validate MM is 01-12. if err := addenda02.isMonth(addenda02.parseStringField(addenda02.TransactionDate[0:2])); err != nil { return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidMonth} @@ -151,14 +132,16 @@ func (addenda02 *Addenda02) Validate() error { // TransactionDate Addenda02 ACH File format is MMDD. If the month is valid, validate the day for the // month 01-31 depending on month. if err := addenda02.isDay(addenda02.parseStringField(addenda02.TransactionDate[0:2]), addenda02.parseStringField(addenda02.TransactionDate[2:4])); err != nil { - return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[2:4]), Msg: msgValidDay} + return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidDay} } return nil } -// fieldInclusion validate mandatory fields are not default values. If fields are +// fieldInclusion validate mandatory fields are not default values and rquired fields are defined. If fields are // invalid the ACH transfer will be returned. +// ToDo: check if we should do fieldInclusion or validate on required fields + func (addenda02 *Addenda02) fieldInclusion() error { if addenda02.recordType == "" { return &FieldError{FieldName: "recordType", Value: addenda02.recordType, Msg: msgFieldInclusion} @@ -166,6 +149,25 @@ func (addenda02 *Addenda02) fieldInclusion() error { if addenda02.typeCode == "" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgFieldInclusion} } + // Required Fields + if addenda02.TerminalIdentificationCode == "" { + return &FieldError{FieldName: "TerminalIdentificationCode", Value: addenda02.TerminalIdentificationCode, Msg: msgFieldRequired} + } + if addenda02.TransactionSerialNumber == "" { + return &FieldError{FieldName: "TransactionSerialNumber", Value: addenda02.TransactionSerialNumber, Msg: msgFieldRequired} + } + if addenda02.TransactionDate == "" { + return &FieldError{FieldName: "TransactionDate", Value: addenda02.TransactionDate, Msg: msgFieldRequired} + } + if addenda02.TerminalLocation == "" { + return &FieldError{FieldName: "TerminalLocation", Value: addenda02.TerminalLocation, Msg: msgFieldRequired} + } + if addenda02.TerminalCity == "" { + return &FieldError{FieldName: "TerminalCity", Value: addenda02.TerminalCity, Msg: msgFieldRequired} + } + if addenda02.TerminalState == "" { + return &FieldError{FieldName: "TerminalState", Value: addenda02.TerminalState, Msg: msgFieldRequired} + } return nil } From 93a39d51b1ef1720bdbaf9dee04a687a3f5a760f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 09:54:35 -0400 Subject: [PATCH 0216/1694] #208 remove extra space #208 remove extra space --- addenda02.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addenda02.go b/addenda02.go index eb05c576e..e6ed64381 100644 --- a/addenda02.go +++ b/addenda02.go @@ -21,10 +21,10 @@ type Addenda02 struct { typeCode string // ReferenceInformationOne may be used for additional reference numbers, identification numbers, // or codes that the merchant needs to identify the particular transaction or customer. - ReferenceInformationOne string `json:"referenceInformationOne, omitempty"` + ReferenceInformationOne string `json:"referenceInformationOne,omitempty"` // ReferenceInformationTwo may be used for additional reference numbers, identification numbers, // or codes that the merchant needs to identify the particular transaction or customer. - ReferenceInformationTwo string `json:"referenceInformationTwo, omitempty"` + ReferenceInformationTwo string `json:"referenceInformationTwo,omitempty"` // TerminalIdentificationCode identifies an Electronic terminal with a unique code that allows // a terminal owner and/or switching network to identify the terminal at which an Entry originated. TerminalIdentificationCode string `json:"terminalIdentificationCode"` @@ -36,7 +36,7 @@ type Addenda02 struct { TransactionDate string `json:"transactionDate"` // AuthorizationCodeOrExpireDate indicates the code that a card authorization center has // furnished to the merchant. - AuthorizationCodeOrExpireDate string `json:"authorizationCodeOrExpireDate, omitempty"` + AuthorizationCodeOrExpireDate string `json:"authorizationCodeOrExpireDate,omitempty"` // Terminal Location identifies the specific location of a terminal (i.e., street names of an // intersection, address, etc.) in accordance with the requirements of Regulation E. TerminalLocation string `json:"terminalLocation"` From 3fad05bbe248d8e240a90afcf721975b3f860f90 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 10:12:16 -0400 Subject: [PATCH 0217/1694] change import to moovio change import to moovio --- test/ach-pos-read/main.go | 2 +- test/ach-pos-write/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ach-pos-read/main.go b/test/ach-pos-read/main.go index f32c1d10b..0481c72ec 100644 --- a/test/ach-pos-read/main.go +++ b/test/ach-pos-read/main.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "log" "os" ) diff --git a/test/ach-pos-write/main.go b/test/ach-pos-write/main.go index 83e7cfb4d..a010e2f9a 100644 --- a/test/ach-pos-write/main.go +++ b/test/ach-pos-write/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "log" "os" "time" From f27e02dcc9d9d39f10fb172239baa2e3a003f332 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 10:19:12 -0400 Subject: [PATCH 0218/1694] Revert to bkmoovio for bkmoovio repo Revert to bkmoovio for bkmoovio repo, until the fork is updated. --- test/ach-pos-read/main.go | 2 +- test/ach-pos-write/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ach-pos-read/main.go b/test/ach-pos-read/main.go index 0481c72ec..f32c1d10b 100644 --- a/test/ach-pos-read/main.go +++ b/test/ach-pos-read/main.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/moov-io/ach" + "github.com/bkmoovio/ach" "log" "os" ) diff --git a/test/ach-pos-write/main.go b/test/ach-pos-write/main.go index a010e2f9a..83e7cfb4d 100644 --- a/test/ach-pos-write/main.go +++ b/test/ach-pos-write/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/moov-io/ach" + "github.com/bkmoovio/ach" "log" "os" "time" From acdcc2632a7c8cffa1575bad8d9f8d7b0798f207 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 10:19:57 -0400 Subject: [PATCH 0219/1694] break bkmoovio for moov-io master --- test/ach-pos-read/main.go | 2 +- test/ach-pos-write/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ach-pos-read/main.go b/test/ach-pos-read/main.go index f32c1d10b..0481c72ec 100644 --- a/test/ach-pos-read/main.go +++ b/test/ach-pos-read/main.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "log" "os" ) diff --git a/test/ach-pos-write/main.go b/test/ach-pos-write/main.go index 83e7cfb4d..a010e2f9a 100644 --- a/test/ach-pos-write/main.go +++ b/test/ach-pos-write/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "log" "os" "time" From 484be249043df4dae366bc50c18100e7440c41f1 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 11:01:26 -0400 Subject: [PATCH 0220/1694] #208 Update Readme fro POS and Addenda Records #208 Update Readme fro POS and Addenda Records --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 30bbaa659..353b4f402 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,16 @@ ACH is under active development but already in production for multiple companies * CCD (Corporate credit or debit) * COR (Automated Notification of Change(NOC)) * POP (Point of Purchase) + * POS (Point of Sale) * PPD (Prearranged payment and deposits) * RCK (Represented Check Entries) * TEL (Telephone-Initiated Entry) * WEB (Internet-initiated Entries) * Return Entries + * Addenda Type Code 02 + * Addenda Type Code 05 + * Addenda Type Code 98 + * Addenda Type Code 99 ## Project Roadmap From 22f3e9b522b9ff975543d5fdfd7990637d0d249e Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 11:29:59 -0600 Subject: [PATCH 0221/1694] Move testdata into ./test/data/ --- {testdata => test/data}/20110729A.ach | 0 {testdata => test/data}/20110805A.ach | 0 {testdata => test/data}/ppd-debit-fixedLength.ach | 0 {testdata => test/data}/ppd-debit.ach | 0 {testdata => test/data}/rck.ach | 0 {testdata => test/data}/web-debit.ach | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {testdata => test/data}/20110729A.ach (100%) rename {testdata => test/data}/20110805A.ach (100%) rename {testdata => test/data}/ppd-debit-fixedLength.ach (100%) rename {testdata => test/data}/ppd-debit.ach (100%) rename {testdata => test/data}/rck.ach (100%) rename {testdata => test/data}/web-debit.ach (100%) diff --git a/testdata/20110729A.ach b/test/data/20110729A.ach similarity index 100% rename from testdata/20110729A.ach rename to test/data/20110729A.ach diff --git a/testdata/20110805A.ach b/test/data/20110805A.ach similarity index 100% rename from testdata/20110805A.ach rename to test/data/20110805A.ach diff --git a/testdata/ppd-debit-fixedLength.ach b/test/data/ppd-debit-fixedLength.ach similarity index 100% rename from testdata/ppd-debit-fixedLength.ach rename to test/data/ppd-debit-fixedLength.ach diff --git a/testdata/ppd-debit.ach b/test/data/ppd-debit.ach similarity index 100% rename from testdata/ppd-debit.ach rename to test/data/ppd-debit.ach diff --git a/testdata/rck.ach b/test/data/rck.ach similarity index 100% rename from testdata/rck.ach rename to test/data/rck.ach diff --git a/testdata/web-debit.ach b/test/data/web-debit.ach similarity index 100% rename from testdata/web-debit.ach rename to test/data/web-debit.ach From c651dfe7253530d4b244f73975e21f6bd430c52b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 11:30:10 -0600 Subject: [PATCH 0222/1694] Update reference for reader test --- reader_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reader_test.go b/reader_test.go index 125b2b24d..deb44e014 100644 --- a/reader_test.go +++ b/reader_test.go @@ -38,7 +38,7 @@ func BenchmarkParseError(b *testing.B) { // testPPDDebitRead validates reading a PPD debit func testPPDDebitRead(t testing.TB) { - f, err := os.Open("./testdata/ppd-debit.ach") + f, err := os.Open("./test/data/ppd-debit.ach") if err != nil { t.Errorf("%T: %s", err, err) } @@ -68,7 +68,7 @@ func BenchmarkPPDDebitRead(b *testing.B) { // testWEBDebitRead validates reading a WEB debit func testWEBDebitRead(t testing.TB) { - f, err := os.Open("./testdata/web-debit.ach") + f, err := os.Open("./test/data/web-debit.ach") if err != nil { t.Errorf("%T: %s", err, err) } @@ -98,7 +98,7 @@ func BenchmarkWEBDebitRead(b *testing.B) { // testPPDDebitFixedLengthRead validates reading a PPD debit fixed width length func testPPDDebitFixedLengthRead(t testing.TB) { - f, err := os.Open("./testdata/ppd-debit-fixedLength.ach") + f, err := os.Open("./test/data/ppd-debit-fixedLength.ach") if err != nil { t.Errorf("%T: %s", err, err) } From d11206d3cb85eac287644bcc6b881ccb3dafc1eb Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 11:36:01 -0600 Subject: [PATCH 0223/1694] Readme for integration tests --- test/data/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test/data/README.md diff --git a/test/data/README.md b/test/data/README.md new file mode 100644 index 000000000..6f8d4f2d2 --- /dev/null +++ b/test/data/README.md @@ -0,0 +1,3 @@ +## Test Data + +Data files utilized for integration tests. \ No newline at end of file From c22260ad02869807cde286f3ad9b06c10ebf7c4c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 14:56:43 -0400 Subject: [PATCH 0224/1694] Update fork from master Update fork from master --- batch.go | 2 + batchPOS_test.go | 3 +- batchSHR.go | 102 +++++++++++ batchSHR_test.go | 437 +++++++++++++++++++++++++++++++++++++++++++++++ converters.go | 1 + entryDetail.go | 36 ++++ 6 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 batchSHR.go create mode 100644 batchSHR_test.go diff --git a/batch.go b/batch.go index 70915491e..439305aef 100644 --- a/batch.go +++ b/batch.go @@ -43,6 +43,8 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { return NewBatchPPD(bh), nil case "RCK": return NewBatchRCK(bh), nil + case "SHR": + return NewBatchSHR(bh), nil case "TEL": return NewBatchTEL(bh), nil case "WEB": diff --git a/batchPOS_test.go b/batchPOS_test.go index 575472fe2..69f377b0e 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -26,7 +26,8 @@ func mockPOSEntryDetail() *EntryDetail { entry.DFIAccountNumber = "744-5678-99" entry.Amount = 25000 entry.IdentificationNumber = "45689033" - entry.SetReceivingCompany("ABC Company") + entry.IndividualName = "Wade Arnold" + //entry.SetReceivingCompany("ABC Company") entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) entry.DiscretionaryData = "01" entry.Category = CategoryForward diff --git a/batchSHR.go b/batchSHR.go new file mode 100644 index 000000000..0820c4323 --- /dev/null +++ b/batchSHR.go @@ -0,0 +1,102 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// BatchSHR holds the BatchHeader and BatchControl and all EntryDetail for SHR Entries. +// +// Shared Network Entry (SHR) is a debit Entry initiated at an “electronic terminal,” +// as that term is defined in Regulation E, to a Consumer Account of the Receiver to pay +// an obligation incurred in a point-of-sale transaction, or to effect a point-of-sale +// terminal cash withdrawal. Also an adjusting or other credit Entry related to such debit +// Entry, transfer of funds, or obligation. SHR Entries are initiated in a shared network +// where the ODFI and RDFI have an agreement in addition to these Rules to process such +// Entries. +type BatchSHR struct { + batch +} + +var msgBatchSHRAddenda = "found and 1 Addenda02 is required for SEC code SHR" +var msgBatchSHRAddendaType = "%T found where Addenda02 is required for SEC code SHR" + +// NewBatchSHR returns a *BatchSHR +func NewBatchSHR(bh *BatchHeader) *BatchSHR { + batch := new(BatchSHR) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchSHR) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // Add type specific validation. + + if batch.Header.StandardEntryClassCode != "SHR" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "SHR") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + // SHR detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 220, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "SHR") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + } + + for _, entry := range batch.Entries { + // SHR detail entries must be a debit + if entry.CreditOrDebit() != "D" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + if err := entry.isCardTransactionType(entry.DiscretionaryData); err != nil { + msg := fmt.Sprintf(msgBatchCardTransactionType, entry.DiscretionaryData) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CardTransactionType", Msg: msg} + } + + // Addenda validations - SHR Addenda must be Addenda02 + + // Addendum must be equal to 1 + if len(entry.Addendum) != 1 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchSHRAddenda} + } + + // Addenda type assertion must be Addenda02 + addenda02, ok := entry.Addendum[0].(*Addenda02) + if !ok { + msg := fmt.Sprintf(msgBatchSHRAddendaType, entry.Addendum[0]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} + } + + // Addenda02 must be Validated + if err := addenda02.Validate(); err != nil { + // convert the field error in to a batch error for a consistent api + if e, ok := err.(*FieldError); ok { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} + } + } + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchSHR) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + return batch.Validate() +} diff --git a/batchSHR_test.go b/batchSHR_test.go new file mode 100644 index 000000000..67a397772 --- /dev/null +++ b/batchSHR_test.go @@ -0,0 +1,437 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +// mockBatchSHRHeader creates a BatchSHR BatchHeader +func mockBatchSHRHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 225 + bh.StandardEntryClassCode = "SHR" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH SHR" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockSHREntryDetail creates a BatchSHR EntryDetail +func mockSHREntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 27 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.SetSHRCardExpirationDate("0718") + entry.SetSHRDocumentReferenceNumber(12345678910) + entry.SetSHRIndividualCardAccountNumber(12345678910123456) + entry.SetTraceNumber(mockBatchSHRHeader().ODFIIdentification, 123) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + return entry +} + +// mockBatchSHR creates a BatchSHR +func mockBatchSHR() *BatchSHR { + mockBatch := NewBatchSHR(mockBatchSHRHeader()) + mockBatch.AddEntry(mockSHREntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// testBatchSHRHeader creates a BatchSHR BatchHeader +func testBatchSHRHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchSHRHeader()) + err, ok := batch.(*BatchSHR) + if !ok { + t.Errorf("Expecting BatchSHR got %T", err) + } +} + +// TestBatchSHRHeader tests validating BatchSHR BatchHeader +func TestBatchSHRHeader(t *testing.T) { + testBatchSHRHeader(t) +} + +// BenchmarkBatchSHRHeader benchmarks validating BatchSHR BatchHeader +func BenchmarkBatchSHRHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRHeader(b) + } +} + +// testBatchSHRCreate validates BatchSHR create +func testBatchSHRCreate(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchSHRCreate tests validating BatchSHR create +func TestBatchSHRCreate(t *testing.T) { + testBatchSHRCreate(t) +} + +// BenchmarkBatchSHRCreate benchmarks validating BatchSHR create +func BenchmarkBatchSHRCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRCreate(b) + } +} + +// testBatchSHRStandardEntryClassCode validates BatchSHR create for an invalid StandardEntryClassCode +func testBatchSHRStandardEntryClassCode(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRStandardEntryClassCode tests validating BatchSHR create for an invalid StandardEntryClassCode +func TestBatchSHRStandardEntryClassCode(t *testing.T) { + testBatchSHRStandardEntryClassCode(t) +} + +// BenchmarkBatchSHRStandardEntryClassCode benchmarks validating BatchSHR create for an invalid StandardEntryClassCode +func BenchmarkBatchSHRStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRStandardEntryClassCode(b) + } +} + +// testBatchSHRServiceClassCodeEquality validates service class code equality +func testBatchSHRServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRServiceClassCodeEquality tests validating service class code equality +func TestBatchSHRServiceClassCodeEquality(t *testing.T) { + testBatchSHRServiceClassCodeEquality(t) +} + +// BenchmarkBatchSHRServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchSHRServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRServiceClassCodeEquality(b) + } +} + +// testBatchSHRServiceClass200 validates BatchSHR create for an invalid ServiceClassCode 200 +func testBatchSHRServiceClass200(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.Header.ServiceClassCode = 200 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRServiceClass200 tests validating BatchSHR create for an invalid ServiceClassCode 200 +func TestBatchSHRServiceClass200(t *testing.T) { + testBatchSHRServiceClass200(t) +} + +// BenchmarkBatchSHRServiceClass200 benchmarks validating BatchSHR create for an invalid ServiceClassCode 200 +func BenchmarkBatchSHRServiceClass200(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRServiceClass200(b) + } +} + +// testBatchSHRServiceClass220 validates BatchSHR create for an invalid ServiceClassCode 220 +func testBatchSHRServiceClass220(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.Header.ServiceClassCode = 220 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRServiceClass220 tests validating BatchSHR create for an invalid ServiceClassCode 220 +func TestBatchSHRServiceClass220(t *testing.T) { + testBatchSHRServiceClass220(t) +} + +// BenchmarkBatchSHRServiceClass220 benchmarks validating BatchSHR create for an invalid ServiceClassCode 220 +func BenchmarkBatchSHRServiceClass220(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRServiceClass220(b) + } +} + +// testBatchSHRServiceClass280 validates BatchSHR create for an invalid ServiceClassCode 280 +func testBatchSHRServiceClass280(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.Header.ServiceClassCode = 280 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRServiceClass280 tests validating BatchSHR create for an invalid ServiceClassCode 280 +func TestBatchSHRServiceClass280(t *testing.T) { + testBatchSHRServiceClass280(t) +} + +// BenchmarkBatchSHRServiceClass280 benchmarks validating BatchSHR create for an invalid ServiceClassCode 280 +func BenchmarkBatchSHRServiceClass280(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRServiceClass280(b) + } +} + +// testBatchSHRTransactionCode validates BatchSHR TransactionCode is not a credit +func testBatchSHRTransactionCode(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.GetEntries()[0].TransactionCode = 22 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRTransactionCode tests validating BatchSHR TransactionCode is not a credit +func TestBatchSHRTransactionCode(t *testing.T) { + testBatchSHRTransactionCode(t) +} + +// BenchmarkBatchSHRTransactionCode benchmarks validating BatchSHR TransactionCode is not a credit +func BenchmarkBatchSHRTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRTransactionCode(b) + } +} + +// testBatchSHRAddendaCount validates BatchSHR Addendum count of 2 +func testBatchSHRAddendaCount(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRAddendaCount tests validating BatchSHR Addendum count of 2 +func TestBatchSHRAddendaCount(t *testing.T) { + testBatchSHRAddendaCount(t) +} + +// BenchmarkBatchSHRAddendaCount benchmarks validating BatchSHR Addendum count of 2 +func BenchmarkBatchSHRAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRAddendaCount(b) + } +} + +// testBatchSHRAddendaCountZero validates Addendum count of 0 +func testBatchSHRAddendaCountZero(t testing.TB) { + mockBatch := NewBatchSHR(mockBatchSHRHeader()) + mockBatch.AddEntry(mockSHREntryDetail()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRAddendaCountZero tests validating Addendum count of 0 +func TestBatchSHRAddendaCountZero(t *testing.T) { + testBatchSHRAddendaCountZero(t) +} + +// BenchmarkBatchSHRAddendaCountZero benchmarks validating Addendum count of 0 +func BenchmarkBatchSHRAddendaCountZero(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRAddendaCountZero(b) + } +} + +// testBatchSHRInvalidAddendum validates Addendum must be Addenda02 +func testBatchSHRInvalidAddendum(t testing.TB) { + mockBatch := NewBatchSHR(mockBatchSHRHeader()) + mockBatch.AddEntry(mockSHREntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRInvalidAddendum tests validating Addendum must be Addenda02 +func TestBatchSHRInvalidAddendum(t *testing.T) { + testBatchSHRInvalidAddendum(t) +} + +// BenchmarkBatchSHRInvalidAddendum benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchSHRInvalidAddendum(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRInvalidAddendum(b) + } +} + +// testBatchSHRInvalidAddenda validates Addendum must be Addenda02 +func testBatchSHRInvalidAddenda(t testing.TB) { + mockBatch := NewBatchSHR(mockBatchSHRHeader()) + mockBatch.AddEntry(mockSHREntryDetail()) + addenda02 := mockAddenda02() + addenda02.recordType = "63" + mockBatch.GetEntries()[0].AddAddenda(addenda02) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRInvalidAddenda tests validating Addendum must be Addenda02 +func TestBatchSHRInvalidAddenda(t *testing.T) { + testBatchSHRInvalidAddenda(t) +} + +// BenchmarkBatchSHRInvalidAddenda benchmarks validating Addendum must be Addenda02 +func BenchmarkBatchSHRInvalidAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRInvalidAddenda(b) + } +} + +// ToDo: Using a FieldError may need to add a BatchError and use *BatchError + +// testBatchSHRInvalidBuild validates an invalid batch build +func testBatchSHRInvalidBuild(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRInvalidBuild tests validating an invalid batch build +func TestBatchSHRInvalidBuild(t *testing.T) { + testBatchSHRInvalidBuild(t) +} + +// BenchmarkBatchSHRInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchSHRInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRInvalidBuild(b) + } +} + +// testBatchSHRCardTransactionType validates BatchSHR create for an invalid CardTransactionType +func testBatchSHRCardTransactionType(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.GetEntries()[0].DiscretionaryData = "555" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CardTransactionType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchSHRCardTransactionType tests validating BatchSHR create for an invalid CardTransactionType +func TestBatchSHRCardTransactionType(t *testing.T) { + testBatchSHRCardTransactionType(t) +} + +// BenchmarkBatchSHRCardTransactionType benchmarks validating BatchSHR create for an invalid CardTransactionType +func BenchmarkBatchSHRCardTransactionType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRCardTransactionType(b) + } +} diff --git a/converters.go b/converters.go index dfd451fc2..8f7961744 100644 --- a/converters.go +++ b/converters.go @@ -66,6 +66,7 @@ func (c *converters) numericField(n int, max uint) string { return s } + // stringRTNField slices to max length and zero filled func (c *converters) stringRTNField(s string, max uint) string { ln := uint(len(s)) diff --git a/entryDetail.go b/entryDetail.go index 6d82726c5..5beb47190 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -328,6 +328,42 @@ func (ed *EntryDetail) POPTerminalStateField() string { return ed.parseStringField(ed.IdentificationNumber[13:15]) } +// SetSHRCardExpirationDate format MMYY is used in SHR, characters 1-4 of underlying +// IdentificationNumber +func (ed *EntryDetail) SetSHRCardExpirationDate(s string) { + ed.IdentificationNumber = ed.alphaField(s,4) +} + +// SetSHRDocumentReferenceNumber format int is used in SHR, characters 5-15 of underlying +// IdentificationNumber +func (ed *EntryDetail) SetSHRDocumentReferenceNumber(i int) { + ed.IdentificationNumber = ed.IdentificationNumber + ed.numericField(i, 11) +} + +// SetSHRIndividualCardAccountNumber format int is used in SHR, underlying +// IndividualName +func (ed *EntryDetail) SetSHRIndividualCardAccountNumber(i int) { + ed.IndividualName = ed.numericField(i, 22) +} + +// SHRCardExpirationDate format MMYY is used in SHR, characters 1-4 of underlying +// IdentificationNumber +func (ed *EntryDetail) SHRCardExpirationDateField() string { + return ed.parseStringField(ed.IdentificationNumber[0:4]) +} + +// SHRDocumentReferenceNumber format int is used in SHR, characters 5-15 of underlying +// IdentificationNumber +func (ed *EntryDetail) SHRDocumentReferenceNumberField() int { + return ed.parseNumField(ed.IdentificationNumber[4:15]) +} + +// SHRIndividualCardAccountNumber format int is used in SHR, underlying +// IndividualName +func (ed *EntryDetail) SHRIndividualCardAccountNumberField() int { + return ed.parseNumField(ed.IndividualName) +} + // IndividualNameField returns a space padded string of IndividualName func (ed *EntryDetail) IndividualNameField() string { return ed.alphaField(ed.IndividualName, 22) From 2acbebe5af701fdc4d75a87741be999efc552229 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 15:04:36 -0400 Subject: [PATCH 0225/1694] #207 gofmt # 207 gofmt --- converters.go | 1 - entryDetail.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/converters.go b/converters.go index 8f7961744..dfd451fc2 100644 --- a/converters.go +++ b/converters.go @@ -66,7 +66,6 @@ func (c *converters) numericField(n int, max uint) string { return s } - // stringRTNField slices to max length and zero filled func (c *converters) stringRTNField(s string, max uint) string { ln := uint(len(s)) diff --git a/entryDetail.go b/entryDetail.go index 5beb47190..2f2e25977 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -331,7 +331,7 @@ func (ed *EntryDetail) POPTerminalStateField() string { // SetSHRCardExpirationDate format MMYY is used in SHR, characters 1-4 of underlying // IdentificationNumber func (ed *EntryDetail) SetSHRCardExpirationDate(s string) { - ed.IdentificationNumber = ed.alphaField(s,4) + ed.IdentificationNumber = ed.alphaField(s, 4) } // SetSHRDocumentReferenceNumber format int is used in SHR, characters 5-15 of underlying From b5f4be187f6e8d33750311b15a3041d7231d177b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 15:13:16 -0400 Subject: [PATCH 0226/1694] #207 golint #207 golint --- entryDetail.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/entryDetail.go b/entryDetail.go index 2f2e25977..f4142d167 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -342,23 +342,24 @@ func (ed *EntryDetail) SetSHRDocumentReferenceNumber(i int) { // SetSHRIndividualCardAccountNumber format int is used in SHR, underlying // IndividualName +// TODO: Overflow for int func (ed *EntryDetail) SetSHRIndividualCardAccountNumber(i int) { ed.IndividualName = ed.numericField(i, 22) } -// SHRCardExpirationDate format MMYY is used in SHR, characters 1-4 of underlying +// SHRCardExpirationDateField format MMYY is used in SHR, characters 1-4 of underlying // IdentificationNumber func (ed *EntryDetail) SHRCardExpirationDateField() string { return ed.parseStringField(ed.IdentificationNumber[0:4]) } -// SHRDocumentReferenceNumber format int is used in SHR, characters 5-15 of underlying +// SHRDocumentReferenceNumberField format int is used in SHR, characters 5-15 of underlying // IdentificationNumber func (ed *EntryDetail) SHRDocumentReferenceNumberField() int { return ed.parseNumField(ed.IdentificationNumber[4:15]) } -// SHRIndividualCardAccountNumber format int is used in SHR, underlying +// SHRIndividualCardAccountNumberField format int is used in SHR, underlying // IndividualName func (ed *EntryDetail) SHRIndividualCardAccountNumberField() int { return ed.parseNumField(ed.IndividualName) From 5ab1c1d64a68656c180cdc61f3177b3a2240cadf Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 15:24:14 -0400 Subject: [PATCH 0227/1694] #207 test coverage updates SHR #207 test coverage for SHRCardExpirationDate --- batchSHR_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/batchSHR_test.go b/batchSHR_test.go index 67a397772..257c7dde0 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -435,3 +435,28 @@ func BenchmarkBatchSHRCardTransactionType(b *testing.B) { testBatchSHRCardTransactionType(b) } } + +// testBatchSHRCardExpirationDateField validates SHRCardExpirationDate +// characters 0-4 of underlying IdentificationNumber +func testBatchSHRCardExpirationDateField(t testing.TB) { + mockBatch := mockBatchSHR() + ts := mockBatch.Entries[0].SHRCardExpirationDateField() + if ts != "0718" { + t.Error("Card Expiration Date is invalid") + } +} + +// TestBatchSHRCardExpirationDateField tests validatingSHRCardExpirationDate +// characters 0-4 of underlying IdentificationNumber +func TestBatchSHRCardExpirationDateField(t *testing.T) { + testBatchSHRCardExpirationDateField(t) +} + +// BenchmarkBatchPOPTerminalStateField benchmarks validating SHRCardExpirationDate +// characters 0-4 of underlying IdentificationNumber +func BenchmarkBatchSHRCardExpirationDateField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRCardExpirationDateField(b) + } +} From 3034559e2fc6903a592772422c55cc8dd33d51c4 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 15:32:18 -0400 Subject: [PATCH 0228/1694] #207 Add test coverage SHRDocumentReferenceNumber #207 Add test coverage SHRDocumentReferenceNumber --- batchSHR_test.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/batchSHR_test.go b/batchSHR_test.go index 257c7dde0..a0fa519eb 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -4,7 +4,9 @@ package ach -import "testing" +import ( + "testing" +) // mockBatchSHRHeader creates a BatchSHR BatchHeader func mockBatchSHRHeader() *BatchHeader { @@ -460,3 +462,28 @@ func BenchmarkBatchSHRCardExpirationDateField(b *testing.B) { testBatchSHRCardExpirationDateField(b) } } + +// testBatchSHRDocumentReferenceNumberField validates SHRDocumentReferenceNumberField +// characters 5-15 of underlying IdentificationNumber +func testBatchSHRDocumentReferenceNumberField(t testing.TB) { + mockBatch := mockBatchSHR() + ts := mockBatch.Entries[0].SHRDocumentReferenceNumberField() + if ts != 12345678910 { + t.Error("Document Reference Number is invalid") + } +} + +// TestBatchSHRDocumentReferenceNumberFieldS tests validating SHRDocumentReferenceNumberField +// characters 5-15 of underlying IdentificationNumber +func TestBatchSHRDocumentReferenceNumberField(t *testing.T) { + testBatchSHRDocumentReferenceNumberField(t) +} + +// BenchmarkBatchPOPTerminalStateField benchmarks validating SHRDocumentReferenceNumberField +// characters 5-15 of underlying IdentificationNumber +func BenchmarkSHRDocumentReferenceNumberField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRDocumentReferenceNumberField(b) + } +} \ No newline at end of file From 1848327a0957017e8f008d8075ebe0b61c3486e0 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 24 May 2018 12:15:23 -0600 Subject: [PATCH 0229/1694] File CRUD functions and tests --- server/repositoryInMemory.go | 58 ++++++++++++++++++++++ server/service.go | 95 ++++++++++++++++++++++++++++++++++++ server/service_test.go | 79 ++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 server/repositoryInMemory.go create mode 100644 server/service.go create mode 100644 server/service_test.go diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go new file mode 100644 index 000000000..bb583c636 --- /dev/null +++ b/server/repositoryInMemory.go @@ -0,0 +1,58 @@ +package server + +// TODO: rename to InMemory and move into a repository directory when stable. +import ( + "sync" + + "github.com/moov-io/ach" +) + +type repositoryInMemory struct { + mtx sync.RWMutex + files map[string]*ach.File +} + +// NewRepositoryInMemory is an in memory ach storage repository for files +func NewRepositoryInMemory() Repository { + f := map[string]*ach.File{} + return &repositoryInMemory{ + files: f, + } +} +func (r *repositoryInMemory) StoreFile(f *ach.File) error { + r.mtx.Lock() + defer r.mtx.Unlock() + if _, ok := r.files[f.ID]; ok { + return ErrAlreadyExists + } + r.files[f.ID] = f + return nil +} + +// FindFile retrieves a ach.File based on the supplied ID +func (r *repositoryInMemory) FindFile(id string) (*ach.File, error) { + r.mtx.RLock() + defer r.mtx.RUnlock() + if val, ok := r.files[id]; ok { + return val, nil + } + return nil, ErrNotFound +} + +// FindAllFiles returns all files that have been saved in memory +func (r *repositoryInMemory) FindAllFiles() []*ach.File { + r.mtx.RLock() + defer r.mtx.RUnlock() + files := make([]*ach.File, 0, len(r.files)) + for _, val := range r.files { + files = append(files, val) + } + return files +} + +func (r *repositoryInMemory) DeleteFile(id string) error { + r.mtx.RLock() + defer r.mtx.RUnlock() + delete(r.files, id) + return nil +} diff --git a/server/service.go b/server/service.go new file mode 100644 index 000000000..c316d0375 --- /dev/null +++ b/server/service.go @@ -0,0 +1,95 @@ +package server + +import ( + "errors" + "strings" + + "github.com/moov-io/ach" + uuid "github.com/satori/go.uuid" +) + +var ( + ErrNotFound = errors.New("Not Found") + ErrAlreadyExists = errors.New("Already Exists") +) + +// Service is a REST interface for interacting with ACH file structures +// TODO: Add ctx to function paramaters to pass the client security token +type Service interface { + // CreateFile creates a new ach file record and returns a resource ID + CreateFile(f ach.File) (string, error) + // AddFile retrieves a file based on the File id + GetFile(id string) (ach.File, error) + // GetFiles retrieves all files accessable from the client. + GetFiles() []ach.File + // DeleteFile takes a file resource ID and deletes it from the repository + DeleteFile(id string) error + // UpdateFile updates the changes properties of a matching File ID + // UpdateFile(f ach.File) (string, error) +} + +// service a concrete implementation of the service. +type service struct { + repository Repository +} + +// NewService creates a new concrete service +func NewService(r Repository) Service { + return &service{ + repository: r, + } +} + +// CreateFile add a file to storage +func (s *service) CreateFile(f ach.File) (string, error) { + if f.ID == "" { + f.ID = NextID() + } + if err := s.repository.StoreFile(&f); err != nil { + return "", err + } + return f.ID, nil +} + +// GetFile returns a files based on the supplied id +func (s *service) GetFile(id string) (ach.File, error) { + f, err := s.repository.FindFile(id) + if err != nil { + return ach.File{}, ErrNotFound + } + return *f, nil +} + +func (s *service) GetFiles() []ach.File { + var result []ach.File + for _, f := range s.repository.FindAllFiles() { + result = append(result, *f) + } + return result +} + +func (s *service) DeleteFile(id string) error { + return s.repository.DeleteFile(id) +} + +// Repository concrete implementations +// ******** + +// Repository is the Service storage mechanism abstraction +type Repository interface { + StoreFile(file *ach.File) error + FindFile(id string) (*ach.File, error) + FindAllFiles() []*ach.File + DeleteFile(id string) error +} + +// Utility Functions +// ***** + +// NextID generates a new resource ID +func NextID() string { + id, _ := uuid.NewV4() + //return id.String() + // make it shorter for testing URL + return string(strings.Split(strings.ToUpper(id.String()), "-")[0]) +} diff --git a/server/service_test.go b/server/service_test.go new file mode 100644 index 000000000..23fb333dd --- /dev/null +++ b/server/service_test.go @@ -0,0 +1,79 @@ +package server + +import ( + "testing" + + "github.com/moov-io/ach" +) + +func mockServiceInMemory() Service { + repository := NewRepositoryInMemory() + repository.StoreFile(&ach.File{ID: "98765"}) + return NewService(repository) +} + +// CreateFile tests +func TestCreateFile(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateFile(ach.File{ID: "12345"}) + if id != "12345" { + t.Errorf("expected %s received %s w/ error %s", "12345", id, err) + } +} +func TestCreateFileIDExists(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateFile(ach.File{ID: "98765"}) + if err != ErrAlreadyExists { + t.Errorf("expected %s received %s w/ error %s", "ErrAlreadyExists", id, err) + } +} + +func TestCreateFileNoID(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateFile(*ach.NewFile()) + if len(id) < 3 { + t.Errorf("expected %s received %s w/ error %s", "NextID", id, err) + } +} + +// Service.GetFile tests + +func TestGetFile(t *testing.T) { + s := mockServiceInMemory() + f, err := s.GetFile("98765") + if err != nil { + t.Errorf("expected %s received %s w/ error %s", "98765", f.ID, err) + } +} + +func TestGetFileNotFound(t *testing.T) { + s := mockServiceInMemory() + f, err := s.GetFile("12345") + if err != ErrNotFound { + t.Errorf("expected %s received %s w/ error %s", "ErrNotFound", f.ID, err) + } +} + +// Service.GetFiles tests + +func TestGetFiles(t *testing.T) { + s := mockServiceInMemory() + files := s.GetFiles() + if len(files) != 1 { + t.Errorf("expected %s received %v", "1", len(files)) + } +} + +// Service.DeleteFile tests + +func TestDeleteFile(t *testing.T) { + s := mockServiceInMemory() + err := s.DeleteFile("98765") + if err != nil { + t.Errorf("expected %s received %s", "nil", err) + } + _, err = s.GetFile("98765") + if err != ErrNotFound { + t.Errorf("expected %s received %s", "ErrNotFound", err) + } +} From 5ffc6e94d4ef05e3f6864924da2cd68e1351a05c Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Fri, 25 May 2018 12:30:55 -0600 Subject: [PATCH 0230/1694] Basic CRUD File endpoints --- cmd/server/main.go | 67 +++++++++++++++++ file.go | 1 + server/endpoints.go | 97 +++++++++++++++++++++++++ server/middlewear.go | 53 ++++++++++++++ server/service.go | 4 +- server/transport.go | 169 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 cmd/server/main.go create mode 100644 server/endpoints.go create mode 100644 server/middlewear.go create mode 100644 server/transport.go diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 000000000..0a6124f37 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "flag" + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/wadearnold/ach/server" + "github.com/wadearnold/kit/log" +) + +/** +CreateFile +curl -d '{"id":"1234"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/ + +GetFile +curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/1234 + +GetFiles +curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/ + +DeleteFile +curl -H "Content-Type: application/json" -X DELETE http://localhost:8080/files/1234 +**/ + +func main() { + var ( + httpAddr = flag.String("http.addr", ":8080", "HTTP listen address") + ) + flag.Parse() + + var logger log.Logger + { + logger = log.NewLogfmtLogger(os.Stderr) + logger = log.With(logger, "ts", log.DefaultTimestampUTC) + logger = log.With(logger, "caller", log.DefaultCaller) + } + + var s server.Service + { + s = server.NewService(server.NewRepositoryInMemory()) + s = server.LoggingMiddleware(logger)(s) + } + + var h http.Handler + { + h = server.MakeHTTPHandler(s, log.With(logger, "component", "HTTP")) + } + + // Listen for application termination. + errs := make(chan error) + go func() { + c := make(chan os.Signal) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + errs <- fmt.Errorf("%s", <-c) + }() + + go func() { + logger.Log("transport", "HTTP", "addr", *httpAddr) + errs <- http.ListenAndServe(*httpAddr, h) + }() + + logger.Log("exit", <-errs) +} diff --git a/file.go b/file.go index 23c4ad30c..883be1d04 100644 --- a/file.go +++ b/file.go @@ -53,6 +53,7 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { + // ID is a client defined string used as a reference to this record. ID string `json:"id"` Header FileHeader `json:"fileHeader"` Batches []Batcher `json:"batches"` diff --git a/server/endpoints.go b/server/endpoints.go new file mode 100644 index 000000000..cbe80c5d9 --- /dev/null +++ b/server/endpoints.go @@ -0,0 +1,97 @@ +package server + +import ( + "context" + + "github.com/go-kit/kit/endpoint" + "github.com/moov-io/ach" +) + +type Endpoints struct { + CreateFileEndpoint endpoint.Endpoint + GetFileEndpoint endpoint.Endpoint + GetFilesEndpoint endpoint.Endpoint + DeleteFileEndpoint endpoint.Endpoint +} + +func MakeServerEndpoints(s Service) Endpoints { + return Endpoints{ + CreateFileEndpoint: MakeCreateFileEndpoint(s), + GetFileEndpoint: MakeGetFileEndpoint(s), + GetFilesEndpoint: MakeGetFilesEndpoint(s), + DeleteFileEndpoint: MakeDeleteFileEndpoint(s), + } +} + +// MakeCreateFileEndpoint returns an endpoint via the passed service. +func MakeCreateFileEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(createFileRequest) + id, e := s.CreateFile(req.File) + return createFileResponse{ID: id, Err: e}, nil + } +} + +type createFileRequest struct { + File ach.File +} + +type createFileResponse struct { + ID string `json:"id,omitempty"` + Err error `json:"err,omitempty"` +} + +func (r createFileResponse) error() error { return r.Err } + +func MakeGetFilesEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + _ = request.(getFilesRequest) + return getFilesResponse{Files: s.GetFiles(), Err: nil}, nil + } +} + +type getFilesRequest struct{} + +type getFilesResponse struct { + Files []ach.File `json:"files,omitempty"` + Err error `json:"error,omitempty"` +} + +// MakeGetFileEndpoint returns an endpoint via the passed service. +// Primarily useful in a server. +func MakeGetFileEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(getFileRequest) + f, e := s.GetFile(req.ID) + return getFileResponse{File: f, Err: e}, nil + } +} + +type getFileRequest struct { + ID string `json:"id,omitempty"` +} + +type getFileResponse struct { + File ach.File `json:"file,omitempty"` + Err error `json:"err,omitempty"` +} + +func (r getFileResponse) error() error { return r.Err } + +func MakeDeleteFileEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(deleteFileRequest) + e := s.DeleteFile(req.ID) + return deleteFileResponse{Err: e}, nil + } +} + +type deleteFileRequest struct { + ID string `json:"id,omitempty"` +} + +type deleteFileResponse struct { + Err error `json:"err,omitempty"` +} + +func (r deleteFileResponse) error() error { return r.Err } diff --git a/server/middlewear.go b/server/middlewear.go new file mode 100644 index 000000000..2d2e6ac14 --- /dev/null +++ b/server/middlewear.go @@ -0,0 +1,53 @@ +package server + +import ( + "time" + + "github.com/go-kit/kit/log" + "github.com/moov-io/ach" +) + +// Middleware describes a service (as opposed to endpoint) middleware. +type Middleware func(Service) Service + +func LoggingMiddleware(logger log.Logger) Middleware { + return func(next Service) Service { + return &loggingMiddleware{ + next: next, + logger: logger, + } + } +} + +type loggingMiddleware struct { + next Service + logger log.Logger +} + +func (mw loggingMiddleware) CreateFile(f ach.File) (id string, err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "CreateFile", "id", f.ID, "took", time.Since(begin), "err", err) + }(time.Now()) + return mw.next.CreateFile(f) +} + +func (mw loggingMiddleware) GetFile(id string) (f ach.File, err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "GetFile", "id", id, "took", time.Since(begin), "err", err) + }(time.Now()) + return mw.next.GetFile(id) +} + +func (mw loggingMiddleware) GetFiles() []ach.File { + defer func(begin time.Time) { + mw.logger.Log("method", "GetFiles", "took", time.Since(begin)) + }(time.Now()) + return mw.next.GetFiles() +} + +func (mw loggingMiddleware) DeleteFile(id string) (err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "DeleteFile", "id", id, "took", time.Since(begin)) + }(time.Now()) + return mw.next.DeleteFile(id) +} diff --git a/server/service.go b/server/service.go index c316d0375..39ba9f026 100644 --- a/server/service.go +++ b/server/service.go @@ -14,13 +14,13 @@ var ( ) // Service is a REST interface for interacting with ACH file structures -// TODO: Add ctx to function paramaters to pass the client security token +// TODO: Add ctx to function parameters to pass the client security token type Service interface { // CreateFile creates a new ach file record and returns a resource ID CreateFile(f ach.File) (string, error) // AddFile retrieves a file based on the File id GetFile(id string) (ach.File, error) - // GetFiles retrieves all files accessable from the client. + // GetFiles retrieves all files accessible from the client. GetFiles() []ach.File // DeleteFile takes a file resource ID and deletes it from the repository DeleteFile(id string) error diff --git a/server/transport.go b/server/transport.go new file mode 100644 index 000000000..501ed88d8 --- /dev/null +++ b/server/transport.go @@ -0,0 +1,169 @@ +package server + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "io/ioutil" + + "net/http" + "net/url" + + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" + "github.com/gorilla/mux" + + "github.com/moov-io/ach" +) + +var ( + // ErrBadRouting is returned when an expected path variable is missing. + // It always indicates programmer error. + ErrBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)") +) + +func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { + r := mux.NewRouter() + e := MakeServerEndpoints(s) + options := []httptransport.ServerOption{ + httptransport.ServerErrorLogger(logger), + httptransport.ServerErrorEncoder(encodeError), + } + + // POST /files/ Creates a file + // GET /files/ retrieves a list of all file's + // GET /files/:id retrieves the given file by id + // DELETE /files/:id delete a file based on supplied id + // *** + // GET /files/:id/validate validates the supplied file id for nacha compliance + // PATCH /files/:id/build build batch and file controls in ach file with supplied values + // PATCH /files/upload/ upload a ach file + + r.Methods("POST").Path("/files/").Handler(httptransport.NewServer( + e.CreateFileEndpoint, + decodeCreateFileRequest, + encodeResponse, + options..., + )) + r.Methods("GET").Path("/files/").Handler(httptransport.NewServer( + e.GetFilesEndpoint, + decodeGetFilesRequest, + encodeResponse, + options..., + )) + r.Methods("GET").Path("/files/{id}").Handler(httptransport.NewServer( + e.GetFileEndpoint, + decodeGetFileRequest, + encodeResponse, + options..., + )) + r.Methods("DELETE").Path("/files/{id}").Handler(httptransport.NewServer( + e.DeleteFileEndpoint, + decodeDeleteFileRequest, + encodeResponse, + options..., + )) + return r +} + +func decodeCreateFileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + var req createFileRequest + // Sets default values + req.File = *ach.NewFile() + if e := json.NewDecoder(r.Body).Decode(&req.File); e != nil { + return nil, e + } + return req, nil +} + +func decodeGetFileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + vars := mux.Vars(r) + id, ok := vars["id"] + if !ok { + return nil, ErrBadRouting + } + return getFileRequest{ID: id}, nil +} + +func decodeDeleteFileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + vars := mux.Vars(r) + id, ok := vars["id"] + if !ok { + return nil, ErrBadRouting + } + return deleteFileRequest{ID: id}, nil +} + +func encodeCreateFileRequest(ctx context.Context, req *http.Request, request interface{}) error { + req.Method, req.URL.Path = "POST", "/files/" + return encodeRequest(ctx, req, request) +} +func decodeGetFilesRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + return getFilesRequest{}, nil +} + +func encodeGetFileRequest(ctx context.Context, req *http.Request, request interface{}) error { + r := request.(getFileRequest) + fileID := url.QueryEscape(r.ID) + req.Method, req.URL.Path = "GET", "/files/"+fileID + return encodeRequest(ctx, req, request) +} + +// errorer is implemented by all concrete response types that may contain +// errors. It allows us to change the HTTP response code without needing to +// trigger an endpoint (transport-level) error. For more information, read the +// big comment in endpoints.go. +type errorer interface { + error() error +} + +// encodeResponse is the common method to encode all response types to the +// client. I chose to do it this way because, since we're using JSON, there's no +// reason to provide anything more specific. It's certainly possible to +// specialize on a per-response (per-method) basis. +func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { + if e, ok := response.(errorer); ok && e.error() != nil { + // Not a Go kit transport error, but a business-logic error. + // Provide those as HTTP errors. + encodeError(ctx, e.error(), w) + return nil + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + return json.NewEncoder(w).Encode(response) +} + +// encodeRequest likewise JSON-encodes the request to the HTTP request body. +// Don't use it directly as a transport/http.Client EncodeRequestFunc: +// Service endpoints require mutating the HTTP method and request path. +func encodeRequest(_ context.Context, req *http.Request, request interface{}) error { + var buf bytes.Buffer + err := json.NewEncoder(&buf).Encode(request) + if err != nil { + return err + } + req.Body = ioutil.NopCloser(&buf) + return nil +} + +func encodeError(_ context.Context, err error, w http.ResponseWriter) { + if err == nil { + panic("encodeError with nil error") + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(codeFrom(err)) + json.NewEncoder(w).Encode(map[string]interface{}{ + "error": err.Error(), + }) +} + +func codeFrom(err error) int { + switch err { + case ErrNotFound: + return http.StatusNotFound + case ErrAlreadyExists: + return http.StatusBadRequest + default: + return http.StatusInternalServerError + } +} From 42e6856399349bf8aad4c7fc1a8b31b1caf36ad1 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 13:44:44 -0600 Subject: [PATCH 0231/1694] Remove code coverage output files --- cover.out | 853 ---------- coverage.html | 4528 ------------------------------------------------- 2 files changed, 5381 deletions(-) delete mode 100644 cover.out delete mode 100644 coverage.html diff --git a/cover.out b/cover.out deleted file mode 100644 index 586f25205..000000000 --- a/cover.out +++ /dev/null @@ -1,853 +0,0 @@ -mode: set -github.com\bkmoovio\ach\batchRCK.go:18.45,23.2 4 1 -github.com\bkmoovio\ach\batchRCK.go:26.41,28.39 1 1 -github.com\bkmoovio\ach\batchRCK.go:33.2,33.48 1 1 -github.com\bkmoovio\ach\batchRCK.go:38.2,38.50 1 1 -github.com\bkmoovio\ach\batchRCK.go:44.2,44.39 1 1 -github.com\bkmoovio\ach\batchRCK.go:51.2,51.58 1 1 -github.com\bkmoovio\ach\batchRCK.go:56.2,56.38 1 1 -github.com\bkmoovio\ach\batchRCK.go:75.2,75.12 1 1 -github.com\bkmoovio\ach\batchRCK.go:28.39,30.3 1 0 -github.com\bkmoovio\ach\batchRCK.go:33.48,35.3 1 1 -github.com\bkmoovio\ach\batchRCK.go:38.50,41.3 2 1 -github.com\bkmoovio\ach\batchRCK.go:45.21,47.101 2 1 -github.com\bkmoovio\ach\batchRCK.go:51.58,54.3 2 1 -github.com\bkmoovio\ach\batchRCK.go:56.38,58.35 1 1 -github.com\bkmoovio\ach\batchRCK.go:64.3,64.28 1 1 -github.com\bkmoovio\ach\batchRCK.go:70.3,70.39 1 1 -github.com\bkmoovio\ach\batchRCK.go:58.35,61.4 2 1 -github.com\bkmoovio\ach\batchRCK.go:64.28,67.4 2 1 -github.com\bkmoovio\ach\batchRCK.go:70.39,73.4 2 1 -github.com\bkmoovio\ach\batchRCK.go:79.39,81.38 1 1 -github.com\bkmoovio\ach\batchRCK.go:87.2,87.25 1 1 -github.com\bkmoovio\ach\batchRCK.go:81.38,83.3 1 1 -github.com\bkmoovio\ach\batchTEL.go:17.45,22.2 4 1 -github.com\bkmoovio\ach\batchTEL.go:25.41,27.39 1 1 -github.com\bkmoovio\ach\batchTEL.go:32.2,32.48 1 1 -github.com\bkmoovio\ach\batchTEL.go:37.2,37.50 1 1 -github.com\bkmoovio\ach\batchTEL.go:42.2,42.38 1 1 -github.com\bkmoovio\ach\batchTEL.go:49.2,49.34 1 1 -github.com\bkmoovio\ach\batchTEL.go:27.39,29.3 1 1 -github.com\bkmoovio\ach\batchTEL.go:32.48,34.3 1 1 -github.com\bkmoovio\ach\batchTEL.go:37.50,40.3 2 1 -github.com\bkmoovio\ach\batchTEL.go:42.38,43.35 1 1 -github.com\bkmoovio\ach\batchTEL.go:43.35,46.4 2 1 -github.com\bkmoovio\ach\batchTEL.go:53.39,55.38 1 1 -github.com\bkmoovio\ach\batchTEL.go:59.2,59.25 1 1 -github.com\bkmoovio\ach\batchTEL.go:55.38,57.3 1 1 -github.com\bkmoovio\ach\batcher.go:39.37,41.2 1 1 -github.com\bkmoovio\ach\converters.go:16.54,19.2 2 1 -github.com\bkmoovio\ach\converters.go:21.60,24.2 2 1 -github.com\bkmoovio\ach\converters.go:27.59,29.2 1 1 -github.com\bkmoovio\ach\converters.go:32.58,35.2 2 1 -github.com\bkmoovio\ach\converters.go:38.59,40.2 1 1 -github.com\bkmoovio\ach\converters.go:43.58,46.2 2 1 -github.com\bkmoovio\ach\converters.go:49.60,51.14 2 1 -github.com\bkmoovio\ach\converters.go:54.2,55.10 2 1 -github.com\bkmoovio\ach\converters.go:51.14,53.3 1 1 -github.com\bkmoovio\ach\converters.go:59.59,62.14 3 1 -github.com\bkmoovio\ach\converters.go:65.2,66.10 2 1 -github.com\bkmoovio\ach\converters.go:62.14,64.3 1 1 -github.com\bkmoovio\ach\converters.go:70.64,72.14 2 1 -github.com\bkmoovio\ach\converters.go:75.2,76.10 2 1 -github.com\bkmoovio\ach\converters.go:72.14,74.3 1 1 -github.com\bkmoovio\ach\addenda05.go:39.32,44.2 4 1 -github.com\bkmoovio\ach\addenda05.go:47.50,59.2 5 1 -github.com\bkmoovio\ach\addenda05.go:62.45,69.2 1 1 -github.com\bkmoovio\ach\addenda05.go:73.46,74.51 1 1 -github.com\bkmoovio\ach\addenda05.go:77.2,77.33 1 1 -github.com\bkmoovio\ach\addenda05.go:81.2,81.65 1 1 -github.com\bkmoovio\ach\addenda05.go:84.2,84.86 1 1 -github.com\bkmoovio\ach\addenda05.go:88.2,88.12 1 1 -github.com\bkmoovio\ach\addenda05.go:74.51,76.3 1 1 -github.com\bkmoovio\ach\addenda05.go:77.33,80.3 2 1 -github.com\bkmoovio\ach\addenda05.go:81.65,83.3 1 1 -github.com\bkmoovio\ach\addenda05.go:84.86,86.3 1 1 -github.com\bkmoovio\ach\addenda05.go:93.52,94.32 1 1 -github.com\bkmoovio\ach\addenda05.go:97.2,97.30 1 1 -github.com\bkmoovio\ach\addenda05.go:100.2,100.35 1 1 -github.com\bkmoovio\ach\addenda05.go:103.2,103.46 1 1 -github.com\bkmoovio\ach\addenda05.go:106.2,106.12 1 1 -github.com\bkmoovio\ach\addenda05.go:94.32,96.3 1 1 -github.com\bkmoovio\ach\addenda05.go:97.30,99.3 1 1 -github.com\bkmoovio\ach\addenda05.go:100.35,102.3 1 1 -github.com\bkmoovio\ach\addenda05.go:103.46,105.3 1 1 -github.com\bkmoovio\ach\addenda05.go:110.69,112.2 1 1 -github.com\bkmoovio\ach\addenda05.go:115.58,117.2 1 1 -github.com\bkmoovio\ach\addenda05.go:120.69,122.2 1 1 -github.com\bkmoovio\ach\addenda05.go:125.47,127.2 1 1 -github.com\bkmoovio\ach\batchARC.go:27.45,32.2 4 1 -github.com\bkmoovio\ach\batchARC.go:35.41,37.39 1 1 -github.com\bkmoovio\ach\batchARC.go:43.2,43.48 1 1 -github.com\bkmoovio\ach\batchARC.go:48.2,48.50 1 1 -github.com\bkmoovio\ach\batchARC.go:54.2,54.39 1 1 -github.com\bkmoovio\ach\batchARC.go:60.2,60.38 1 1 -github.com\bkmoovio\ach\batchARC.go:79.2,79.12 1 1 -github.com\bkmoovio\ach\batchARC.go:37.39,39.3 1 1 -github.com\bkmoovio\ach\batchARC.go:43.48,45.3 1 1 -github.com\bkmoovio\ach\batchARC.go:48.50,51.3 2 1 -github.com\bkmoovio\ach\batchARC.go:55.21,57.101 2 1 -github.com\bkmoovio\ach\batchARC.go:60.38,62.35 1 1 -github.com\bkmoovio\ach\batchARC.go:68.3,68.29 1 1 -github.com\bkmoovio\ach\batchARC.go:74.3,74.39 1 1 -github.com\bkmoovio\ach\batchARC.go:62.35,65.4 2 1 -github.com\bkmoovio\ach\batchARC.go:68.29,71.4 2 1 -github.com\bkmoovio\ach\batchARC.go:74.39,77.4 2 1 -github.com\bkmoovio\ach\batchARC.go:83.39,85.38 1 1 -github.com\bkmoovio\ach\batchARC.go:91.2,91.25 1 1 -github.com\bkmoovio\ach\batchARC.go:85.38,87.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:110.36,117.2 2 1 -github.com\bkmoovio\ach\batchHeader.go:120.45,155.2 13 1 -github.com\bkmoovio\ach\batchHeader.go:158.40,174.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:178.41,179.44 1 1 -github.com\bkmoovio\ach\batchHeader.go:182.2,182.26 1 1 -github.com\bkmoovio\ach\batchHeader.go:186.2,186.63 1 1 -github.com\bkmoovio\ach\batchHeader.go:189.2,189.64 1 1 -github.com\bkmoovio\ach\batchHeader.go:192.2,192.75 1 1 -github.com\bkmoovio\ach\batchHeader.go:195.2,195.58 1 1 -github.com\bkmoovio\ach\batchHeader.go:198.2,198.71 1 1 -github.com\bkmoovio\ach\batchHeader.go:201.2,201.68 1 1 -github.com\bkmoovio\ach\batchHeader.go:204.2,204.70 1 1 -github.com\bkmoovio\ach\batchHeader.go:207.2,207.12 1 1 -github.com\bkmoovio\ach\batchHeader.go:179.44,181.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:182.26,185.3 2 1 -github.com\bkmoovio\ach\batchHeader.go:186.63,188.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:189.64,191.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:192.75,194.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:195.58,197.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:198.71,200.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:201.68,203.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:204.70,206.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:212.47,213.25 1 1 -github.com\bkmoovio\ach\batchHeader.go:216.2,216.30 1 1 -github.com\bkmoovio\ach\batchHeader.go:219.2,219.26 1 1 -github.com\bkmoovio\ach\batchHeader.go:222.2,222.36 1 1 -github.com\bkmoovio\ach\batchHeader.go:225.2,225.37 1 1 -github.com\bkmoovio\ach\batchHeader.go:228.2,228.38 1 1 -github.com\bkmoovio\ach\batchHeader.go:231.2,231.33 1 1 -github.com\bkmoovio\ach\batchHeader.go:234.2,234.12 1 1 -github.com\bkmoovio\ach\batchHeader.go:213.25,215.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:216.30,218.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:219.26,221.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:222.36,224.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:225.37,227.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:228.38,230.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:231.33,233.3 1 1 -github.com\bkmoovio\ach\batchHeader.go:238.50,240.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:243.63,245.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:248.60,250.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:253.62,255.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:258.61,260.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:263.57,265.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:268.57,270.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:273.50,275.2 1 1 -github.com\bkmoovio\ach\batchHeader.go:277.53,279.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:96.33,106.2 2 1 -github.com\bkmoovio\ach\fileHeader.go:109.44,137.2 13 1 -github.com\bkmoovio\ach\fileHeader.go:140.39,157.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:161.40,163.44 1 1 -github.com\bkmoovio\ach\fileHeader.go:166.2,166.26 1 1 -github.com\bkmoovio\ach\fileHeader.go:170.2,170.66 1 1 -github.com\bkmoovio\ach\fileHeader.go:173.2,173.33 1 1 -github.com\bkmoovio\ach\fileHeader.go:177.2,177.28 1 1 -github.com\bkmoovio\ach\fileHeader.go:180.2,180.31 1 1 -github.com\bkmoovio\ach\fileHeader.go:183.2,183.26 1 1 -github.com\bkmoovio\ach\fileHeader.go:186.2,186.71 1 1 -github.com\bkmoovio\ach\fileHeader.go:189.2,189.39 1 1 -github.com\bkmoovio\ach\fileHeader.go:192.2,192.44 1 1 -github.com\bkmoovio\ach\fileHeader.go:195.2,195.66 1 1 -github.com\bkmoovio\ach\fileHeader.go:198.2,198.60 1 1 -github.com\bkmoovio\ach\fileHeader.go:208.2,208.12 1 1 -github.com\bkmoovio\ach\fileHeader.go:163.44,165.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:166.26,169.3 2 1 -github.com\bkmoovio\ach\fileHeader.go:170.66,172.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:173.33,176.3 2 1 -github.com\bkmoovio\ach\fileHeader.go:177.28,179.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:180.31,182.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:183.26,185.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:186.71,188.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:189.39,191.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:192.44,194.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:195.66,197.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:198.60,200.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:213.46,214.25 1 1 -github.com\bkmoovio\ach\fileHeader.go:217.2,217.35 1 1 -github.com\bkmoovio\ach\fileHeader.go:220.2,220.30 1 1 -github.com\bkmoovio\ach\fileHeader.go:223.2,223.34 1 1 -github.com\bkmoovio\ach\fileHeader.go:226.2,226.29 1 1 -github.com\bkmoovio\ach\fileHeader.go:229.2,229.25 1 1 -github.com\bkmoovio\ach\fileHeader.go:232.2,232.29 1 1 -github.com\bkmoovio\ach\fileHeader.go:235.2,235.25 1 1 -github.com\bkmoovio\ach\fileHeader.go:238.2,238.12 1 1 -github.com\bkmoovio\ach\fileHeader.go:214.25,216.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:217.35,219.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:220.30,222.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:223.34,225.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:226.29,228.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:229.25,231.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:232.29,234.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:235.25,237.3 1 1 -github.com\bkmoovio\ach\fileHeader.go:242.58,244.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:247.53,249.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:252.54,254.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:257.54,259.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:262.62,264.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:267.57,269.2 1 1 -github.com\bkmoovio\ach\fileHeader.go:272.51,274.2 1 1 -github.com\bkmoovio\ach\writer.go:24.37,28.2 1 1 -github.com\bkmoovio\ach\writer.go:31.42,32.40 1 1 -github.com\bkmoovio\ach\writer.go:36.2,38.72 2 1 -github.com\bkmoovio\ach\writer.go:41.2,43.37 2 1 -github.com\bkmoovio\ach\writer.go:65.2,65.73 1 1 -github.com\bkmoovio\ach\writer.go:68.2,71.64 2 1 -github.com\bkmoovio\ach\writer.go:77.2,77.12 1 1 -github.com\bkmoovio\ach\writer.go:32.40,34.3 1 0 -github.com\bkmoovio\ach\writer.go:38.72,40.3 1 0 -github.com\bkmoovio\ach\writer.go:43.37,44.79 1 1 -github.com\bkmoovio\ach\writer.go:47.3,48.44 2 1 -github.com\bkmoovio\ach\writer.go:60.3,60.80 1 1 -github.com\bkmoovio\ach\writer.go:63.3,63.14 1 1 -github.com\bkmoovio\ach\writer.go:44.79,46.4 1 0 -github.com\bkmoovio\ach\writer.go:48.44,49.68 1 1 -github.com\bkmoovio\ach\writer.go:52.4,53.43 2 1 -github.com\bkmoovio\ach\writer.go:49.68,51.5 1 0 -github.com\bkmoovio\ach\writer.go:53.43,54.71 1 1 -github.com\bkmoovio\ach\writer.go:57.5,57.16 1 1 -github.com\bkmoovio\ach\writer.go:54.71,56.6 1 0 -github.com\bkmoovio\ach\writer.go:60.80,62.4 1 0 -github.com\bkmoovio\ach\writer.go:65.73,67.3 1 0 -github.com\bkmoovio\ach\writer.go:71.64,72.76 1 1 -github.com\bkmoovio\ach\writer.go:72.76,74.4 1 0 -github.com\bkmoovio\ach\writer.go:82.26,84.2 1 1 -github.com\bkmoovio\ach\writer.go:87.32,90.2 2 0 -github.com\bkmoovio\ach\writer.go:93.48,94.29 1 1 -github.com\bkmoovio\ach\writer.go:102.2,102.20 1 1 -github.com\bkmoovio\ach\writer.go:94.29,98.17 2 1 -github.com\bkmoovio\ach\writer.go:98.17,100.4 1 0 -github.com\bkmoovio\ach\batchBOC.go:32.45,37.2 4 1 -github.com\bkmoovio\ach\batchBOC.go:40.41,42.39 1 1 -github.com\bkmoovio\ach\batchBOC.go:48.2,48.48 1 1 -github.com\bkmoovio\ach\batchBOC.go:53.2,53.50 1 1 -github.com\bkmoovio\ach\batchBOC.go:59.2,59.39 1 1 -github.com\bkmoovio\ach\batchBOC.go:65.2,65.38 1 1 -github.com\bkmoovio\ach\batchBOC.go:84.2,84.12 1 1 -github.com\bkmoovio\ach\batchBOC.go:42.39,44.3 1 1 -github.com\bkmoovio\ach\batchBOC.go:48.48,50.3 1 1 -github.com\bkmoovio\ach\batchBOC.go:53.50,56.3 2 1 -github.com\bkmoovio\ach\batchBOC.go:60.21,62.101 2 1 -github.com\bkmoovio\ach\batchBOC.go:65.38,67.35 1 1 -github.com\bkmoovio\ach\batchBOC.go:73.3,73.29 1 1 -github.com\bkmoovio\ach\batchBOC.go:79.3,79.39 1 1 -github.com\bkmoovio\ach\batchBOC.go:67.35,70.4 2 1 -github.com\bkmoovio\ach\batchBOC.go:73.29,76.4 2 1 -github.com\bkmoovio\ach\batchBOC.go:79.39,82.4 2 1 -github.com\bkmoovio\ach\batchBOC.go:88.39,90.38 1 1 -github.com\bkmoovio\ach\batchBOC.go:96.2,96.25 1 1 -github.com\bkmoovio\ach\batchBOC.go:90.38,92.3 1 1 -github.com\bkmoovio\ach\batchCCD.go:19.45,24.2 4 1 -github.com\bkmoovio\ach\batchCCD.go:27.41,29.39 1 1 -github.com\bkmoovio\ach\batchCCD.go:34.2,34.48 1 1 -github.com\bkmoovio\ach\batchCCD.go:37.2,37.47 1 1 -github.com\bkmoovio\ach\batchCCD.go:42.2,42.50 1 1 -github.com\bkmoovio\ach\batchCCD.go:47.2,47.12 1 1 -github.com\bkmoovio\ach\batchCCD.go:29.39,31.3 1 1 -github.com\bkmoovio\ach\batchCCD.go:34.48,36.3 1 1 -github.com\bkmoovio\ach\batchCCD.go:37.47,39.3 1 1 -github.com\bkmoovio\ach\batchCCD.go:42.50,45.3 2 1 -github.com\bkmoovio\ach\batchCCD.go:51.39,53.38 1 1 -github.com\bkmoovio\ach\batchCCD.go:57.2,57.25 1 1 -github.com\bkmoovio\ach\batchCCD.go:53.38,55.3 1 1 -github.com\bkmoovio\ach\batchPPD.go:13.45,18.2 4 1 -github.com\bkmoovio\ach\batchPPD.go:21.41,23.39 1 1 -github.com\bkmoovio\ach\batchPPD.go:29.2,29.48 1 1 -github.com\bkmoovio\ach\batchPPD.go:32.2,32.47 1 1 -github.com\bkmoovio\ach\batchPPD.go:38.2,38.12 1 1 -github.com\bkmoovio\ach\batchPPD.go:23.39,25.3 1 1 -github.com\bkmoovio\ach\batchPPD.go:29.48,31.3 1 0 -github.com\bkmoovio\ach\batchPPD.go:32.47,34.3 1 1 -github.com\bkmoovio\ach\batchPPD.go:42.39,44.38 1 1 -github.com\bkmoovio\ach\batchPPD.go:50.2,50.25 1 1 -github.com\bkmoovio\ach\batchPPD.go:44.38,46.3 1 1 -github.com\bkmoovio\ach\file.go:50.36,52.2 1 1 -github.com\bkmoovio\ach\file.go:70.22,75.2 1 1 -github.com\bkmoovio\ach\file.go:78.31,80.44 1 1 -github.com\bkmoovio\ach\file.go:84.2,84.25 1 1 -github.com\bkmoovio\ach\file.go:88.2,94.34 7 1 -github.com\bkmoovio\ach\file.go:110.2,113.36 3 1 -github.com\bkmoovio\ach\file.go:118.2,124.12 6 1 -github.com\bkmoovio\ach\file.go:80.44,82.3 1 1 -github.com\bkmoovio\ach\file.go:84.25,86.3 1 1 -github.com\bkmoovio\ach\file.go:94.34,108.3 8 1 -github.com\bkmoovio\ach\file.go:113.36,115.3 1 1 -github.com\bkmoovio\ach\file.go:115.3,117.3 1 1 -github.com\bkmoovio\ach\file.go:128.50,129.22 1 1 -github.com\bkmoovio\ach\file.go:133.2,133.40 1 1 -github.com\bkmoovio\ach\file.go:136.2,137.18 2 1 -github.com\bkmoovio\ach\file.go:130.17,131.77 1 1 -github.com\bkmoovio\ach\file.go:133.40,135.3 1 1 -github.com\bkmoovio\ach\file.go:141.46,144.2 2 1 -github.com\bkmoovio\ach\file.go:147.33,149.44 1 1 -github.com\bkmoovio\ach\file.go:154.2,154.48 1 1 -github.com\bkmoovio\ach\file.go:158.2,158.41 1 1 -github.com\bkmoovio\ach\file.go:162.2,162.24 1 1 -github.com\bkmoovio\ach\file.go:149.44,152.3 2 1 -github.com\bkmoovio\ach\file.go:154.48,156.3 1 1 -github.com\bkmoovio\ach\file.go:158.41,160.3 1 1 -github.com\bkmoovio\ach\file.go:167.44,170.34 2 1 -github.com\bkmoovio\ach\file.go:173.2,173.42 1 1 -github.com\bkmoovio\ach\file.go:177.2,177.12 1 1 -github.com\bkmoovio\ach\file.go:170.34,172.3 1 1 -github.com\bkmoovio\ach\file.go:173.42,176.3 2 1 -github.com\bkmoovio\ach\file.go:182.37,185.34 3 1 -github.com\bkmoovio\ach\file.go:189.2,189.58 1 1 -github.com\bkmoovio\ach\file.go:193.2,193.60 1 1 -github.com\bkmoovio\ach\file.go:197.2,197.12 1 1 -github.com\bkmoovio\ach\file.go:185.34,188.3 2 1 -github.com\bkmoovio\ach\file.go:189.58,192.3 2 1 -github.com\bkmoovio\ach\file.go:193.60,196.3 2 1 -github.com\bkmoovio\ach\file.go:201.36,203.45 2 1 -github.com\bkmoovio\ach\file.go:207.2,207.12 1 1 -github.com\bkmoovio\ach\file.go:203.45,206.3 2 1 -github.com\bkmoovio\ach\file.go:212.44,214.34 2 1 -github.com\bkmoovio\ach\file.go:217.2,217.33 1 1 -github.com\bkmoovio\ach\file.go:214.34,216.3 1 1 -github.com\bkmoovio\ach\fileControl.go:44.45,62.2 8 1 -github.com\bkmoovio\ach\fileControl.go:65.35,70.2 1 1 -github.com\bkmoovio\ach\fileControl.go:73.40,84.2 1 1 -github.com\bkmoovio\ach\fileControl.go:88.41,89.44 1 1 -github.com\bkmoovio\ach\fileControl.go:92.2,92.26 1 1 -github.com\bkmoovio\ach\fileControl.go:96.2,96.12 1 1 -github.com\bkmoovio\ach\fileControl.go:89.44,91.3 1 1 -github.com\bkmoovio\ach\fileControl.go:92.26,95.3 2 1 -github.com\bkmoovio\ach\fileControl.go:101.47,102.25 1 1 -github.com\bkmoovio\ach\fileControl.go:105.2,105.24 1 1 -github.com\bkmoovio\ach\fileControl.go:108.2,108.24 1 1 -github.com\bkmoovio\ach\fileControl.go:111.2,111.31 1 1 -github.com\bkmoovio\ach\fileControl.go:114.2,114.23 1 1 -github.com\bkmoovio\ach\fileControl.go:117.2,117.12 1 1 -github.com\bkmoovio\ach\fileControl.go:102.25,104.3 1 1 -github.com\bkmoovio\ach\fileControl.go:105.24,107.3 1 1 -github.com\bkmoovio\ach\fileControl.go:108.24,110.3 1 1 -github.com\bkmoovio\ach\fileControl.go:111.31,113.3 1 1 -github.com\bkmoovio\ach\fileControl.go:114.23,116.3 1 1 -github.com\bkmoovio\ach\fileControl.go:121.49,123.2 1 1 -github.com\bkmoovio\ach\fileControl.go:126.49,128.2 1 1 -github.com\bkmoovio\ach\fileControl.go:131.56,133.2 1 1 -github.com\bkmoovio\ach\fileControl.go:136.48,138.2 1 1 -github.com\bkmoovio\ach\fileControl.go:141.72,143.2 1 1 -github.com\bkmoovio\ach\fileControl.go:146.73,148.2 1 1 -github.com\bkmoovio\ach\reader.go:22.37,23.20 1 1 -github.com\bkmoovio\ach\reader.go:26.2,26.79 1 1 -github.com\bkmoovio\ach\reader.go:23.20,25.3 1 1 -github.com\bkmoovio\ach\reader.go:46.41,52.2 1 1 -github.com\bkmoovio\ach\reader.go:56.49,58.2 1 1 -github.com\bkmoovio\ach\reader.go:61.37,65.2 1 1 -github.com\bkmoovio\ach\reader.go:70.39,73.23 2 1 -github.com\bkmoovio\ach\reader.go:94.2,94.37 1 1 -github.com\bkmoovio\ach\reader.go:99.2,99.39 1 1 -github.com\bkmoovio\ach\reader.go:105.2,105.20 1 1 -github.com\bkmoovio\ach\reader.go:73.23,78.10 4 1 -github.com\bkmoovio\ach\reader.go:79.84,80.57 1 1 -github.com\bkmoovio\ach\reader.go:83.35,86.31 3 1 -github.com\bkmoovio\ach\reader.go:87.11,89.40 2 1 -github.com\bkmoovio\ach\reader.go:80.57,82.5 1 1 -github.com\bkmoovio\ach\reader.go:89.40,91.5 1 1 -github.com\bkmoovio\ach\reader.go:94.37,98.3 2 1 -github.com\bkmoovio\ach\reader.go:99.39,103.3 2 1 -github.com\bkmoovio\ach\reader.go:108.60,111.26 2 1 -github.com\bkmoovio\ach\reader.go:121.2,121.12 1 1 -github.com\bkmoovio\ach\reader.go:111.26,113.39 2 1 -github.com\bkmoovio\ach\reader.go:113.39,115.40 2 1 -github.com\bkmoovio\ach\reader.go:118.4,118.15 1 1 -github.com\bkmoovio\ach\reader.go:115.40,117.5 1 1 -github.com\bkmoovio\ach\reader.go:124.36,125.20 1 1 -github.com\bkmoovio\ach\reader.go:164.2,164.12 1 1 -github.com\bkmoovio\ach\reader.go:126.21,127.45 1 1 -github.com\bkmoovio\ach\reader.go:130.22,131.46 1 1 -github.com\bkmoovio\ach\reader.go:134.22,135.46 1 1 -github.com\bkmoovio\ach\reader.go:138.23,139.42 1 1 -github.com\bkmoovio\ach\reader.go:142.23,143.47 1 1 -github.com\bkmoovio\ach\reader.go:146.3,146.51 1 1 -github.com\bkmoovio\ach\reader.go:150.3,151.23 2 1 -github.com\bkmoovio\ach\reader.go:152.22,153.25 1 1 -github.com\bkmoovio\ach\reader.go:157.3,157.46 1 1 -github.com\bkmoovio\ach\reader.go:160.10,162.83 2 1 -github.com\bkmoovio\ach\reader.go:127.45,129.4 1 1 -github.com\bkmoovio\ach\reader.go:131.46,133.4 1 1 -github.com\bkmoovio\ach\reader.go:135.46,137.4 1 1 -github.com\bkmoovio\ach\reader.go:139.42,141.4 1 1 -github.com\bkmoovio\ach\reader.go:143.47,145.4 1 1 -github.com\bkmoovio\ach\reader.go:146.51,149.4 2 1 -github.com\bkmoovio\ach\reader.go:153.25,155.9 1 1 -github.com\bkmoovio\ach\reader.go:157.46,159.4 1 1 -github.com\bkmoovio\ach\reader.go:168.42,170.37 2 1 -github.com\bkmoovio\ach\reader.go:174.2,176.49 2 1 -github.com\bkmoovio\ach\reader.go:179.2,179.12 1 1 -github.com\bkmoovio\ach\reader.go:170.37,173.3 1 1 -github.com\bkmoovio\ach\reader.go:176.49,178.3 1 1 -github.com\bkmoovio\ach\reader.go:183.43,185.27 2 1 -github.com\bkmoovio\ach\reader.go:191.2,193.38 3 1 -github.com\bkmoovio\ach\reader.go:198.2,199.16 2 1 -github.com\bkmoovio\ach\reader.go:203.2,204.12 2 1 -github.com\bkmoovio\ach\reader.go:185.27,188.3 1 1 -github.com\bkmoovio\ach\reader.go:193.38,195.3 1 1 -github.com\bkmoovio\ach\reader.go:199.16,201.3 1 0 -github.com\bkmoovio\ach\reader.go:208.43,210.27 2 1 -github.com\bkmoovio\ach\reader.go:213.2,215.38 3 1 -github.com\bkmoovio\ach\reader.go:218.2,219.12 2 1 -github.com\bkmoovio\ach\reader.go:210.27,212.3 1 1 -github.com\bkmoovio\ach\reader.go:215.38,217.3 1 0 -github.com\bkmoovio\ach\reader.go:223.39,226.27 2 1 -github.com\bkmoovio\ach\reader.go:230.2,230.43 1 1 -github.com\bkmoovio\ach\reader.go:233.2,236.39 3 1 -github.com\bkmoovio\ach\reader.go:265.2,265.12 1 1 -github.com\bkmoovio\ach\reader.go:226.27,229.3 2 1 -github.com\bkmoovio\ach\reader.go:230.43,232.3 1 1 -github.com\bkmoovio\ach\reader.go:236.39,237.22 1 1 -github.com\bkmoovio\ach\reader.go:238.13,241.47 3 1 -github.com\bkmoovio\ach\reader.go:244.4,244.65 1 1 -github.com\bkmoovio\ach\reader.go:245.13,248.47 3 1 -github.com\bkmoovio\ach\reader.go:251.4,251.65 1 1 -github.com\bkmoovio\ach\reader.go:252.13,255.47 3 1 -github.com\bkmoovio\ach\reader.go:258.4,258.65 1 1 -github.com\bkmoovio\ach\reader.go:241.47,243.5 1 1 -github.com\bkmoovio\ach\reader.go:248.47,250.5 1 0 -github.com\bkmoovio\ach\reader.go:255.47,257.5 1 0 -github.com\bkmoovio\ach\reader.go:260.3,263.3 2 1 -github.com\bkmoovio\ach\reader.go:269.44,271.27 2 1 -github.com\bkmoovio\ach\reader.go:275.2,276.63 2 1 -github.com\bkmoovio\ach\reader.go:279.2,279.12 1 1 -github.com\bkmoovio\ach\reader.go:271.27,274.3 1 1 -github.com\bkmoovio\ach\reader.go:276.63,278.3 1 1 -github.com\bkmoovio\ach\reader.go:283.43,285.39 2 1 -github.com\bkmoovio\ach\reader.go:289.2,290.50 2 1 -github.com\bkmoovio\ach\reader.go:293.2,293.12 1 1 -github.com\bkmoovio\ach\reader.go:285.39,288.3 1 1 -github.com\bkmoovio\ach\reader.go:290.50,292.3 1 1 -github.com\bkmoovio\ach\validators.go:34.37,36.2 1 1 -github.com\bkmoovio\ach\validators.go:53.52,54.14 1 1 -github.com\bkmoovio\ach\validators.go:66.2,66.36 1 1 -github.com\bkmoovio\ach\validators.go:63.7,64.13 1 1 -github.com\bkmoovio\ach\validators.go:70.50,71.14 1 1 -github.com\bkmoovio\ach\validators.go:77.2,77.31 1 1 -github.com\bkmoovio\ach\validators.go:74.86,75.13 1 1 -github.com\bkmoovio\ach\validators.go:83.51,84.14 1 1 -github.com\bkmoovio\ach\validators.go:100.2,100.39 1 1 -github.com\bkmoovio\ach\validators.go:97.8,98.13 1 1 -github.com\bkmoovio\ach\validators.go:121.55,122.14 1 1 -github.com\bkmoovio\ach\validators.go:229.2,229.39 1 1 -github.com\bkmoovio\ach\validators.go:226.6,227.13 1 1 -github.com\bkmoovio\ach\validators.go:233.60,234.14 1 1 -github.com\bkmoovio\ach\validators.go:244.2,244.38 1 1 -github.com\bkmoovio\ach\validators.go:241.5,242.13 1 1 -github.com\bkmoovio\ach\validators.go:248.57,249.43 1 1 -github.com\bkmoovio\ach\validators.go:252.2,252.12 1 1 -github.com\bkmoovio\ach\validators.go:249.43,251.3 1 1 -github.com\bkmoovio\ach\validators.go:256.52,257.38 1 1 -github.com\bkmoovio\ach\validators.go:261.2,261.12 1 1 -github.com\bkmoovio\ach\validators.go:257.38,260.3 1 1 -github.com\bkmoovio\ach\validators.go:271.67,273.25 2 1 -github.com\bkmoovio\ach\validators.go:276.2,293.31 17 1 -github.com\bkmoovio\ach\validators.go:273.25,275.3 1 1 -github.com\bkmoovio\ach\validators.go:297.42,299.2 1 1 -github.com\bkmoovio\ach\batch.go:28.49,29.35 1 1 -github.com\bkmoovio\ach\batch.go:52.2,53.71 2 1 -github.com\bkmoovio\ach\batch.go:30.13,31.30 1 1 -github.com\bkmoovio\ach\batch.go:32.13,33.30 1 1 -github.com\bkmoovio\ach\batch.go:34.13,35.30 1 1 -github.com\bkmoovio\ach\batch.go:36.13,37.30 1 1 -github.com\bkmoovio\ach\batch.go:38.13,39.30 1 1 -github.com\bkmoovio\ach\batch.go:40.13,41.30 1 1 -github.com\bkmoovio\ach\batch.go:42.13,43.30 1 1 -github.com\bkmoovio\ach\batch.go:44.13,45.30 1 1 -github.com\bkmoovio\ach\batch.go:46.13,47.30 1 1 -github.com\bkmoovio\ach\batch.go:48.13,49.30 1 1 -github.com\bkmoovio\ach\batch.go:50.10,50.10 0 1 -github.com\bkmoovio\ach\batch.go:57.36,61.49 2 1 -github.com\bkmoovio\ach\batch.go:69.2,69.69 1 1 -github.com\bkmoovio\ach\batch.go:74.2,74.79 1 1 -github.com\bkmoovio\ach\batch.go:79.2,79.73 1 1 -github.com\bkmoovio\ach\batch.go:84.2,84.59 1 1 -github.com\bkmoovio\ach\batch.go:89.2,89.50 1 1 -github.com\bkmoovio\ach\batch.go:93.2,93.52 1 1 -github.com\bkmoovio\ach\batch.go:97.2,97.46 1 1 -github.com\bkmoovio\ach\batch.go:101.2,101.44 1 1 -github.com\bkmoovio\ach\batch.go:105.2,105.48 1 1 -github.com\bkmoovio\ach\batch.go:109.2,109.50 1 1 -github.com\bkmoovio\ach\batch.go:113.2,113.50 1 1 -github.com\bkmoovio\ach\batch.go:116.2,116.27 1 1 -github.com\bkmoovio\ach\batch.go:61.49,63.37 1 1 -github.com\bkmoovio\ach\batch.go:66.3,66.90 1 0 -github.com\bkmoovio\ach\batch.go:63.37,65.4 1 1 -github.com\bkmoovio\ach\batch.go:69.69,72.3 2 1 -github.com\bkmoovio\ach\batch.go:74.79,77.3 2 1 -github.com\bkmoovio\ach\batch.go:79.73,82.3 2 1 -github.com\bkmoovio\ach\batch.go:84.59,87.3 2 1 -github.com\bkmoovio\ach\batch.go:89.50,91.3 1 1 -github.com\bkmoovio\ach\batch.go:93.52,95.3 1 1 -github.com\bkmoovio\ach\batch.go:97.46,99.3 1 1 -github.com\bkmoovio\ach\batch.go:101.44,103.3 1 1 -github.com\bkmoovio\ach\batch.go:105.48,107.3 1 1 -github.com\bkmoovio\ach\batch.go:109.50,111.3 1 1 -github.com\bkmoovio\ach\batch.go:113.50,115.3 1 1 -github.com\bkmoovio\ach\batch.go:121.35,123.48 1 1 -github.com\bkmoovio\ach\batch.go:126.2,126.29 1 1 -github.com\bkmoovio\ach\batch.go:130.2,132.38 3 1 -github.com\bkmoovio\ach\batch.go:161.2,171.12 10 1 -github.com\bkmoovio\ach\batch.go:123.48,125.3 1 1 -github.com\bkmoovio\ach\batch.go:126.29,128.3 1 1 -github.com\bkmoovio\ach\batch.go:132.38,135.17 3 1 -github.com\bkmoovio\ach\batch.go:139.3,140.17 2 1 -github.com\bkmoovio\ach\batch.go:145.3,145.48 1 1 -github.com\bkmoovio\ach\batch.go:148.3,150.33 3 1 -github.com\bkmoovio\ach\batch.go:135.17,137.4 1 0 -github.com\bkmoovio\ach\batch.go:140.17,142.4 1 0 -github.com\bkmoovio\ach\batch.go:145.48,147.4 1 1 -github.com\bkmoovio\ach\batch.go:150.33,152.62 1 1 -github.com\bkmoovio\ach\batch.go:156.4,156.16 1 1 -github.com\bkmoovio\ach\batch.go:152.62,155.5 2 1 -github.com\bkmoovio\ach\batch.go:175.57,177.2 1 1 -github.com\bkmoovio\ach\batch.go:180.46,182.2 1 1 -github.com\bkmoovio\ach\batch.go:185.60,187.2 1 1 -github.com\bkmoovio\ach\batch.go:190.48,192.2 1 1 -github.com\bkmoovio\ach\batch.go:195.49,197.2 1 1 -github.com\bkmoovio\ach\batch.go:200.50,203.2 2 1 -github.com\bkmoovio\ach\batch.go:206.39,208.2 1 1 -github.com\bkmoovio\ach\batch.go:211.46,212.48 1 1 -github.com\bkmoovio\ach\batch.go:215.2,215.38 1 1 -github.com\bkmoovio\ach\batch.go:225.2,225.33 1 1 -github.com\bkmoovio\ach\batch.go:212.48,214.3 1 1 -github.com\bkmoovio\ach\batch.go:215.38,216.42 1 1 -github.com\bkmoovio\ach\batch.go:219.3,219.42 1 1 -github.com\bkmoovio\ach\batch.go:216.42,218.4 1 1 -github.com\bkmoovio\ach\batch.go:219.42,220.45 1 1 -github.com\bkmoovio\ach\batch.go:220.45,222.5 1 1 -github.com\bkmoovio\ach\batch.go:231.47,233.38 2 1 -github.com\bkmoovio\ach\batch.go:236.2,236.51 1 1 -github.com\bkmoovio\ach\batch.go:240.2,240.12 1 1 -github.com\bkmoovio\ach\batch.go:233.38,235.3 1 1 -github.com\bkmoovio\ach\batch.go:236.51,239.3 2 1 -github.com\bkmoovio\ach\batch.go:246.43,248.56 2 1 -github.com\bkmoovio\ach\batch.go:253.2,253.58 1 1 -github.com\bkmoovio\ach\batch.go:257.2,257.12 1 1 -github.com\bkmoovio\ach\batch.go:248.56,251.3 2 1 -github.com\bkmoovio\ach\batch.go:253.58,256.3 2 1 -github.com\bkmoovio\ach\batch.go:260.69,261.38 1 1 -github.com\bkmoovio\ach\batch.go:269.2,269.22 1 1 -github.com\bkmoovio\ach\batch.go:261.38,262.158 1 1 -github.com\bkmoovio\ach\batch.go:265.3,265.189 1 1 -github.com\bkmoovio\ach\batch.go:262.158,264.4 1 1 -github.com\bkmoovio\ach\batch.go:265.189,267.4 1 1 -github.com\bkmoovio\ach\batch.go:274.49,276.38 2 1 -github.com\bkmoovio\ach\batch.go:283.2,283.12 1 1 -github.com\bkmoovio\ach\batch.go:276.38,277.35 1 1 -github.com\bkmoovio\ach\batch.go:281.3,281.30 1 1 -github.com\bkmoovio\ach\batch.go:277.35,280.4 2 1 -github.com\bkmoovio\ach\batch.go:287.41,289.49 2 1 -github.com\bkmoovio\ach\batch.go:293.2,293.12 1 1 -github.com\bkmoovio\ach\batch.go:289.49,292.3 2 1 -github.com\bkmoovio\ach\batch.go:298.49,300.38 2 1 -github.com\bkmoovio\ach\batch.go:306.2,306.37 1 1 -github.com\bkmoovio\ach\batch.go:300.38,305.3 2 1 -github.com\bkmoovio\ach\batch.go:310.45,311.44 1 1 -github.com\bkmoovio\ach\batch.go:319.2,319.12 1 1 -github.com\bkmoovio\ach\batch.go:311.44,312.39 1 1 -github.com\bkmoovio\ach\batch.go:312.39,313.66 1 1 -github.com\bkmoovio\ach\batch.go:313.66,316.5 2 1 -github.com\bkmoovio\ach\batch.go:324.47,325.38 1 1 -github.com\bkmoovio\ach\batch.go:332.2,332.12 1 1 -github.com\bkmoovio\ach\batch.go:325.38,326.77 1 1 -github.com\bkmoovio\ach\batch.go:326.77,329.4 2 1 -github.com\bkmoovio\ach\batch.go:336.47,337.38 1 1 -github.com\bkmoovio\ach\batch.go:363.2,363.12 1 1 -github.com\bkmoovio\ach\batch.go:337.38,338.30 1 1 -github.com\bkmoovio\ach\batch.go:338.30,340.41 1 1 -github.com\bkmoovio\ach\batch.go:343.4,345.43 2 1 -github.com\bkmoovio\ach\batch.go:340.41,342.5 1 1 -github.com\bkmoovio\ach\batch.go:345.43,347.42 1 1 -github.com\bkmoovio\ach\batch.go:347.42,349.36 1 1 -github.com\bkmoovio\ach\batch.go:353.6,355.79 2 1 -github.com\bkmoovio\ach\batch.go:349.36,352.7 2 1 -github.com\bkmoovio\ach\batch.go:355.79,358.7 2 1 -github.com\bkmoovio\ach\batch.go:369.53,370.38 1 1 -github.com\bkmoovio\ach\batch.go:377.2,377.12 1 1 -github.com\bkmoovio\ach\batch.go:370.38,371.34 1 1 -github.com\bkmoovio\ach\batch.go:371.34,374.4 2 1 -github.com\bkmoovio\ach\batch.go:381.55,382.38 1 1 -github.com\bkmoovio\ach\batch.go:390.2,390.12 1 1 -github.com\bkmoovio\ach\batch.go:382.38,383.42 1 1 -github.com\bkmoovio\ach\batch.go:383.42,384.38 1 1 -github.com\bkmoovio\ach\batch.go:384.38,387.5 2 1 -github.com\bkmoovio\ach\batch.go:394.40,396.28 2 1 -github.com\bkmoovio\ach\batch.go:406.2,406.12 1 1 -github.com\bkmoovio\ach\batch.go:396.28,397.43 1 1 -github.com\bkmoovio\ach\batch.go:397.43,398.48 1 1 -github.com\bkmoovio\ach\batch.go:401.4,401.45 1 1 -github.com\bkmoovio\ach\batch.go:398.48,399.13 1 0 -github.com\bkmoovio\ach\batch.go:401.45,403.5 1 1 -github.com\bkmoovio\ach\batch.go:412.47,413.38 1 1 -github.com\bkmoovio\ach\batch.go:420.2,420.12 1 1 -github.com\bkmoovio\ach\batch.go:413.38,414.141 1 1 -github.com\bkmoovio\ach\batch.go:414.141,418.4 2 0 -github.com\bkmoovio\ach\batchCOR.go:23.45,28.2 4 1 -github.com\bkmoovio\ach\batchCOR.go:31.41,33.39 1 1 -github.com\bkmoovio\ach\batchCOR.go:38.2,38.44 1 1 -github.com\bkmoovio\ach\batchCOR.go:43.2,43.50 1 1 -github.com\bkmoovio\ach\batchCOR.go:50.2,50.103 1 1 -github.com\bkmoovio\ach\batchCOR.go:55.2,55.38 1 1 -github.com\bkmoovio\ach\batchCOR.go:66.2,66.12 1 1 -github.com\bkmoovio\ach\batchCOR.go:33.39,35.3 1 0 -github.com\bkmoovio\ach\batchCOR.go:38.44,40.3 1 1 -github.com\bkmoovio\ach\batchCOR.go:43.50,46.3 2 0 -github.com\bkmoovio\ach\batchCOR.go:50.103,53.3 2 1 -github.com\bkmoovio\ach\batchCOR.go:55.38,59.56 1 1 -github.com\bkmoovio\ach\batchCOR.go:59.56,62.4 2 1 -github.com\bkmoovio\ach\batchCOR.go:70.39,72.38 1 1 -github.com\bkmoovio\ach\batchCOR.go:76.2,76.25 1 1 -github.com\bkmoovio\ach\batchCOR.go:72.38,74.3 1 1 -github.com\bkmoovio\ach\batchCOR.go:80.44,81.38 1 1 -github.com\bkmoovio\ach\batchCOR.go:100.2,100.12 1 1 -github.com\bkmoovio\ach\batchCOR.go:81.38,83.31 1 1 -github.com\bkmoovio\ach\batchCOR.go:87.3,88.10 2 1 -github.com\bkmoovio\ach\batchCOR.go:93.3,93.46 1 1 -github.com\bkmoovio\ach\batchCOR.go:83.31,85.4 1 1 -github.com\bkmoovio\ach\batchCOR.go:88.10,91.4 2 1 -github.com\bkmoovio\ach\batchCOR.go:93.46,95.38 1 1 -github.com\bkmoovio\ach\batchCOR.go:95.38,97.5 1 1 -github.com\bkmoovio\ach\batchPOS.go:31.45,36.2 4 1 -github.com\bkmoovio\ach\batchPOS.go:39.41,41.39 1 1 -github.com\bkmoovio\ach\batchPOS.go:47.2,47.44 1 1 -github.com\bkmoovio\ach\batchPOS.go:51.2,51.50 1 1 -github.com\bkmoovio\ach\batchPOS.go:64.2,64.38 1 1 -github.com\bkmoovio\ach\batchPOS.go:72.2,72.12 1 1 -github.com\bkmoovio\ach\batchPOS.go:41.39,43.3 1 1 -github.com\bkmoovio\ach\batchPOS.go:47.44,49.3 1 1 -github.com\bkmoovio\ach\batchPOS.go:51.50,54.3 2 1 -github.com\bkmoovio\ach\batchPOS.go:64.38,66.35 1 1 -github.com\bkmoovio\ach\batchPOS.go:66.35,69.4 2 1 -github.com\bkmoovio\ach\batchPOS.go:76.39,78.38 1 1 -github.com\bkmoovio\ach\batchPOS.go:84.2,84.25 1 1 -github.com\bkmoovio\ach\batchPOS.go:78.38,80.3 1 1 -github.com\bkmoovio\ach\batchPOS.go:88.44,89.38 1 1 -github.com\bkmoovio\ach\batchPOS.go:108.2,108.12 1 1 -github.com\bkmoovio\ach\batchPOS.go:89.38,91.31 1 1 -github.com\bkmoovio\ach\batchPOS.go:95.3,96.10 2 1 -github.com\bkmoovio\ach\batchPOS.go:101.3,101.46 1 1 -github.com\bkmoovio\ach\batchPOS.go:91.31,93.4 1 1 -github.com\bkmoovio\ach\batchPOS.go:96.10,99.4 2 1 -github.com\bkmoovio\ach\batchPOS.go:101.46,103.38 1 1 -github.com\bkmoovio\ach\batchPOS.go:103.38,105.5 1 1 -github.com\bkmoovio\ach\addenda99.go:28.13,31.2 1 1 -github.com\bkmoovio\ach\addenda99.go:71.32,77.2 2 1 -github.com\bkmoovio\ach\addenda99.go:80.50,97.2 8 1 -github.com\bkmoovio\ach\addenda99.go:100.45,111.2 1 1 -github.com\bkmoovio\ach\addenda99.go:114.46,116.33 1 1 -github.com\bkmoovio\ach\addenda99.go:122.2,123.9 2 1 -github.com\bkmoovio\ach\addenda99.go:127.2,127.12 1 1 -github.com\bkmoovio\ach\addenda99.go:116.33,119.3 2 0 -github.com\bkmoovio\ach\addenda99.go:123.9,126.3 1 1 -github.com\bkmoovio\ach\addenda99.go:131.47,133.2 1 1 -github.com\bkmoovio\ach\addenda99.go:136.57,138.2 1 1 -github.com\bkmoovio\ach\addenda99.go:141.55,143.36 1 1 -github.com\bkmoovio\ach\addenda99.go:147.2,147.58 1 1 -github.com\bkmoovio\ach\addenda99.go:143.36,145.3 1 1 -github.com\bkmoovio\ach\addenda99.go:151.55,153.2 1 1 -github.com\bkmoovio\ach\addenda99.go:156.62,158.2 1 1 -github.com\bkmoovio\ach\addenda99.go:161.55,163.2 1 1 -github.com\bkmoovio\ach\addenda99.go:165.50,217.29 3 1 -github.com\bkmoovio\ach\addenda99.go:220.2,220.13 1 1 -github.com\bkmoovio\ach\addenda99.go:217.29,219.3 1 1 -github.com\bkmoovio\ach\batchControl.go:74.46,98.2 11 1 -github.com\bkmoovio\ach\batchControl.go:101.38,108.2 1 1 -github.com\bkmoovio\ach\batchControl.go:111.41,125.2 1 1 -github.com\bkmoovio\ach\batchControl.go:129.42,130.44 1 1 -github.com\bkmoovio\ach\batchControl.go:133.2,133.26 1 1 -github.com\bkmoovio\ach\batchControl.go:137.2,137.63 1 1 -github.com\bkmoovio\ach\batchControl.go:141.2,141.68 1 1 -github.com\bkmoovio\ach\batchControl.go:145.2,145.72 1 1 -github.com\bkmoovio\ach\batchControl.go:149.2,149.12 1 1 -github.com\bkmoovio\ach\batchControl.go:130.44,132.3 1 1 -github.com\bkmoovio\ach\batchControl.go:133.26,136.3 2 1 -github.com\bkmoovio\ach\batchControl.go:137.63,139.3 1 1 -github.com\bkmoovio\ach\batchControl.go:141.68,143.3 1 1 -github.com\bkmoovio\ach\batchControl.go:145.72,147.3 1 1 -github.com\bkmoovio\ach\batchControl.go:154.48,155.25 1 1 -github.com\bkmoovio\ach\batchControl.go:158.2,158.30 1 1 -github.com\bkmoovio\ach\batchControl.go:161.2,161.42 1 1 -github.com\bkmoovio\ach\batchControl.go:164.2,164.12 1 1 -github.com\bkmoovio\ach\batchControl.go:155.25,157.3 1 1 -github.com\bkmoovio\ach\batchControl.go:158.30,160.3 1 1 -github.com\bkmoovio\ach\batchControl.go:161.42,163.3 1 1 -github.com\bkmoovio\ach\batchControl.go:168.57,170.2 1 1 -github.com\bkmoovio\ach\batchControl.go:173.49,175.2 1 1 -github.com\bkmoovio\ach\batchControl.go:178.67,180.2 1 1 -github.com\bkmoovio\ach\batchControl.go:183.68,185.2 1 1 -github.com\bkmoovio\ach\batchControl.go:188.61,190.2 1 1 -github.com\bkmoovio\ach\batchControl.go:193.65,195.2 1 1 -github.com\bkmoovio\ach\batchControl.go:198.58,200.2 1 1 -github.com\bkmoovio\ach\batchControl.go:203.51,205.2 1 1 -github.com\bkmoovio\ach\batchWeb.go:19.45,24.2 4 1 -github.com\bkmoovio\ach\batchWeb.go:27.41,29.39 1 1 -github.com\bkmoovio\ach\batchWeb.go:34.2,34.48 1 1 -github.com\bkmoovio\ach\batchWeb.go:37.2,37.47 1 1 -github.com\bkmoovio\ach\batchWeb.go:42.2,42.50 1 1 -github.com\bkmoovio\ach\batchWeb.go:47.2,47.34 1 1 -github.com\bkmoovio\ach\batchWeb.go:29.39,31.3 1 1 -github.com\bkmoovio\ach\batchWeb.go:34.48,36.3 1 1 -github.com\bkmoovio\ach\batchWeb.go:37.47,39.3 1 1 -github.com\bkmoovio\ach\batchWeb.go:42.50,45.3 2 1 -github.com\bkmoovio\ach\batchWeb.go:51.39,53.38 1 1 -github.com\bkmoovio\ach\batchWeb.go:57.2,57.25 1 1 -github.com\bkmoovio\ach\batchWeb.go:53.38,55.3 1 1 -github.com\bkmoovio\ach\addenda02.go:51.32,56.2 4 1 -github.com\bkmoovio\ach\addenda02.go:59.50,84.2 12 1 -github.com\bkmoovio\ach\addenda02.go:87.45,103.2 1 1 -github.com\bkmoovio\ach\addenda02.go:107.46,108.51 1 1 -github.com\bkmoovio\ach\addenda02.go:111.2,111.33 1 1 -github.com\bkmoovio\ach\addenda02.go:115.2,115.65 1 1 -github.com\bkmoovio\ach\addenda02.go:120.2,120.32 1 1 -github.com\bkmoovio\ach\addenda02.go:124.2,124.12 1 1 -github.com\bkmoovio\ach\addenda02.go:108.51,110.3 1 1 -github.com\bkmoovio\ach\addenda02.go:111.33,114.3 2 1 -github.com\bkmoovio\ach\addenda02.go:115.65,117.3 1 1 -github.com\bkmoovio\ach\addenda02.go:120.32,122.3 1 1 -github.com\bkmoovio\ach\addenda02.go:132.52,133.32 1 1 -github.com\bkmoovio\ach\addenda02.go:136.2,136.30 1 1 -github.com\bkmoovio\ach\addenda02.go:139.2,139.48 1 1 -github.com\bkmoovio\ach\addenda02.go:142.2,142.45 1 1 -github.com\bkmoovio\ach\addenda02.go:145.2,145.37 1 1 -github.com\bkmoovio\ach\addenda02.go:148.2,148.38 1 1 -github.com\bkmoovio\ach\addenda02.go:151.2,151.34 1 1 -github.com\bkmoovio\ach\addenda02.go:154.2,154.35 1 1 -github.com\bkmoovio\ach\addenda02.go:158.2,158.12 1 1 -github.com\bkmoovio\ach\addenda02.go:133.32,135.3 1 1 -github.com\bkmoovio\ach\addenda02.go:136.30,138.3 1 1 -github.com\bkmoovio\ach\addenda02.go:139.48,141.3 1 1 -github.com\bkmoovio\ach\addenda02.go:142.45,144.3 1 1 -github.com\bkmoovio\ach\addenda02.go:145.37,147.3 1 1 -github.com\bkmoovio\ach\addenda02.go:148.38,150.3 1 1 -github.com\bkmoovio\ach\addenda02.go:151.34,153.3 1 1 -github.com\bkmoovio\ach\addenda02.go:154.35,156.3 1 1 -github.com\bkmoovio\ach\addenda02.go:162.47,164.2 1 1 -github.com\bkmoovio\ach\addenda02.go:167.67,169.2 1 1 -github.com\bkmoovio\ach\addenda02.go:172.67,174.2 1 1 -github.com\bkmoovio\ach\addenda02.go:177.70,179.2 1 1 -github.com\bkmoovio\ach\addenda02.go:182.67,184.2 1 1 -github.com\bkmoovio\ach\addenda02.go:187.59,190.2 1 1 -github.com\bkmoovio\ach\addenda02.go:193.73,195.2 1 1 -github.com\bkmoovio\ach\addenda02.go:198.60,200.2 1 1 -github.com\bkmoovio\ach\addenda02.go:203.56,205.2 1 1 -github.com\bkmoovio\ach\addenda02.go:208.57,210.2 1 1 -github.com\bkmoovio\ach\addenda02.go:213.55,215.2 1 1 -github.com\bkmoovio\ach\addenda98.go:49.13,52.2 1 1 -github.com\bkmoovio\ach\addenda98.go:61.32,67.2 2 1 -github.com\bkmoovio\ach\addenda98.go:70.50,85.2 7 1 -github.com\bkmoovio\ach\addenda98.go:88.45,100.2 1 1 -github.com\bkmoovio\ach\addenda98.go:103.46,104.33 1 1 -github.com\bkmoovio\ach\addenda98.go:109.2,109.32 1 1 -github.com\bkmoovio\ach\addenda98.go:114.2,115.9 2 1 -github.com\bkmoovio\ach\addenda98.go:120.2,120.35 1 1 -github.com\bkmoovio\ach\addenda98.go:124.2,124.12 1 1 -github.com\bkmoovio\ach\addenda98.go:104.33,107.3 2 1 -github.com\bkmoovio\ach\addenda98.go:109.32,111.3 1 1 -github.com\bkmoovio\ach\addenda98.go:115.9,117.3 1 1 -github.com\bkmoovio\ach\addenda98.go:120.35,122.3 1 1 -github.com\bkmoovio\ach\addenda98.go:128.47,130.2 1 1 -github.com\bkmoovio\ach\addenda98.go:133.57,135.2 1 1 -github.com\bkmoovio\ach\addenda98.go:138.55,140.2 1 1 -github.com\bkmoovio\ach\addenda98.go:143.57,145.2 1 1 -github.com\bkmoovio\ach\addenda98.go:148.55,150.2 1 1 -github.com\bkmoovio\ach\addenda98.go:152.50,169.29 3 1 -github.com\bkmoovio\ach\addenda98.go:172.2,172.13 1 1 -github.com\bkmoovio\ach\addenda98.go:169.29,171.3 1 1 -github.com\bkmoovio\ach\batchPOP.go:29.45,34.2 4 1 -github.com\bkmoovio\ach\batchPOP.go:37.41,39.39 1 1 -github.com\bkmoovio\ach\batchPOP.go:45.2,45.48 1 1 -github.com\bkmoovio\ach\batchPOP.go:50.2,50.50 1 1 -github.com\bkmoovio\ach\batchPOP.go:56.2,56.39 1 1 -github.com\bkmoovio\ach\batchPOP.go:62.2,62.38 1 1 -github.com\bkmoovio\ach\batchPOP.go:82.2,82.12 1 1 -github.com\bkmoovio\ach\batchPOP.go:39.39,41.3 1 1 -github.com\bkmoovio\ach\batchPOP.go:45.48,47.3 1 1 -github.com\bkmoovio\ach\batchPOP.go:50.50,53.3 2 1 -github.com\bkmoovio\ach\batchPOP.go:57.21,59.101 2 1 -github.com\bkmoovio\ach\batchPOP.go:62.38,64.35 1 1 -github.com\bkmoovio\ach\batchPOP.go:70.3,70.29 1 1 -github.com\bkmoovio\ach\batchPOP.go:76.3,76.39 1 1 -github.com\bkmoovio\ach\batchPOP.go:64.35,67.4 2 1 -github.com\bkmoovio\ach\batchPOP.go:70.29,73.4 2 1 -github.com\bkmoovio\ach\batchPOP.go:76.39,79.4 2 1 -github.com\bkmoovio\ach\batchPOP.go:86.39,88.38 1 1 -github.com\bkmoovio\ach\batchPOP.go:94.2,94.25 1 1 -github.com\bkmoovio\ach\batchPOP.go:88.38,90.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:101.36,107.2 2 1 -github.com\bkmoovio\ach\entryDetail.go:110.45,136.2 11 1 -github.com\bkmoovio\ach\entryDetail.go:139.40,152.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:156.41,157.44 1 1 -github.com\bkmoovio\ach\entryDetail.go:160.2,160.26 1 1 -github.com\bkmoovio\ach\entryDetail.go:164.2,164.65 1 1 -github.com\bkmoovio\ach\entryDetail.go:167.2,167.63 1 1 -github.com\bkmoovio\ach\entryDetail.go:170.2,170.67 1 1 -github.com\bkmoovio\ach\entryDetail.go:173.2,173.61 1 1 -github.com\bkmoovio\ach\entryDetail.go:176.2,176.64 1 1 -github.com\bkmoovio\ach\entryDetail.go:180.2,183.16 3 1 -github.com\bkmoovio\ach\entryDetail.go:187.2,187.32 1 1 -github.com\bkmoovio\ach\entryDetail.go:191.2,191.12 1 1 -github.com\bkmoovio\ach\entryDetail.go:157.44,159.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:160.26,163.3 2 1 -github.com\bkmoovio\ach\entryDetail.go:164.65,166.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:167.63,169.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:170.67,172.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:173.61,175.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:176.64,178.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:183.16,185.3 1 0 -github.com\bkmoovio\ach\entryDetail.go:187.32,190.3 2 1 -github.com\bkmoovio\ach\entryDetail.go:196.47,197.25 1 1 -github.com\bkmoovio\ach\entryDetail.go:200.2,200.29 1 1 -github.com\bkmoovio\ach\entryDetail.go:203.2,203.33 1 1 -github.com\bkmoovio\ach\entryDetail.go:206.2,206.31 1 1 -github.com\bkmoovio\ach\entryDetail.go:209.2,209.29 1 1 -github.com\bkmoovio\ach\entryDetail.go:212.2,212.25 1 1 -github.com\bkmoovio\ach\entryDetail.go:215.2,215.12 1 1 -github.com\bkmoovio\ach\entryDetail.go:197.25,199.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:200.29,202.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:203.33,205.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:206.31,208.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:209.29,211.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:212.25,214.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:219.68,222.24 2 1 -github.com\bkmoovio\ach\entryDetail.go:223.18,227.21 4 1 -github.com\bkmoovio\ach\entryDetail.go:228.18,232.21 4 1 -github.com\bkmoovio\ach\entryDetail.go:233.18,237.21 4 1 -github.com\bkmoovio\ach\entryDetail.go:239.10,242.21 3 1 -github.com\bkmoovio\ach\entryDetail.go:247.58,252.2 4 1 -github.com\bkmoovio\ach\entryDetail.go:255.75,258.2 2 1 -github.com\bkmoovio\ach\entryDetail.go:261.57,263.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:266.55,268.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:271.45,273.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:276.59,278.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:282.56,284.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:288.55,290.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:294.58,296.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:300.53,302.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:306.54,308.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:312.59,315.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:319.54,322.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:326.55,329.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:332.53,334.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:337.55,339.2 1 0 -github.com\bkmoovio\ach\entryDetail.go:342.54,344.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:347.56,349.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:352.50,356.2 2 1 -github.com\bkmoovio\ach\entryDetail.go:359.49,361.14 2 1 -github.com\bkmoovio\ach\entryDetail.go:361.14,363.3 1 0 -github.com\bkmoovio\ach\entryDetail.go:363.3,365.3 1 1 -github.com\bkmoovio\ach\entryDetail.go:369.50,371.2 1 1 -github.com\bkmoovio\ach\entryDetail.go:374.47,377.17 2 1 -github.com\bkmoovio\ach\entryDetail.go:383.2,383.11 1 0 -github.com\bkmoovio\ach\entryDetail.go:378.26,379.13 1 1 -github.com\bkmoovio\ach\entryDetail.go:380.31,381.13 1 1 -github.com\bkmoovio\ach\entryDetail.go:388.60,391.17 2 1 -github.com\bkmoovio\ach\entryDetail.go:397.2,397.11 1 1 -github.com\bkmoovio\ach\entryDetail.go:393.16,394.21 1 1 -github.com\bkmoovio\ach\entryDetail.go:395.10,395.10 0 1 diff --git a/coverage.html b/coverage.html deleted file mode 100644 index f338a47c8..000000000 --- a/coverage.html +++ /dev/null @@ -1,4528 +0,0 @@ - - - - - - - - -
- -
- not tracked - - not covered - covered - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - From e2dc45f6fa911bd44aef1a82c7c8198d9ec38eb4 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 13:45:09 -0600 Subject: [PATCH 0232/1694] Ignore code coverage ignore gcode coverage output in future commits. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index c3142be78..fea5b5e18 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ _testmain.go *.prof .vscode/launch.json + +# code coverage +coverage.html +cover.out From 010e07b8948ac8f2c9065eef787a1d30eeefddf5 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 13:52:44 -0600 Subject: [PATCH 0233/1694] Ignore /server/ in travis build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9675d5e26..cdecbd636 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_install: - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo before_script: -- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/) +- GO_FILES=$(find . -iname '*.go' -type f | grep -v /test/ | grep -v /cmd/ | grep -v /server/) script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES From 3a8563875768d6308e615f4f248e2d7615b1c616 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 14:08:18 -0600 Subject: [PATCH 0234/1694] Remove NOC and Returns if they don't exist --- file.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/file.go b/file.go index 1637388bf..2e6d397b0 100644 --- a/file.go +++ b/file.go @@ -60,9 +60,9 @@ type File struct { Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches - NotificationOfChange []*BatchCOR + NotificationOfChange []*BatchCOR `json:"notificationOfChange,omitempty"` // ReturnEntries is a slice of references to file.Batches that contain return entries - ReturnEntries []Batcher + ReturnEntries []Batcher `json:"returnEntries,omitempty"` converters } From e41521627ac828c5dfc18fb6b5e3848c6cb24309 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 16:54:36 -0400 Subject: [PATCH 0235/1694] #207 SHRIndividualCardAccountNumberField # 207 SHRIndividualCardAccountNumberField --- batchSHR_test.go | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/batchSHR_test.go b/batchSHR_test.go index a0fa519eb..dc7c21b0d 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -454,7 +454,7 @@ func TestBatchSHRCardExpirationDateField(t *testing.T) { testBatchSHRCardExpirationDateField(t) } -// BenchmarkBatchPOPTerminalStateField benchmarks validating SHRCardExpirationDate +// BenchmarkBatchSHRCardExpirationDateField benchmarks validating SHRCardExpirationDate // characters 0-4 of underlying IdentificationNumber func BenchmarkBatchSHRCardExpirationDateField(b *testing.B) { b.ReportAllocs() @@ -473,17 +473,42 @@ func testBatchSHRDocumentReferenceNumberField(t testing.TB) { } } -// TestBatchSHRDocumentReferenceNumberFieldS tests validating SHRDocumentReferenceNumberField +// TestBatchSHRDocumentReferenceNumberField tests validating SHRDocumentReferenceNumberField // characters 5-15 of underlying IdentificationNumber func TestBatchSHRDocumentReferenceNumberField(t *testing.T) { testBatchSHRDocumentReferenceNumberField(t) } -// BenchmarkBatchPOPTerminalStateField benchmarks validating SHRDocumentReferenceNumberField +// BenchmarkBatchSHRDocumentReferenceNumberField benchmarks validating SHRDocumentReferenceNumberField // characters 5-15 of underlying IdentificationNumber func BenchmarkSHRDocumentReferenceNumberField(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { testBatchSHRDocumentReferenceNumberField(b) } +} + +// testBatchSHRIndividualCardAccountNumberField validates SHRIndividualCardAccountNumberField +// underlying IndividualName +func testBatchSHRIndividualCardAccountNumberField(t testing.TB) { + mockBatch := mockBatchSHR() + ts := mockBatch.Entries[0].SHRIndividualCardAccountNumberField() + if ts != 12345678910123456 { + t.Error("Document Reference Number is invalid") + } +} + +// TestBatchSHRIndividualCardAccountNumberField tests validating SHRIndividualCardAccountNumberField +// characters 5-15 of underlying IndividualName +func TestBatchSHRIndividualCardAccountNumberField(t *testing.T) { + testBatchSHRIndividualCardAccountNumberField(t) +} + +// BenchmarkBatchSHRIndividualCardAccountNumberField benchmarks validating SHRIndividualCardAccountNumberField +// characters 5-15 of underlying IndividualName +func BenchmarkBatchSHRDocumentReferenceNumberField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRIndividualCardAccountNumberField(b) + } } \ No newline at end of file From d05177ec2ba8fa0625c937b9017cd59eca8b5de4 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 16:55:43 -0400 Subject: [PATCH 0236/1694] #207 Update test error message #207 Update test error message --- batchSHR_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/batchSHR_test.go b/batchSHR_test.go index dc7c21b0d..4b33a65bb 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -494,7 +494,7 @@ func testBatchSHRIndividualCardAccountNumberField(t testing.TB) { mockBatch := mockBatchSHR() ts := mockBatch.Entries[0].SHRIndividualCardAccountNumberField() if ts != 12345678910123456 { - t.Error("Document Reference Number is invalid") + t.Error("Individual Card Account Number is invalid") } } @@ -511,4 +511,4 @@ func BenchmarkBatchSHRDocumentReferenceNumberField(b *testing.B) { for i := 0; i < b.N; i++ { testBatchSHRIndividualCardAccountNumberField(b) } -} \ No newline at end of file +} From d1f84e2ce83bbb436de8168a00960d00001c4352 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 17:01:14 -0400 Subject: [PATCH 0237/1694] gofmt gofmt --- batchSHR_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batchSHR_test.go b/batchSHR_test.go index 4b33a65bb..0c793e28d 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -511,4 +511,4 @@ func BenchmarkBatchSHRDocumentReferenceNumberField(b *testing.B) { for i := 0; i < b.N; i++ { testBatchSHRIndividualCardAccountNumberField(b) } -} +} \ No newline at end of file From 6f81cc5befa31dfb22e45fe96c97937147e2aa83 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 17:06:06 -0400 Subject: [PATCH 0238/1694] revert tests reverts test --- batchSHR_test.go | 75 ------------------------------------------------ 1 file changed, 75 deletions(-) diff --git a/batchSHR_test.go b/batchSHR_test.go index 0c793e28d..7b0e3f83b 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -437,78 +437,3 @@ func BenchmarkBatchSHRCardTransactionType(b *testing.B) { testBatchSHRCardTransactionType(b) } } - -// testBatchSHRCardExpirationDateField validates SHRCardExpirationDate -// characters 0-4 of underlying IdentificationNumber -func testBatchSHRCardExpirationDateField(t testing.TB) { - mockBatch := mockBatchSHR() - ts := mockBatch.Entries[0].SHRCardExpirationDateField() - if ts != "0718" { - t.Error("Card Expiration Date is invalid") - } -} - -// TestBatchSHRCardExpirationDateField tests validatingSHRCardExpirationDate -// characters 0-4 of underlying IdentificationNumber -func TestBatchSHRCardExpirationDateField(t *testing.T) { - testBatchSHRCardExpirationDateField(t) -} - -// BenchmarkBatchSHRCardExpirationDateField benchmarks validating SHRCardExpirationDate -// characters 0-4 of underlying IdentificationNumber -func BenchmarkBatchSHRCardExpirationDateField(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchSHRCardExpirationDateField(b) - } -} - -// testBatchSHRDocumentReferenceNumberField validates SHRDocumentReferenceNumberField -// characters 5-15 of underlying IdentificationNumber -func testBatchSHRDocumentReferenceNumberField(t testing.TB) { - mockBatch := mockBatchSHR() - ts := mockBatch.Entries[0].SHRDocumentReferenceNumberField() - if ts != 12345678910 { - t.Error("Document Reference Number is invalid") - } -} - -// TestBatchSHRDocumentReferenceNumberField tests validating SHRDocumentReferenceNumberField -// characters 5-15 of underlying IdentificationNumber -func TestBatchSHRDocumentReferenceNumberField(t *testing.T) { - testBatchSHRDocumentReferenceNumberField(t) -} - -// BenchmarkBatchSHRDocumentReferenceNumberField benchmarks validating SHRDocumentReferenceNumberField -// characters 5-15 of underlying IdentificationNumber -func BenchmarkSHRDocumentReferenceNumberField(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchSHRDocumentReferenceNumberField(b) - } -} - -// testBatchSHRIndividualCardAccountNumberField validates SHRIndividualCardAccountNumberField -// underlying IndividualName -func testBatchSHRIndividualCardAccountNumberField(t testing.TB) { - mockBatch := mockBatchSHR() - ts := mockBatch.Entries[0].SHRIndividualCardAccountNumberField() - if ts != 12345678910123456 { - t.Error("Individual Card Account Number is invalid") - } -} - -// TestBatchSHRIndividualCardAccountNumberField tests validating SHRIndividualCardAccountNumberField -// characters 5-15 of underlying IndividualName -func TestBatchSHRIndividualCardAccountNumberField(t *testing.T) { - testBatchSHRIndividualCardAccountNumberField(t) -} - -// BenchmarkBatchSHRIndividualCardAccountNumberField benchmarks validating SHRIndividualCardAccountNumberField -// characters 5-15 of underlying IndividualName -func BenchmarkBatchSHRDocumentReferenceNumberField(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - testBatchSHRIndividualCardAccountNumberField(b) - } -} \ No newline at end of file From 0bc59d8f4a560a3c5c32d52817213f3b1b973c99 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 17:14:24 -0400 Subject: [PATCH 0239/1694] #207 code coverage #207 code coverage --- batchSHR_test.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/batchSHR_test.go b/batchSHR_test.go index 7b0e3f83b..81c660abb 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -437,3 +437,78 @@ func BenchmarkBatchSHRCardTransactionType(b *testing.B) { testBatchSHRCardTransactionType(b) } } + +// testBatchSHRCardExpirationDateField validates SHRCardExpirationDate +// characters 0-4 of underlying IdentificationNumber +func testBatchSHRCardExpirationDateField(t testing.TB) { + mockBatch := mockBatchSHR() + ts := mockBatch.Entries[0].SHRCardExpirationDateField() + if ts != "0718" { + t.Error("Card Expiration Date is invalid") + } +} + +// TestBatchSHRCardExpirationDateField tests validatingSHRCardExpirationDate +// characters 0-4 of underlying IdentificationNumber +func TestBatchSHRCardExpirationDateField(t *testing.T) { + testBatchSHRCardExpirationDateField(t) +} + +// BenchmarkBatchSHRCardExpirationDateField benchmarks validating SHRCardExpirationDate +// characters 0-4 of underlying IdentificationNumber +func BenchmarkBatchSHRCardExpirationDateField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRCardExpirationDateField(b) + } +} + +// testBatchSHRDocumentReferenceNumberField validates SHRDocumentReferenceNumberField +// characters 5-15 of underlying IdentificationNumber +func testBatchSHRDocumentReferenceNumberField(t testing.TB) { + mockBatch := mockBatchSHR() + ts := mockBatch.Entries[0].SHRDocumentReferenceNumberField() + if ts != 12345678910 { + t.Error("Document Reference Number is invalid") + } +} + +// TestBatchSHRDocumentReferenceNumberField tests validating SHRDocumentReferenceNumberField +// characters 5-15 of underlying IdentificationNumber +func TestBatchSHRDocumentReferenceNumberField(t *testing.T) { + testBatchSHRDocumentReferenceNumberField(t) +} + +// BenchmarkBatchSHRDocumentReferenceNumberField benchmarks validating SHRDocumentReferenceNumberField +// characters 5-15 of underlying IdentificationNumber +func BenchmarkSHRDocumentReferenceNumberField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRDocumentReferenceNumberField(b) + } +} + +// testBatchSHRIndividualCardAccountNumberField validates SHRIndividualCardAccountNumberField +// underlying IndividualName +func testBatchSHRIndividualCardAccountNumberField(t testing.TB) { + mockBatch := mockBatchSHR() + ts := mockBatch.Entries[0].SHRIndividualCardAccountNumberField() + if ts != 12345678910123456 { + t.Error("Individual Card Account Number is invalid") + } +} + +// TestBatchSHRIndividualCardAccountNumberField tests validating SHRIndividualCardAccountNumberField +// underlying IndividualName +func TestBatchSHRIndividualCardAccountNumberField(t *testing.T) { + testBatchSHRIndividualCardAccountNumberField(t) +} + +// BenchmarkBatchSHRIndividualCardAccountNumberField benchmarks validating SHRIndividualCardAccountNumberField +// underlying IndividualName +func BenchmarkBatchSHRDocumentReferenceNumberField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchSHRIndividualCardAccountNumberField(b) + } +} From a95955553a3eb55e7e63003adc4d008d6873f16c Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 18 Jun 2018 15:57:11 -0600 Subject: [PATCH 0240/1694] OpenAPI specification Create initital endpoint mapping in OpenAPI for working with a File --- server/openapi.yaml | 301 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 server/openapi.yaml diff --git a/server/openapi.yaml b/server/openapi.yaml new file mode 100644 index 000000000..1c2fe534c --- /dev/null +++ b/server/openapi.yaml @@ -0,0 +1,301 @@ +openapi: "3.0.0" +info: + description: Moov ACH is a RESTful enpoint of an ACH file creator to enable developers of all languages and read, write, and validate NACHA files. + version: "0.0.1" + title: "Moov ACH" + contact: + email: "apiteam@moov.io" + license: + name: "Apache 2.0" + url: "http://www.apache.org/licenses/LICENSE-2.0.html" +servers: +- url: https://api.moov.io/v1/ + description: Production server +- url: https://sandbox.moov.io/v1/ + description: Development server. +tags: + - name: Files + description: File contains the structures of a ACH File. It contains one and only one File Header and File Control with at least one Batches. + - name: Batches + description: Batch objects hold the Batch Header and Batch Control and all Entry Records and Addenda records for the Batch. + +paths: +#FILES + /files: + get: + tags: + - Files + summary: Gets a list of Files + operationId: getFiles + responses: + '200': + description: A list of File objects + headers: + X-Total-Count: + description: The total number of Originators + schema: + type: integer + content: + application/json: + schema: + $ref: '#/components/schemas/Files' + post: + tags: + - Files + summary: Create a new File object + operationId: addFile + requestBody: + $ref: '#/components/requestBodies/File' + responses: + '201': + description: A JSON object containing a new File + headers: + Location: + description: The location of the new resource + schema: + type: string + format: uri + content: + application/json: + schema: + $ref: '#/components/schemas/File' + '400': + description: "Invalid File Header Object" + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /files/{file_id}: + get: + tags: + - Files + summary: Retrieves the details of an existing File. You need only supply the unique File identifier that was returned upon creation. + operationId: getFileByID + parameters: + - $ref: '#/components/parameters/requestID' + - name: file_id + in: path + description: File ID + required: true + schema: + type: string + format: uuid + example: 3f2d23ee-68d9-494e-a23e-cd0a9ce09281 + responses: + '200': + description: A File object for the supplied ID + content: + application/json: + schema: + $ref: '#/components/schemas/File' + '404': + description: A resource with the specified ID was not found + post: + tags: + - Files + summary: Updates the specified File Header by setting the values of the parameters passed. Any parameters not provided will be left unchanged. + operationId: updateFile + parameters: + - name: file_id + in: path + description: File ID + required: true + schema: + type: string + format: uuid + example: 3f2d23ee-68d9-494e-a23e-cd0a9ce09281 + requestBody: + $ref: '#/components/requestBodies/File' + responses: + '201': + description: A JSON object containing a new File + headers: + Location: + description: The location of the new resource + schema: + type: string + format: uri + content: + application/json: + schema: + $ref: '#/components/schemas/File' + '400': + description: "Invalid File Header Object" + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - Files + summary: Permanently deletes a File and associated Batches. It cannot be undone. + parameters: + - name: file_id + in: path + description: File ID + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Permanently deleted File. + content: + application/json: + schema: + type: string + format: uuid + example: 3f2d23ee-68d9-494e-a23e-cd0a9ce09281 + '404': + description: A File with the specified ID was not found. +# COMPONENTS to be re-used +components: + schemas: + File: + properties: + id: + type: string + format: uuid + description: File ID + example: 724b6abe-e39d-4727-bf66-b499faf06938 + FileHeader: + $ref: '#/components/schemas/FileHeader' + Files: + type: array + items: + $ref: '#/components/schemas/File' + FileHeader: + required: + - immediateOrigin + - immediateOriginName + - immediateDestination + - immediateDestinationName + properties: + id: + type: string + format: uuid + description: File Header ID (same as File) + example: 724b6abe-e39d-4727-bf66-b499faf06938 + immediateOrigin: + type: string + description: contains the Routing Number of the ACH Operator or sending point that is sending the file. + minLength: 9 + maxLength: 10 + example: "99991234" + immediateOriginName: + type: string + description: The name of the ACH operator or sending point that is sending the file. + maxLength: 23 + example: My Bank Name + immediateDestination: + type: string + maxLength: 10 + minLength: 9 + example: "69100013" + description: contains the Routing Number of the ACH Operator or receiving point to which the file is being sent + immediateDestinationName: + type: string + description: The name of the ACH or receiving point for which that file is destined. + maxLength: 23 + example: Federal Reserve Bank + fileCreationDate: + type: string + description: expressed in a "YYMMDD" format. The File Creation Date is the date on which the file is prepared by an ODFI + example: "102318" + minLength: 6 + maxLength: 6 + fileCreationTime: + type: string + description: Expressed in "HHMM" (24 hour clock) format. + example: "1601" + minLength: 4 + maxLength: 4 + FileControl: + properties: + id: + type: string + format: uuid + description: File Control ID (same as File) + + + Error: + required: + - code + - message + properties: + code: + type: string + description: A detailed error code + example: ValidationError + message: + type: string + description: A human readable description of the problem + example: Validation error(s) present. + requestBodies: + File: + description: A JSON object containing a new File + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/FileHeader' + parameters: + offsetParam: + in: query + name: offset + required: false + description: The number of items to skip before starting to collect the result set + schema: + type: integer + minimum: 1 + default: 0 + limitParam: + in: query + name: limit + description: The number of items to return + required: false + schema: + type: integer + minimum: 0 + maximum: 100 + default: 25 + example: 10 + idempotencyKey: + in: header + name: Idempotency-Key + description: Idempotent key in the header which expires after 24/hrs + example: a4f88150-9f5d-4f75-a1b1-0344f268506e + required: false + schema: + type: string + format: uuid + requestID: + in: header + name: Request_Id + description: Optional Request ID allows application developer to tracce requests through the systems logs + example: r4f99150-8f5d-5f75-z1b1-9344f268506f + schema: + type: string + format: uuid + startDate: + in: query + name: startDate + description: Filter objects created after this date. ISO-8601 format YYYY-MM-DD. Can optionally be used with endDate to specify a date range. + schema: + type: string + format: date-time + endDate: + in: query + name: endDate + description: Filter objects created before this date. ISO-8601 format YYYY-MM-DD. Can optionally be used with startDate to specify a date range. + schema: + type: string + format: date-time + expand: + in: query + name: expand + required: false + description: Return nested objects rather than ID's in the response body. + example: depository + schema: + type: string \ No newline at end of file From 6039a341787b5126ae58ceffefbbb574839395ef Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 18 Jun 2018 18:04:02 -0400 Subject: [PATCH 0241/1694] #207 MMYY validator #207 MMYY validator --- batchSHR.go | 9 ++++++++ batchSHR_test.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ validators.go | 16 ++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/batchSHR.go b/batchSHR.go index 0820c4323..51b4adbd8 100644 --- a/batchSHR.go +++ b/batchSHR.go @@ -65,6 +65,15 @@ func (batch *BatchSHR) Validate() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CardTransactionType", Msg: msg} } + // CardExpirationDate BatchSHR ACH File format is MMYY. Validate MM is 01-12. + if err := entry.isMonth(entry.parseStringField(entry.SHRCardExpirationDateField()[0:2])); err != nil { + return &FieldError{FieldName: "CardExpirationDate", Value: entry.parseStringField(entry.SHRCardExpirationDateField()[0:2]), Msg: msgValidMonth} + } + + if err := entry.isYear(entry.parseStringField(entry.SHRCardExpirationDateField()[2:4])); err != nil { + return &FieldError{FieldName: "CardExpirationDate", Value: entry.parseStringField(entry.SHRCardExpirationDateField()[2:4]), Msg: msgValidYear} + } + // Addenda validations - SHR Addenda must be Addenda02 // Addendum must be equal to 1 diff --git a/batchSHR_test.go b/batchSHR_test.go index 81c660abb..021e95721 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -512,3 +512,59 @@ func BenchmarkBatchSHRDocumentReferenceNumberField(b *testing.B) { testBatchSHRIndividualCardAccountNumberField(b) } } + +// testSHRCardExpirationDateMonth validates the month is valid for CardExpirationDate +func testSHRCardExpirationDateMonth(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.GetEntries()[0].SetSHRCardExpirationDate("1306") + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CardExpirationDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestSHRCardExpirationDateMonth tests validating the month is valid for CardExpirationDate +func TestSHRSHRCardExpirationDateMonth(t *testing.T) { + testSHRCardExpirationDateMonth(t) +} + +// BenchmarkSHRCardExpirationDateMonth test validating the month is valid for CardExpirationDate +func BenchmarkSHRCardExpirationDateMonth(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testSHRCardExpirationDateMonth(b) + } +} + +// testSHRCardExpirationDateYear validates the year is valid for CardExpirationDate +func testSHRCardExpirationDateYear(t testing.TB) { + mockBatch := mockBatchSHR() + mockBatch.GetEntries()[0].SetSHRCardExpirationDate("0612") + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CardExpirationDate" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestSHRCardExpirationDateYear tests validating the year is valid for CardExpirationDate +func TestSHRSHRCardExpirationDateYear(t *testing.T) { + testSHRCardExpirationDateYear(t) +} + +// BenchmarkSHRCardExpirationDateYear test validating the year is valid for CardExpirationDate +func BenchmarkSHRCardExpirationDateYear(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testSHRCardExpirationDateYear(b) + } +} diff --git a/validators.go b/validators.go index ca13e000c..a88a9f7ea 100644 --- a/validators.go +++ b/validators.go @@ -30,6 +30,7 @@ var ( msgCardTransactionType = "is an invalid Card Transaction Type" msgValidMonth = "is an invalid month" msgValidDay = "is an invalid day" + msgValidYear = "is an invalid year" ) // validator is common validation and formatting of golang types to ach type strings @@ -75,6 +76,21 @@ func (v *validator) isCardTransactionType(code string) error { return errors.New(msgCardTransactionType) } +// isYear validates a 2 digit year 18-50 (2018 - 2050) +// ToDo: Add/remove more years as card expiration dates need/don't need them +func (v *validator) isYear(s string) error { + switch s { + case + "18", "19", + "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", + "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", + "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", + "50": + return nil + } + return errors.New(msgValidYear) +} + // isMonth validates a 2 digit month 01-12 func (v *validator) isMonth(s string) error { switch s { From 47c86d5cea784517b5fb0399df374b2e181420cd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 19 Jun 2018 09:50:37 -0400 Subject: [PATCH 0242/1694] #207 batchSHR #207 batchSHR --- addenda98.go | 2 +- addenda99.go | 2 +- batchControl.go | 8 ++++---- batchHeader.go | 2 +- batchSHR_test.go | 8 ++++---- converters.go | 4 ++-- converters_test.go | 6 +++--- entryDetail.go | 24 ++++++++++++------------ fileHeader.go | 6 +++--- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/addenda98.go b/addenda98.go index 6577e8ff6..017d82925 100644 --- a/addenda98.go +++ b/addenda98.go @@ -136,7 +136,7 @@ func (addenda98 *Addenda98) OriginalTraceField() string { // OriginalDFIField returns a zero padded OriginalDFI string func (addenda98 *Addenda98) OriginalDFIField() string { - return addenda98.stringRTNField(addenda98.OriginalDFI, 8) + return addenda98.stringField(addenda98.OriginalDFI, 8) } //CorrectedDataField returns a space padded CorrectedData string diff --git a/addenda99.go b/addenda99.go index 7c9083119..676cfbbf3 100644 --- a/addenda99.go +++ b/addenda99.go @@ -149,7 +149,7 @@ func (Addenda99 *Addenda99) DateOfDeathField() string { // OriginalDFIField returns a zero padded OriginalDFI string func (Addenda99 *Addenda99) OriginalDFIField() string { - return Addenda99.stringRTNField(Addenda99.OriginalDFI, 8) + return Addenda99.stringField(Addenda99.OriginalDFI, 8) } //AddendaInformationField returns a space padded AddendaInformation string diff --git a/batchControl.go b/batchControl.go index 16e0ead06..077e23666 100644 --- a/batchControl.go +++ b/batchControl.go @@ -36,7 +36,7 @@ type BatchControl struct { TotalDebitEntryDollarAmount int `json:"totalDebit"` // TotalCreditEntryDollarAmount Contains accumulated Entry credit totals within the batch. TotalCreditEntryDollarAmount int `json:"totalCredit"` - // CompanyIdentification is an alphameric code used to identify an Originator + // CompanyIdentification is an alphanumeric code used to identify an Originator // The Company Identification Field must be included on all // prenotification records and on each entry initiated pursuant to such // prenotification. The Company ID may begin with the ANSI one-digit @@ -174,7 +174,7 @@ func (bc *BatchControl) EntryHashField() string { return bc.numericField(bc.EntryHash, 10) } -//TotalDebitEntryDollarAmountField get a zero padded Debity Entry Amount +//TotalDebitEntryDollarAmountField get a zero padded Debit Entry Amount func (bc *BatchControl) TotalDebitEntryDollarAmountField() string { return bc.numericField(bc.TotalDebitEntryDollarAmount, 12) } @@ -184,7 +184,7 @@ func (bc *BatchControl) TotalCreditEntryDollarAmountField() string { return bc.numericField(bc.TotalCreditEntryDollarAmount, 12) } -// CompanyIdentificationField get the CompanyIdentification righ padded +// CompanyIdentificationField get the CompanyIdentification right padded func (bc *BatchControl) CompanyIdentificationField() string { return bc.alphaField(bc.CompanyIdentification, 10) } @@ -196,7 +196,7 @@ func (bc *BatchControl) MessageAuthenticationCodeField() string { // ODFIIdentificationField get the odfi number zero padded func (bc *BatchControl) ODFIIdentificationField() string { - return bc.stringRTNField(bc.ODFIIdentification, 8) + return bc.stringField(bc.ODFIIdentification, 8) } // BatchNumberField gets a string of the batch number zero padded diff --git a/batchHeader.go b/batchHeader.go index 58d48330a..31fbd1947 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -266,7 +266,7 @@ func (bh *BatchHeader) EffectiveEntryDateField() string { // ODFIIdentificationField get the odfi number zero padded func (bh *BatchHeader) ODFIIdentificationField() string { - return bh.stringRTNField(bh.ODFIIdentification, 8) + return bh.stringField(bh.ODFIIdentification, 8) } // BatchNumberField get the batch number zero padded diff --git a/batchSHR_test.go b/batchSHR_test.go index 021e95721..c41307e58 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -28,8 +28,8 @@ func mockSHREntryDetail() *EntryDetail { entry.DFIAccountNumber = "744-5678-99" entry.Amount = 25000 entry.SetSHRCardExpirationDate("0718") - entry.SetSHRDocumentReferenceNumber(12345678910) - entry.SetSHRIndividualCardAccountNumber(12345678910123456) + entry.SetSHRDocumentReferenceNumber("12345678910") + entry.SetSHRIndividualCardAccountNumber("1234567891123456789") entry.SetTraceNumber(mockBatchSHRHeader().ODFIIdentification, 123) entry.DiscretionaryData = "01" entry.Category = CategoryForward @@ -468,7 +468,7 @@ func BenchmarkBatchSHRCardExpirationDateField(b *testing.B) { func testBatchSHRDocumentReferenceNumberField(t testing.TB) { mockBatch := mockBatchSHR() ts := mockBatch.Entries[0].SHRDocumentReferenceNumberField() - if ts != 12345678910 { + if ts != "12345678910" { t.Error("Document Reference Number is invalid") } } @@ -493,7 +493,7 @@ func BenchmarkSHRDocumentReferenceNumberField(b *testing.B) { func testBatchSHRIndividualCardAccountNumberField(t testing.TB) { mockBatch := mockBatchSHR() ts := mockBatch.Entries[0].SHRIndividualCardAccountNumberField() - if ts != 12345678910123456 { + if ts != "0001234567891123456789" { t.Error("Individual Card Account Number is invalid") } } diff --git a/converters.go b/converters.go index dfd451fc2..1000fd1cc 100644 --- a/converters.go +++ b/converters.go @@ -66,8 +66,8 @@ func (c *converters) numericField(n int, max uint) string { return s } -// stringRTNField slices to max length and zero filled -func (c *converters) stringRTNField(s string, max uint) string { +// stringField slices to max length and zero filled +func (c *converters) stringField(s string, max uint) string { ln := uint(len(s)) if ln > max { return s[:max] diff --git a/converters_test.go b/converters_test.go index 2627d580e..3e3dc5e84 100644 --- a/converters_test.go +++ b/converters_test.go @@ -141,7 +141,7 @@ func BenchmarkParseStringField(b *testing.B) { // testRTNFieldShort ensures zero padding and right justified func testRTNFieldShort(t testing.TB) { c := converters{} - result := c.stringRTNField("123456", 8) + result := c.stringField("123456", 8) if result != "00123456" { t.Errorf("Zero padding 8 character string : '%v'", result) } @@ -163,7 +163,7 @@ func BenchmarkRTNFieldShort(b *testing.B) { // testRTNFieldLong ensures sliced to max length func testRTNFieldLong(t testing.TB) { c := converters{} - result := c.stringRTNField("1234567899", 8) + result := c.stringField("1234567899", 8) if result != "12345678" { t.Errorf("first 8 character string: '%v'", result) } @@ -185,7 +185,7 @@ func BenchmarkRTNFieldLong(b *testing.B) { // testRTNFieldExact ensures exact match func testRTNFieldExact(t testing.TB) { c := converters{} - result := c.stringRTNField("123456789", 9) + result := c.stringField("123456789", 9) if result != "123456789" { t.Errorf("first 9 character string: '%v'", result) } diff --git a/entryDetail.go b/entryDetail.go index f4142d167..cb2bea595 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -245,7 +245,7 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { // SetRDFI takes the 9 digit RDFI account number and separates it for RDFIIdentification and CheckDigit func (ed *EntryDetail) SetRDFI(rdfi string) *EntryDetail { - s := ed.stringRTNField(rdfi, 9) + s := ed.stringField(rdfi, 9) ed.RDFIIdentification = ed.parseStringField(s[:8]) ed.CheckDigit = ed.parseStringField(s[8:9]) return ed @@ -253,13 +253,13 @@ func (ed *EntryDetail) SetRDFI(rdfi string) *EntryDetail { // SetTraceNumber takes first 8 digits of ODFI and concatenates a sequence number onto the TraceNumber func (ed *EntryDetail) SetTraceNumber(ODFIIdentification string, seq int) { - trace := ed.stringRTNField(ODFIIdentification, 8) + ed.numericField(seq, 7) + trace := ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7) ed.TraceNumber = ed.parseNumField(trace) } // RDFIIdentificationField get the rdfiIdentification with zero padding func (ed *EntryDetail) RDFIIdentificationField() string { - return ed.stringRTNField(ed.RDFIIdentification, 8) + return ed.stringField(ed.RDFIIdentification, 8) } // DFIAccountNumberField gets the DFIAccountNumber with space padding @@ -336,15 +336,15 @@ func (ed *EntryDetail) SetSHRCardExpirationDate(s string) { // SetSHRDocumentReferenceNumber format int is used in SHR, characters 5-15 of underlying // IdentificationNumber -func (ed *EntryDetail) SetSHRDocumentReferenceNumber(i int) { - ed.IdentificationNumber = ed.IdentificationNumber + ed.numericField(i, 11) +func (ed *EntryDetail) SetSHRDocumentReferenceNumber(s string) { + ed.IdentificationNumber = ed.IdentificationNumber + ed.stringField(s, 11) } // SetSHRIndividualCardAccountNumber format int is used in SHR, underlying // IndividualName -// TODO: Overflow for int -func (ed *EntryDetail) SetSHRIndividualCardAccountNumber(i int) { - ed.IndividualName = ed.numericField(i, 22) + +func (ed *EntryDetail) SetSHRIndividualCardAccountNumber(s string) { + ed.IndividualName = ed.stringField(s, 22) } // SHRCardExpirationDateField format MMYY is used in SHR, characters 1-4 of underlying @@ -355,14 +355,14 @@ func (ed *EntryDetail) SHRCardExpirationDateField() string { // SHRDocumentReferenceNumberField format int is used in SHR, characters 5-15 of underlying // IdentificationNumber -func (ed *EntryDetail) SHRDocumentReferenceNumberField() int { - return ed.parseNumField(ed.IdentificationNumber[4:15]) +func (ed *EntryDetail) SHRDocumentReferenceNumberField() string { + return ed.stringField(ed.IdentificationNumber[4:15], 11) } // SHRIndividualCardAccountNumberField format int is used in SHR, underlying // IndividualName -func (ed *EntryDetail) SHRIndividualCardAccountNumberField() int { - return ed.parseNumField(ed.IndividualName) +func (ed *EntryDetail) SHRIndividualCardAccountNumberField() string { + return ed.stringField(ed.IndividualName, 22) } // IndividualNameField returns a space padded string of IndividualName diff --git a/fileHeader.go b/fileHeader.go index cf01b2fde..f5fec7d8c 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -116,7 +116,7 @@ func (fh *FileHeader) Parse(record string) { // (14-23) A 10-digit number assigned to you by the ODFI once they approve you to originate ACH files through them fh.ImmediateOrigin = fh.parseStringField(record[13:23]) // 24-29 Today's date in YYMMDD format - // must be after todays date. + // must be after today's date. fh.FileCreationDate = fh.parseSimpleDate(record[23:29]) // 30-33 The current time in HHMM format fh.FileCreationTime = fh.parseSimpleTime(record[29:33]) @@ -240,12 +240,12 @@ func (fh *FileHeader) fieldInclusion() error { // ImmediateDestinationField gets the immediate destination number with zero padding func (fh *FileHeader) ImmediateDestinationField() string { - return " " + fh.stringRTNField(fh.ImmediateDestination, 9) + return " " + fh.stringField(fh.ImmediateDestination, 9) } // ImmediateOriginField gets the immediate origin number with 0 padding func (fh *FileHeader) ImmediateOriginField() string { - return " " + fh.stringRTNField(fh.ImmediateOrigin, 9) + return " " + fh.stringField(fh.ImmediateOrigin, 9) } // FileCreationDateField gets the file creation date in YYMMDD format From 394080b638fca35c451a2fc405a4a8a539213ba6 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 19 Jun 2018 09:57:02 -0400 Subject: [PATCH 0243/1694] # 207 golint SetSHRIndividualCardAccountNumber # 207 golint SetSHRIndividualCardAccountNumber --- entryDetail.go | 1 - 1 file changed, 1 deletion(-) diff --git a/entryDetail.go b/entryDetail.go index cb2bea595..8ff8a4ed8 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -342,7 +342,6 @@ func (ed *EntryDetail) SetSHRDocumentReferenceNumber(s string) { // SetSHRIndividualCardAccountNumber format int is used in SHR, underlying // IndividualName - func (ed *EntryDetail) SetSHRIndividualCardAccountNumber(s string) { ed.IndividualName = ed.stringField(s, 22) } From 7b418dd40ae65a5bc5b6b35a13c594b0abc08c63 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 19 Jun 2018 10:13:38 -0400 Subject: [PATCH 0244/1694] # 207 test coverage writer_test.go # 207 test coverage writer_test.go --- writer_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/writer_test.go b/writer_test.go index d41beb22e..fd08657e7 100644 --- a/writer_test.go +++ b/writer_test.go @@ -49,10 +49,57 @@ func TestPPDWrite(t *testing.T) { testPPDWrite(t) } -// BenchmarkPPDWrite benchmarks writing a PPD ACH file +// BenchmarkPPDWrite benchmarks validating writing a PPD ACH file func BenchmarkPPDWrite(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { testPPDWrite(b) } } + +// testFileWriteErr validates error for file write +func testFileWriteErr(t testing.TB) { + file := NewFile().SetHeader(mockFileHeader()) + entry := mockEntryDetail() + entry.AddAddenda(mockAddenda05()) + batch := NewBatchPPD(mockBatchPPDHeader()) + batch.SetHeader(mockBatchHeader()) + batch.AddEntry(entry) + batch.Create() + file.AddBatch(batch) + + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + file.Batches[0].GetControl().EntryAddendaCount = 10 + + b := &bytes.Buffer{} + f := NewWriter(b) + + if err := f.WriteAll([]*File{file}); err != nil { + if e, ok := err.(*FileError); ok { + if e.FieldName != "EntryAddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestFileWriteErr tests validating error for file write +func TestFileWriteErr(t *testing.T) { + testFileWriteErr(t) +} + +// BenchmarkFileWriteErr benchmarks error for file write +func BenchmarkFileWriteErr(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileWriteErr(b) + } +} From 9cc0312ff9b992d4f2bced8eca1d54d7342e8e76 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 19 Jun 2018 10:54:18 -0400 Subject: [PATCH 0245/1694] # 207 Update test and README Update SHRCardExpirationDate Update ReadME --- README.md | 3 ++- batchSHR_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 353b4f402..26025e9d7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Package 'moov-io/ach' implements a file reader and writer for parsing [ACH](http ACH is under active development but already in production for multiple companies. Please star the project if you are interested in its progress. * Library currently supports the reading and writing - * ARC (Accounts Receivable Entry + * ARC (Accounts Receivable Entry) * BOC (Back Office Conversion) * CCD (Corporate credit or debit) * COR (Automated Notification of Change(NOC)) @@ -24,6 +24,7 @@ ACH is under active development but already in production for multiple companies * POS (Point of Sale) * PPD (Prearranged payment and deposits) * RCK (Represented Check Entries) + * SHR (Shared Network Entry) * TEL (Telephone-Initiated Entry) * WEB (Internet-initiated Entries) * Return Entries diff --git a/batchSHR_test.go b/batchSHR_test.go index c41307e58..52ea69ddc 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -27,7 +27,7 @@ func mockSHREntryDetail() *EntryDetail { entry.SetRDFI("231380104") entry.DFIAccountNumber = "744-5678-99" entry.Amount = 25000 - entry.SetSHRCardExpirationDate("0718") + entry.SetSHRCardExpirationDate("0722") entry.SetSHRDocumentReferenceNumber("12345678910") entry.SetSHRIndividualCardAccountNumber("1234567891123456789") entry.SetTraceNumber(mockBatchSHRHeader().ODFIIdentification, 123) From ae041317a70465989d7dcda8dfa7fbd2579b61a3 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 19 Jun 2018 11:47:13 -0400 Subject: [PATCH 0246/1694] #207 Add write and read SHR Test Add write and read SHR Test --- batchSHR_test.go | 2 +- test/ach-shr-read/main.go | 35 ++++++++++++++ test/ach-shr-read/main_test.go | 7 +++ test/ach-shr-read/shr-debit.ach | 10 ++++ test/ach-shr-write/main.go | 81 +++++++++++++++++++++++++++++++++ test/ach-shr-write/main_test.go | 7 +++ 6 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 test/ach-shr-read/main.go create mode 100644 test/ach-shr-read/main_test.go create mode 100644 test/ach-shr-read/shr-debit.ach create mode 100644 test/ach-shr-write/main.go create mode 100644 test/ach-shr-write/main_test.go diff --git a/batchSHR_test.go b/batchSHR_test.go index 52ea69ddc..ac9bb1db6 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -443,7 +443,7 @@ func BenchmarkBatchSHRCardTransactionType(b *testing.B) { func testBatchSHRCardExpirationDateField(t testing.TB) { mockBatch := mockBatchSHR() ts := mockBatch.Entries[0].SHRCardExpirationDateField() - if ts != "0718" { + if ts != "0722" { t.Error("Card Expiration Date is invalid") } } diff --git a/test/ach-shr-read/main.go b/test/ach-shr-read/main.go new file mode 100644 index 000000000..5fe7ccb8d --- /dev/null +++ b/test/ach-shr-read/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "github.com/moov-io/ach" + "log" + "os" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("shr-debit.ach") + if err != nil { + log.Fatal(err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("SHR Card Expiration Date: %v \n", achFile.Batches[0].GetEntries()[0].SHRCardExpirationDateField()) + fmt.Printf("SHR Document Reference Number: %v \n", achFile.Batches[0].GetEntries()[0].SHRDocumentReferenceNumberField()) + fmt.Printf("SHR Individual Card Account Number: %v \n", achFile.Batches[0].GetEntries()[0].SHRIndividualCardAccountNumberField()) +} diff --git a/test/ach-shr-read/main_test.go b/test/ach-shr-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-shr-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-shr-read/shr-debit.ach b/test/ach-shr-read/shr-debit.ach new file mode 100644 index 000000000..b756514cc --- /dev/null +++ b/test/ach-shr-read/shr-debit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821806190000A094101Federal Reserve Bank My Bank Name +5225Name on Account 121042882 SHRPayment 180620 0121042880000001 +62723138010412345678 01000000000722123456789100001234567891123456789011121042880000001 +702REFONEAREFTERM021000490614123456Target Store 0049 PHILADELPHIA PA121042880000001 +82250000020023138010000100000000000000000000121042882 121042880000001 +9000001000001000000020023138010000100000000000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-shr-write/main.go b/test/ach-shr-write/main.go new file mode 100644 index 000000000..2e42bc9a4 --- /dev/null +++ b/test/ach-shr-write/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "github.com/moov-io/ach" + "log" + "os" + "time" +) + +func main() { + // Example transfer to write an ACH SHR file to send/credit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + // Set originator bank ODFI and destination Operator for the financial institution + // this is the funding/receiving source of the transfer + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "SHR" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "Payment" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 22: Debit (withdrawal) from checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.SetSHRCardExpirationDate("0722") + entry.SetSHRDocumentReferenceNumber("12345678910") + entry.SetSHRIndividualCardAccountNumber("1234567891123456789") + entry.DiscretionaryData = "01" + + addenda02 := ach.NewAddenda02() + addenda02.ReferenceInformationOne = "REFONEA" + addenda02.ReferenceInformationTwo = "REF" + addenda02.TerminalIdentificationCode = "TERM02" + addenda02.TransactionSerialNumber = "100049" + addenda02.TransactionDate = "0614" + addenda02.AuthorizationCodeOrExpireDate = "123456" + addenda02.TerminalLocation = "Target Store 0049" + addenda02.TerminalCity = "PHILADELPHIA" + addenda02.TerminalState = "PA" + addenda02.TraceNumber = 121042880000001 + + // build the batch + batch := ach.NewBatchSHR(bh) + batch.AddEntry(entry) + batch.GetEntries()[0].AddAddenda(addenda02) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.WriteAll([]*ach.File{file}); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/test/ach-shr-write/main_test.go b/test/ach-shr-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-shr-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From 0d3b5e912c6c245105843a63609a0bd5d4c8380a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 19 Jun 2018 15:35:35 -0400 Subject: [PATCH 0247/1694] #209 Support for SEC Code CIE #209 Support for SEC Code CIE --- README.md | 1 + batch.go | 2 + batchCIE.go | 100 +++++++++++ batchCIE_test.go | 436 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 539 insertions(+) create mode 100644 batchCIE.go create mode 100644 batchCIE_test.go diff --git a/README.md b/README.md index 26025e9d7..b509942ea 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ ACH is under active development but already in production for multiple companies * ARC (Accounts Receivable Entry) * BOC (Back Office Conversion) * CCD (Corporate credit or debit) + * CIE (Customer-Initiated Entry) * COR (Automated Notification of Change(NOC)) * POP (Point of Purchase) * POS (Point of Sale) diff --git a/batch.go b/batch.go index 439305aef..c961c76eb 100644 --- a/batch.go +++ b/batch.go @@ -33,6 +33,8 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { return NewBatchBOC(bh), nil case "CCD": return NewBatchCCD(bh), nil + case "CIE": + return NewBatchCIE(bh), nil case "COR": return NewBatchCOR(bh), nil case "POP": diff --git a/batchCIE.go b/batchCIE.go new file mode 100644 index 000000000..2bdbaf8aa --- /dev/null +++ b/batchCIE.go @@ -0,0 +1,100 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// BatchCIE holds the BatchHeader and BatchControl and all EntryDetail for CIE Entries. +// +// Customer-Initiated Entry (or CIE entry) is a credit entry initiated on behalf of, +// and upon the instruction of, a consumer to transfer funds to a non-consumer Receiver. +// CIE entries are usually transmitted to a company for payment of funds that the consumer +// owes to that company and are initiated by the consumer through some type of online +// banking product or bill payment service provider. With CIEs, funds owed by the consumer +// are “pushed” to the biller in the form of an ACH credit, as opposed to the biller’s use of +// a debit application (e.g., PPD, WEB) to “pull” the funds from a customer’s account. +type BatchCIE struct { + batch +} + +var msgBatchCIEAddenda = "found and 1 Addenda05 is the maximum for SEC code CIE" +var msgBatchCIEAddendaType = "%T found where Addenda05 is required for SEC code CIE" + +// NewBatchCIE returns a *BatchCIE +func NewBatchCIE(bh *BatchHeader) *BatchCIE { + batch := new(BatchCIE) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchCIE) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // Add type specific validation. + + if batch.Header.StandardEntryClassCode != "CIE" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "CIE") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + // CIE detail entries can only be a debit, ServiceClassCode must allow debits + switch batch.Header.ServiceClassCode { + case 200, 225, 280: + msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "CIE") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg} + } + + for _, entry := range batch.Entries { + // CIE detail entries must be a debit + if entry.CreditOrDebit() != "C" { + msg := fmt.Sprintf(msgBatchTransactionCodeCredit, entry.TransactionCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} + } + + // Addenda validations - CIE Addenda must be Addenda05 + + // Addendum must be equal to 1 + if len(entry.Addendum) > 1 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchCIEAddenda} + } + + if len(entry.Addendum) > 0 { + // Addenda type assertion must be Addenda05 + addenda05, ok := entry.Addendum[0].(*Addenda05) + if !ok { + msg := fmt.Sprintf(msgBatchCIEAddendaType, entry.Addendum[0]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} + } + + // Addenda05 must be Validated + if err := addenda05.Validate(); err != nil { + // convert the field error in to a batch error for a consistent api + if e, ok := err.(*FieldError); ok { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} + } + } + } + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchCIE) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + return batch.Validate() +} diff --git a/batchCIE_test.go b/batchCIE_test.go new file mode 100644 index 000000000..734281912 --- /dev/null +++ b/batchCIE_test.go @@ -0,0 +1,436 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +// mockBatchCIEHeader creates a BatchCIE BatchHeader +func mockBatchCIEHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "CIE" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH CIE" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockCIEEntryDetail creates a BatchCIE EntryDetail +func mockCIEEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.IndividualName = "Wade Arnold" + entry.SetTraceNumber(mockBatchCIEHeader().ODFIIdentification, 1) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + return entry +} + +// mockBatchCIE creates a BatchCIE +func mockBatchCIE() *BatchCIE { + mockBatch := NewBatchCIE(mockBatchCIEHeader()) + mockBatch.AddEntry(mockCIEEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +// testBatchCIEHeader creates a BatchCIE BatchHeader +func testBatchCIEHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchCIEHeader()) + err, ok := batch.(*BatchCIE) + if !ok { + t.Errorf("Expecting BatchCIE got %T", err) + } +} + +// TestBatchCIEHeader tests validating BatchCIE BatchHeader +func TestBatchCIEHeader(t *testing.T) { + testBatchCIEHeader(t) +} + +// BenchmarkBatchCIEHeader benchmarks validating BatchCIE BatchHeader +func BenchmarkBatchCIEHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEHeader(b) + } +} + +// testBatchCIECreate validates BatchCIE create +func testBatchCIECreate(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchCIECreate tests validating BatchCIE create +func TestBatchCIECreate(t *testing.T) { + testBatchCIECreate(t) +} + +// BenchmarkBatchCIECreate benchmarks validating BatchCIE create +func BenchmarkBatchCIECreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIECreate(b) + } +} + +// testBatchCIEStandardEntryClassCode validates BatchCIE create for an invalid StandardEntryClassCode +func testBatchCIEStandardEntryClassCode(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEStandardEntryClassCode tests validating BatchCIE create for an invalid StandardEntryClassCode +func TestBatchCIEStandardEntryClassCode(t *testing.T) { + testBatchCIEStandardEntryClassCode(t) +} + +// BenchmarkBatchCIEStandardEntryClassCode benchmarks validating BatchCIE create for an invalid StandardEntryClassCode +func BenchmarkBatchCIEStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEStandardEntryClassCode(b) + } +} + +// testBatchCIEServiceClassCodeEquality validates service class code equality +func testBatchCIEServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEServiceClassCodeEquality tests validating service class code equality +func TestBatchCIEServiceClassCodeEquality(t *testing.T) { + testBatchCIEServiceClassCodeEquality(t) +} + +// BenchmarkBatchCIEServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchCIEServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEServiceClassCodeEquality(b) + } +} + +// testBatchCIEServiceClass200 validates BatchCIE create for an invalid ServiceClassCode 200 +func testBatchCIEServiceClass200(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.Header.ServiceClassCode = 200 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEServiceClass200 tests validating BatchCIE create for an invalid ServiceClassCode 200 +func TestBatchCIEServiceClass200(t *testing.T) { + testBatchCIEServiceClass200(t) +} + +// BenchmarkBatchCIEServiceClass200 benchmarks validating BatchCIE create for an invalid ServiceClassCode 200 +func BenchmarkBatchCIEServiceClass200(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEServiceClass200(b) + } +} + +// testBatchCIEServiceClass225 validates BatchCIE create for an invalid ServiceClassCode 225 +func testBatchCIEServiceClass225(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.Header.ServiceClassCode = 225 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEServiceClass225 tests validating BatchCIE create for an invalid ServiceClassCode 225 +func TestBatchCIEServiceClass225(t *testing.T) { + testBatchCIEServiceClass225(t) +} + +// BenchmarkBatchCIEServiceClass225 benchmarks validating BatchCIE create for an invalid ServiceClassCode 225 +func BenchmarkBatchCIEServiceClass225(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEServiceClass225(b) + } +} + +// testBatchCIEServiceClass280 validates BatchCIE create for an invalid ServiceClassCode 280 +func testBatchCIEServiceClass280(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.Header.ServiceClassCode = 280 + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEServiceClass280 tests validating BatchCIE create for an invalid ServiceClassCode 280 +func TestBatchCIEServiceClass280(t *testing.T) { + testBatchCIEServiceClass280(t) +} + +// BenchmarkBatchCIEServiceClass280 benchmarks validating BatchCIE create for an invalid ServiceClassCode 280 +func BenchmarkBatchCIEServiceClass280(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEServiceClass280(b) + } +} + +// testBatchCIETransactionCode validates BatchCIE TransactionCode is not a debit +func testBatchCIETransactionCode(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.GetEntries()[0].TransactionCode = 27 + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIETransactionCode tests validating BatchCIE TransactionCode is not a credit +func TestBatchCIETransactionCode(t *testing.T) { + testBatchCIETransactionCode(t) +} + +// BenchmarkBatchCIETransactionCode benchmarks validating BatchCIE TransactionCode is not a credit +func BenchmarkBatchCIETransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIETransactionCode(b) + } +} + +// testBatchCIEAddendaCount validates BatchCIE Addendum count of 2 +func testBatchCIEAddendaCount(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEAddendaCount tests validating BatchCIE Addendum count of 2 +func TestBatchCIEAddendaCount(t *testing.T) { + testBatchCIEAddendaCount(t) +} + +// BenchmarkBatchCIEAddendaCount benchmarks validating BatchCIE Addendum count of 2 +func BenchmarkBatchCIEAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEAddendaCount(b) + } +} + +// testBatchCIEAddendaCountZero validates Addendum count of 0 +func testBatchCIEAddendaCountZero(t testing.TB) { + mockBatch := NewBatchCIE(mockBatchCIEHeader()) + mockBatch.AddEntry(mockCIEEntryDetail()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEAddendaCountZero tests validating Addendum count of 0 +func TestBatchCIEAddendaCountZero(t *testing.T) { + testBatchCIEAddendaCountZero(t) +} + +// BenchmarkBatchCIEAddendaCountZero benchmarks validating Addendum count of 0 +func BenchmarkBatchCIEAddendaCountZero(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEAddendaCountZero(b) + } +} + +// testBatchCIEInvalidAddendum validates Addendum must be Addenda05 +func testBatchCIEInvalidAddendum(t testing.TB) { + mockBatch := NewBatchCIE(mockBatchCIEHeader()) + mockBatch.AddEntry(mockCIEEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEInvalidAddendum tests validating Addendum must be Addenda05 +func TestBatchCIEInvalidAddendum(t *testing.T) { + testBatchCIEInvalidAddendum(t) +} + +// BenchmarkBatchCIEInvalidAddendum benchmarks validating Addendum must be Addenda05 +func BenchmarkBatchCIEInvalidAddendum(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEInvalidAddendum(b) + } +} + +// testBatchCIEInvalidAddenda validates Addendum must be Addenda05 with record type 7 +func testBatchCIEInvalidAddenda(t testing.TB) { + mockBatch := NewBatchCIE(mockBatchCIEHeader()) + mockBatch.AddEntry(mockCIEEntryDetail()) + addenda05 := mockAddenda05() + addenda05.recordType = "63" + mockBatch.GetEntries()[0].AddAddenda(addenda05) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEInvalidAddenda tests validating Addendum must be Addenda05 with record type 7 +func TestBatchCIEInvalidAddenda(t *testing.T) { + testBatchCIEInvalidAddenda(t) +} + +// BenchmarkBatchCIEInvalidAddenda benchmarks validating Addendum must be Addenda05 with record type 7 +func BenchmarkBatchCIEInvalidAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEInvalidAddenda(b) + } +} + +// ToDo: Using a FieldError may need to add a BatchError and use *BatchError + +// testBatchCIEInvalidBuild validates an invalid batch build +func testBatchCIEInvalidBuild(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIEInvalidBuild tests validating an invalid batch build +func TestBatchCIEInvalidBuild(t *testing.T) { + testBatchCIEInvalidBuild(t) +} + +// BenchmarkBatchCIEInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchCIEInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIEInvalidBuild(b) + } +} + +// testBatchCIECardTransactionType validates BatchCIE create for an invalid CardTransactionType +func testBatchCIECardTransactionType(t testing.TB) { + mockBatch := mockBatchCIE() + mockBatch.GetEntries()[0].DiscretionaryData = "555" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "CardTransactionType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCIECardTransactionType tests validating BatchCIE create for an invalid CardTransactionType +func TestBatchCIECardTransactionType(t *testing.T) { + testBatchCIECardTransactionType(t) +} + +// BenchmarkBatchCIECardTransactionType benchmarks validating BatchCIE create for an invalid CardTransactionType +func BenchmarkBatchCIECardTransactionType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCIECardTransactionType(b) + } +} From 47dee396b55b9ca3d026b0cac9e3a706b00ca05d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 19 Jun 2018 16:34:18 -0400 Subject: [PATCH 0248/1694] #209 TraceNumber #209 TraceNumber --- BatchARC_test.go | 4 ++-- batchBOC_test.go | 4 ++-- batchPOP_test.go | 2 +- batchPOS_test.go | 2 +- batchRCK_test.go | 4 ++-- batchSHR_test.go | 2 +- batchTEL_test.go | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/BatchARC_test.go b/BatchARC_test.go index bb589d3b0..914a53566 100644 --- a/BatchARC_test.go +++ b/BatchARC_test.go @@ -27,7 +27,7 @@ func mockARCEntryDetail() *EntryDetail { entry.Amount = 25000 entry.SetCheckSerialNumber("123456789") entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchARCHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchARCHeader().ODFIIdentification, 1) entry.Category = CategoryForward return entry } @@ -63,7 +63,7 @@ func mockARCEntryDetailCredit() *EntryDetail { entry.Amount = 25000 entry.SetCheckSerialNumber("123456789") entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchARCHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchARCHeader().ODFIIdentification, 1) entry.Category = CategoryForward return entry } diff --git a/batchBOC_test.go b/batchBOC_test.go index c2d352a5b..2eed0c320 100644 --- a/batchBOC_test.go +++ b/batchBOC_test.go @@ -27,7 +27,7 @@ func mockBOCEntryDetail() *EntryDetail { entry.Amount = 25000 entry.SetCheckSerialNumber("123456789") entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchBOCHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchBOCHeader().ODFIIdentification, 1) entry.Category = CategoryForward return entry } @@ -63,7 +63,7 @@ func mockBOCEntryDetailCredit() *EntryDetail { entry.Amount = 25000 entry.SetCheckSerialNumber("123456789") entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchBOCHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchBOCHeader().ODFIIdentification, 1) entry.Category = CategoryForward return entry } diff --git a/batchPOP_test.go b/batchPOP_test.go index f16e7218c..54bc57790 100644 --- a/batchPOP_test.go +++ b/batchPOP_test.go @@ -29,7 +29,7 @@ func mockPOPEntryDetail() *EntryDetail { entry.SetPOPTerminalCity("PHIL") entry.SetPOPTerminalState("PA") entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchPOPHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchPOPHeader().ODFIIdentification, 1) entry.Category = CategoryForward return entry } diff --git a/batchPOS_test.go b/batchPOS_test.go index 69f377b0e..06428f26c 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -28,7 +28,7 @@ func mockPOSEntryDetail() *EntryDetail { entry.IdentificationNumber = "45689033" entry.IndividualName = "Wade Arnold" //entry.SetReceivingCompany("ABC Company") - entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchPOSHeader().ODFIIdentification, 1) entry.DiscretionaryData = "01" entry.Category = CategoryForward return entry diff --git a/batchRCK_test.go b/batchRCK_test.go index ff7ccc711..52863ac77 100644 --- a/batchRCK_test.go +++ b/batchRCK_test.go @@ -27,7 +27,7 @@ func mockRCKEntryDetail() *EntryDetail { entry.Amount = 2400 entry.SetCheckSerialNumber("123456789") entry.IndividualName = "Wade Arnold" - entry.SetTraceNumber(mockBatchRCKHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchRCKHeader().ODFIIdentification, 1) entry.Category = CategoryForward return entry } @@ -63,7 +63,7 @@ func mockRCKEntryDetailCredit() *EntryDetail { entry.Amount = 2400 entry.SetCheckSerialNumber("123456789") entry.IndividualName = "Wade Arnold" - entry.SetTraceNumber(mockBatchRCKHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchRCKHeader().ODFIIdentification, 1) entry.Category = CategoryForward return entry } diff --git a/batchSHR_test.go b/batchSHR_test.go index ac9bb1db6..52649899e 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -30,7 +30,7 @@ func mockSHREntryDetail() *EntryDetail { entry.SetSHRCardExpirationDate("0722") entry.SetSHRDocumentReferenceNumber("12345678910") entry.SetSHRIndividualCardAccountNumber("1234567891123456789") - entry.SetTraceNumber(mockBatchSHRHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchSHRHeader().ODFIIdentification, 1) entry.DiscretionaryData = "01" entry.Category = CategoryForward return entry diff --git a/batchTEL_test.go b/batchTEL_test.go index b556edbfd..d8aaaa336 100644 --- a/batchTEL_test.go +++ b/batchTEL_test.go @@ -25,7 +25,7 @@ func mockTELEntryDetail() *EntryDetail { entry.Amount = 5000000 entry.IdentificationNumber = "Phone 333-2222" entry.IndividualName = "Wade Arnold" - entry.SetTraceNumber(mockBatchTELHeader().ODFIIdentification, 123) + entry.SetTraceNumber(mockBatchTELHeader().ODFIIdentification, 1) entry.SetPaymentType("S") return entry } From fd4a5008d4409fe3f57e66636219d3bb7a9b1eff Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 20 Jun 2018 10:00:07 -0400 Subject: [PATCH 0249/1694] #209 CIE Write and Read Test CIE Write and Read Test Documentation fixes for POS test and batch.go --- batch.go | 2 +- test/ach-cie-read/cie-credit.ach | 10 +++++ test/ach-cie-read/main.go | 34 +++++++++++++++ test/ach-cie-read/main_test.go | 7 ++++ test/ach-cie-write/main.go | 72 ++++++++++++++++++++++++++++++++ test/ach-cie-write/main_test.go | 7 ++++ test/ach-pos-read/main.go | 2 +- test/ach-pos-write/main.go | 6 +-- 8 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 test/ach-cie-read/cie-credit.ach create mode 100644 test/ach-cie-read/main.go create mode 100644 test/ach-cie-read/main_test.go create mode 100644 test/ach-cie-write/main.go create mode 100644 test/ach-cie-write/main_test.go diff --git a/batch.go b/batch.go index c961c76eb..1fe6c3d6d 100644 --- a/batch.go +++ b/batch.go @@ -10,7 +10,7 @@ import ( "strings" ) -// Batch holds the Batch Header and Batch Control and all Entry Records for PPD Entries +// Batch holds the Batch Header and Batch Control and all Entry Records type batch struct { // ID is a client defined string used as a reference to this record. ID string `json:"id"` diff --git a/test/ach-cie-read/cie-credit.ach b/test/ach-cie-read/cie-credit.ach new file mode 100644 index 000000000..e809a72f6 --- /dev/null +++ b/test/ach-cie-read/cie-credit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821806200000A094101Federal Reserve Bank My Bank Name +5220Name on Account 121042882 CIEPayment 180621 0121042880000001 +62223138010412345678 0100000000 Receiver Account Name 011121042880000001 +705Credit Store Account 00010000001 +82200000020023138010000000000000000100000000121042882 121042880000001 +9000001000001000000020023138010000000000000000100000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-cie-read/main.go b/test/ach-cie-read/main.go new file mode 100644 index 000000000..8a4d14801 --- /dev/null +++ b/test/ach-cie-read/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "github.com/bkmoovio/ach" + "log" + "os" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("cie-credit.ach") + if err != nil { + log.Fatal(err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("Total Amount Credit: %v \n", achFile.Control.TotalCreditEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("Addenda05: %v \n", achFile.Batches[0].GetEntries()[0].Addendum[0].String()) +} diff --git a/test/ach-cie-read/main_test.go b/test/ach-cie-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-cie-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-cie-write/main.go b/test/ach-cie-write/main.go new file mode 100644 index 000000000..2be2b5fbc --- /dev/null +++ b/test/ach-cie-write/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "github.com/bkmoovio/ach" + "log" + "os" + "time" +) + +func main() { + // Example transfer to write an ACH CIE file to send/credit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + // Set originator bank ODFI and destination Operator for the financial institution + // this is the funding/receiving source of the transfer + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 220 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "CIE" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "Payment" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 22 // Code 22: Credit to Store checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.IndividualName = "Receiver Account Name" // Identifies the receiver of the transaction + entry.DiscretionaryData = "01" + + addenda05 := ach.NewAddenda05() + addenda05.PaymentRelatedInformation = "Credit Store Account" + addenda05.SequenceNumber = 1 + addenda05.EntryDetailSequenceNumber = 0000001 + + // build the batch + batch := ach.NewBatchCIE(bh) + batch.AddEntry(entry) + batch.GetEntries()[0].AddAddenda(addenda05) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.WriteAll([]*ach.File{file}); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/test/ach-cie-write/main_test.go b/test/ach-cie-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-cie-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-pos-read/main.go b/test/ach-pos-read/main.go index 0481c72ec..a5c37a8cd 100644 --- a/test/ach-pos-read/main.go +++ b/test/ach-pos-read/main.go @@ -29,6 +29,6 @@ func main() { fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) - fmt.Printf("POS Card Transaction Type : %v \n", achFile.Batches[0].GetEntries()[0].DiscretionaryDataField()) + fmt.Printf("POS Card Transaction Type: %v \n", achFile.Batches[0].GetEntries()[0].DiscretionaryDataField()) fmt.Printf("POS Trace Number: %v \n", achFile.Batches[0].GetEntries()[0].TraceNumberField()) } diff --git a/test/ach-pos-write/main.go b/test/ach-pos-write/main.go index a010e2f9a..51b00478e 100644 --- a/test/ach-pos-write/main.go +++ b/test/ach-pos-write/main.go @@ -25,8 +25,8 @@ func main() { bh.ServiceClassCode = 225 // ACH credit pushes money out, 225 debits/pulls money in. bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver bh.CompanyIdentification = fh.ImmediateOrigin - bh.StandardEntryClassCode = "POS" // Consumer destination vs Company CCD - bh.CompanyEntryDescription = "REG.SALARY" // will be on receiving accounts statement + bh.StandardEntryClassCode = "POS" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "Sale" // will be on receiving accounts statement bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) bh.ODFIIdentification = "121042882" // Originating Routing Number @@ -34,7 +34,7 @@ func main() { // can be multiple entry's per batch entry := ach.NewEntryDetail() // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) - entry.TransactionCode = 27 // Code 22: Debit (withdrawal) from checking account + entry.TransactionCode = 27 // Code 27: Debit (withdrawal) from checking account entry.SetRDFI("231380104") // Receivers bank transit routing number entry.DFIAccountNumber = "12345678" // Receivers bank account number entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 From cc286bd9ed361a0eef7a28703a435a048db04ff5 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 20 Jun 2018 10:09:04 -0400 Subject: [PATCH 0250/1694] #209 Update Individual Name #209 Update Individual Name --- batchCIE_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batchCIE_test.go b/batchCIE_test.go index 734281912..e4c8f2c3f 100644 --- a/batchCIE_test.go +++ b/batchCIE_test.go @@ -26,7 +26,7 @@ func mockCIEEntryDetail() *EntryDetail { entry.DFIAccountNumber = "744-5678-99" entry.Amount = 25000 entry.IdentificationNumber = "45689033" - entry.IndividualName = "Wade Arnold" + entry.IndividualName = "Receiver Account Name" entry.SetTraceNumber(mockBatchCIEHeader().ODFIIdentification, 1) entry.DiscretionaryData = "01" entry.Category = CategoryForward From bc0638d6c065997fa99020cee994acb117eb7a60 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 20 Jun 2018 10:41:51 -0400 Subject: [PATCH 0251/1694] Remove err cecks when writing a record with maximum length 94 Remove err cecks when writing a record with maximum length 94 --- writer.go | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/writer.go b/writer.go index d83469611..fadf4c5c0 100644 --- a/writer.go +++ b/writer.go @@ -13,7 +13,7 @@ import ( // A Writer writes an ach.file to a NACHA encoded file. // // As returned by NewWriter, a Writer writes ach.file structs into -// NACHA formted files. +// NACHA formatted files. // type Writer struct { w *bufio.Writer @@ -35,43 +35,29 @@ func (w *Writer) Write(file *File) error { w.lineNum = 0 // Iterate over all records in the file - if _, err := w.w.WriteString(file.Header.String() + "\n"); err != nil { - return err - } + w.w.WriteString(file.Header.String() + "\n") w.lineNum++ for _, batch := range file.Batches { - if _, err := w.w.WriteString(batch.GetHeader().String() + "\n"); err != nil { - return err - } + w.w.WriteString(batch.GetHeader().String() + "\n") w.lineNum++ for _, entry := range batch.GetEntries() { - if _, err := w.w.WriteString(entry.String() + "\n"); err != nil { - return err - } + w.w.WriteString(entry.String() + "\n") w.lineNum++ for _, addenda := range entry.Addendum { - if _, err := w.w.WriteString(addenda.String() + "\n"); err != nil { - return err - } + w.w.WriteString(addenda.String() + "\n") w.lineNum++ } } - if _, err := w.w.WriteString(batch.GetControl().String() + "\n"); err != nil { - return err - } + w.w.WriteString(batch.GetControl().String() + "\n") w.lineNum++ } - if _, err := w.w.WriteString(file.Control.String() + "\n"); err != nil { - return err - } + w.w.WriteString(file.Control.String() + "\n") w.lineNum++ // pad the final block for i := 0; i < (10-(w.lineNum%10)) && w.lineNum%10 != 0; i++ { - if _, err := w.w.WriteString(strings.Repeat("9", 94) + "\n"); err != nil { - return err - } + w.w.WriteString(strings.Repeat("9", 94) + "\n") } return nil @@ -83,12 +69,6 @@ func (w *Writer) Flush() { w.w.Flush() } -// Error reports any error that has occurred during a previous Write or Flush. -func (w *Writer) Error() error { - _, err := w.w.Write(nil) - return err -} - // WriteAll writes multiple ach.files to w using Write and then calls Flush. func (w *Writer) WriteAll(files []*File) error { for _, file := range files { From 250c275f13ee5c6d4fd39a153a63327d25d12853 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 20 Jun 2018 11:10:00 -0400 Subject: [PATCH 0252/1694] Remove WriteAll Remove WriteAll --- record_test.go | 2 +- test/ach-arc-write/main.go | 2 +- test/ach-boc-write/main.go | 2 +- test/ach-cie-write/main.go | 2 +- test/ach-pop-write/main.go | 2 +- test/ach-pos-write/main.go | 2 +- test/ach-ppd-write/main.go | 2 +- test/ach-rck-write/main.go | 2 +- test/ach-shr-write/main.go | 2 +- writer.go | 15 +-------------- writer_test.go | 5 +++-- 11 files changed, 13 insertions(+), 25 deletions(-) diff --git a/record_test.go b/record_test.go index 393a50f34..5718983a3 100644 --- a/record_test.go +++ b/record_test.go @@ -222,7 +222,7 @@ func testBuildFile(t testing.TB) { // Finally we write the file to an io.Writer var b bytes.Buffer w := NewWriter(&b) - if err := w.WriteAll([]*File{file}); err != nil { + if err := w.Write(file); err != nil { t.Errorf("%T: %s", err, err) } w.Flush() diff --git a/test/ach-arc-write/main.go b/test/ach-arc-write/main.go index a9a680dac..08c036971 100644 --- a/test/ach-arc-write/main.go +++ b/test/ach-arc-write/main.go @@ -57,7 +57,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/test/ach-boc-write/main.go b/test/ach-boc-write/main.go index a8794521e..c6085e427 100644 --- a/test/ach-boc-write/main.go +++ b/test/ach-boc-write/main.go @@ -57,7 +57,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/test/ach-cie-write/main.go b/test/ach-cie-write/main.go index 2be2b5fbc..fc9f8ffc6 100644 --- a/test/ach-cie-write/main.go +++ b/test/ach-cie-write/main.go @@ -65,7 +65,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/test/ach-pop-write/main.go b/test/ach-pop-write/main.go index 654f77d70..fa5f63b78 100644 --- a/test/ach-pop-write/main.go +++ b/test/ach-pop-write/main.go @@ -59,7 +59,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/test/ach-pos-write/main.go b/test/ach-pos-write/main.go index 51b00478e..c792a8b9a 100644 --- a/test/ach-pos-write/main.go +++ b/test/ach-pos-write/main.go @@ -72,7 +72,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/test/ach-ppd-write/main.go b/test/ach-ppd-write/main.go index a320bfea6..411a9ca20 100644 --- a/test/ach-ppd-write/main.go +++ b/test/ach-ppd-write/main.go @@ -59,7 +59,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/test/ach-rck-write/main.go b/test/ach-rck-write/main.go index fa81c70f6..adf2bcbe4 100644 --- a/test/ach-rck-write/main.go +++ b/test/ach-rck-write/main.go @@ -57,7 +57,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/test/ach-shr-write/main.go b/test/ach-shr-write/main.go index 2e42bc9a4..4451b9682 100644 --- a/test/ach-shr-write/main.go +++ b/test/ach-shr-write/main.go @@ -74,7 +74,7 @@ func main() { // write the file to std out. Anything io.Writer w := ach.NewWriter(os.Stdout) - if err := w.WriteAll([]*ach.File{file}); err != nil { + if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() diff --git a/writer.go b/writer.go index fadf4c5c0..beaf46f39 100644 --- a/writer.go +++ b/writer.go @@ -60,7 +60,7 @@ func (w *Writer) Write(file *File) error { w.w.WriteString(strings.Repeat("9", 94) + "\n") } - return nil + return w.w.Flush() } // Flush writes any buffered data to the underlying io.Writer. @@ -68,16 +68,3 @@ func (w *Writer) Write(file *File) error { func (w *Writer) Flush() { w.w.Flush() } - -// WriteAll writes multiple ach.files to w using Write and then calls Flush. -func (w *Writer) WriteAll(files []*File) error { - for _, file := range files { - err := w.Write(file) - // TODO if one of the files errors at a Writer struct flag to decide if - // the other files should still be written - if err != nil { - return err - } - } - return w.w.Flush() -} diff --git a/writer_test.go b/writer_test.go index fd08657e7..2de2e9455 100644 --- a/writer_test.go +++ b/writer_test.go @@ -31,9 +31,10 @@ func testPPDWrite(t testing.TB) { b := &bytes.Buffer{} f := NewWriter(b) - if err := f.WriteAll([]*File{file}); err != nil { + if err := f.Write(file); err != nil { t.Errorf("%T: %s", err, err) } + r := NewReader(strings.NewReader(b.String())) _, err := r.Read() if err != nil { @@ -80,7 +81,7 @@ func testFileWriteErr(t testing.TB) { b := &bytes.Buffer{} f := NewWriter(b) - if err := f.WriteAll([]*File{file}); err != nil { + if err := f.Write(file); err != nil { if e, ok := err.(*FileError); ok { if e.FieldName != "EntryAddendaCount" { t.Errorf("%T: %s", err, err) From fb0f9268937c810daa073b9b4dac6a3345a45f9d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 20 Jun 2018 11:48:22 -0400 Subject: [PATCH 0253/1694] Add some additional test coverage Add some additional test coverage --- batchCCD_test.go | 27 ++++++++++++++++++++++++++- batchWEB_test.go | 34 ++++++++++++++++++++++++++++++++++ entryDetail.go | 1 + entryDetail_test.go | 4 ++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/batchCCD_test.go b/batchCCD_test.go index cd778aa26..604aa6c9e 100644 --- a/batchCCD_test.go +++ b/batchCCD_test.go @@ -88,7 +88,7 @@ func TestBatchCCDAddendumCount(t *testing.T) { testBatchCCDAddendumCount(t) } -// BenchmarkBatchCCDAddendumCoun benchmarks batch control CCD can only have one addendum per entry detail +// BenchmarkBatchCCDAddendumCount benchmarks batch control CCD can only have one addendum per entry detail func BenchmarkBatchCCDAddendumCount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -239,3 +239,28 @@ func BenchmarkBatchCCDCreate(b *testing.B) { testBatchCCDCreate(b) } } + +// testBatchCCDReceivingCompanyField validates CCDReceivingCompanyField +// underlying IndividualName +func testBatchCCDReceivingCompanyField(t testing.TB) { + mockBatch := mockBatchCCD() + ts := mockBatch.Entries[0].ReceivingCompanyField() + if ts != "Best Co. #23 " { + t.Error("Receiving Company Field is invalid") + } +} + +// TestBatchCCDReceivingCompanyField tests validating CCDReceivingCompanyField +// underlying IndividualName +func TestBatchCCDReceivingCompanyFieldField(t *testing.T) { + testBatchCCDReceivingCompanyField(t) +} + +// BenchmarkBatchCCDReceivingCompanyField benchmarks validating CCDReceivingCompanyField +// underlying IndividualName +func BenchmarkBatchCCDReceivingCompanyField(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCCDReceivingCompanyField(b) + } +} diff --git a/batchWEB_test.go b/batchWEB_test.go index 1bb6c3552..cf0fb946d 100644 --- a/batchWEB_test.go +++ b/batchWEB_test.go @@ -214,3 +214,37 @@ func BenchmarkBatchWebCreate(b *testing.B) { testBatchWebCreate(b) } } + +// testBatchWebPaymentTypeR validates that the entry detail +// payment type / discretionary data is reoccurring +func testBatchWebPaymentTypeR(t testing.TB) { + mockBatch := mockBatchWEB() + mockBatch.GetEntries()[0].DiscretionaryData = "R" + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "PaymentType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + if mockBatch.GetEntries()[0].PaymentTypeField() != "R" { + t.Errorf("PaymentTypeField %v was expecting R", mockBatch.GetEntries()[0].PaymentTypeField()) + } +} + +// TestBatchWebPaymentTypeR tests validating that the entry detail +// payment type / discretionary data is reoccurring +func TestBatchWebPaymentTypeR(t *testing.T) { + testBatchWebPaymentTypeR(t) +} + +// BenchmarkBatchWebPaymentTypeR benchmarks validating that the entry detail +// payment type / discretionary data is reoccurring +func BenchmarkBatchWebPaymentTypeR(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchWebPaymentTypeR(b) + } +} diff --git a/entryDetail.go b/entryDetail.go index 8ff8a4ed8..3a3c3bca5 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -415,6 +415,7 @@ func (ed *EntryDetail) CreditOrDebit() string { return "C" case "5", "6", "7", "8", "9": return "D" + default: } return "" } diff --git a/entryDetail_test.go b/entryDetail_test.go index 96ffb8e6a..40228666b 100644 --- a/entryDetail_test.go +++ b/entryDetail_test.go @@ -606,6 +606,10 @@ func testEDCreditOrDebit(t testing.TB) { if entry.CreditOrDebit() != "D" { t.Errorf("TransactionCode %v expected a Debit(D) got %v", entry.TransactionCode, entry.CreditOrDebit()) } + entry.TransactionCode = 10 + if entry.CreditOrDebit() != "" { + t.Errorf("TransactionCode %v is invalid", entry.TransactionCode) + } } // TestEDCreditOrDebit tests validating debit and credit transaction code From 3537ef8d481f836dd0a7747a7b043945934c1586 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 20 Jun 2018 12:31:34 -0400 Subject: [PATCH 0254/1694] Update Readme to obsolete references to example Update Readme to obsolete refernces to example --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b509942ea..bb0572948 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ ACH is under active development but already in production for multiple companies * Additional SEC codes will be added based on library users needs. Please open an issue with a valid test file. * Review the project issues for more detailed information -## Usage and examples -The following is a high level of reading and writing an ach file. Examples exist in projects [example](https://github.com/moov-io/ach/tree/master/example) folder with more details. +## Usage and tests +The following is a high level of reading and writing an ach file. Test exist in projects [test](https://github.com/moov-io/ach/tree/master/test) folder with more details. ### Read a file @@ -76,7 +76,7 @@ if len(achFile.ReturnEntries) > 0 { ``` ### Create a file -The following is based on [simple file creation](https://github.com/moov-io/ach/tree/master/example/simple-file-creation) +The following is based on [simple file creation](https://github.com/moov-io/ach/tree/master/test/simple-file-creation) ```go fh := ach.NewFileHeader() From d338213b2e1f6b03bb6f4635c98e3b36af71bb00 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 20 Jun 2018 12:34:43 -0400 Subject: [PATCH 0255/1694] typo typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb0572948..4c14fcdc6 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ACH is under active development but already in production for multiple companies * Review the project issues for more detailed information ## Usage and tests -The following is a high level of reading and writing an ach file. Test exist in projects [test](https://github.com/moov-io/ach/tree/master/test) folder with more details. +The following is a high level of reading and writing an ach file. Tests exist in projects [test](https://github.com/moov-io/ach/tree/master/test) folder with more details. ### Read a file From b748a9912029f9077dcf03d843b917972dae74a6 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 21 Jun 2018 13:17:01 -0400 Subject: [PATCH 0256/1694] Addenda02 updates Added alphaNumeric checks --- addenda02.go | 31 +++++-- addenda02_test.go | 208 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+), 6 deletions(-) diff --git a/addenda02.go b/addenda02.go index e6ed64381..804cd153d 100644 --- a/addenda02.go +++ b/addenda02.go @@ -12,7 +12,6 @@ import ( // Addenda02 is a Addendumer addenda which provides business transaction information for Addenda Type // Code 02 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. type Addenda02 struct { - //ToDo: Verify which fields should be omitempty // ID is a client defined string used as a reference to this record. ID string `json:"id"` // RecordType defines the type of record in the block. entryAddenda02 Pos 7 @@ -97,7 +96,6 @@ func (addenda02 *Addenda02) String() string { addenda02.ReferenceInformationTwoField(), addenda02.TerminalIdentificationCodeField(), addenda02.TransactionSerialNumberField(), - // ToDo: Follow up on best way to get TransactionDate - should it be treated as an alpha field addenda02.TransactionDateField(), addenda02.AuthorizationCodeOrExpireDateField(), addenda02.TerminalLocationField(), @@ -121,10 +119,21 @@ func (addenda02 *Addenda02) Validate() error { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: err.Error()} } // Type Code must be 02 - // ToDo: Evaluate if Addenda05 and Addenda99 should be modified to validate on 05 and 99 if addenda02.typeCode != "02" { return &FieldError{FieldName: "TypeCode", Value: addenda02.typeCode, Msg: msgAddendaTypeCode} } + if err := addenda02.isAlphanumeric(addenda02.ReferenceInformationOne); err != nil { + return &FieldError{FieldName: "ReferenceInformationOne", Value: addenda02.ReferenceInformationOne, Msg: err.Error()} + } + if err := addenda02.isAlphanumeric(addenda02.ReferenceInformationTwo); err != nil { + return &FieldError{FieldName: "ReferenceInformationTwo", Value: addenda02.ReferenceInformationTwo, Msg: err.Error()} + } + if err := addenda02.isAlphanumeric(addenda02.TerminalIdentificationCode); err != nil { + return &FieldError{FieldName: "TerminalIdentificationCode", Value: addenda02.TerminalIdentificationCode, Msg: err.Error()} + } + if err := addenda02.isAlphanumeric(addenda02.TransactionSerialNumber); err != nil { + return &FieldError{FieldName: "TransactionSerialNumber", Value: addenda02.TransactionSerialNumber, Msg: err.Error()} + } // TransactionDate Addenda02 ACH File format is MMDD. Validate MM is 01-12. if err := addenda02.isMonth(addenda02.parseStringField(addenda02.TransactionDate[0:2])); err != nil { return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidMonth} @@ -134,14 +143,24 @@ func (addenda02 *Addenda02) Validate() error { if err := addenda02.isDay(addenda02.parseStringField(addenda02.TransactionDate[0:2]), addenda02.parseStringField(addenda02.TransactionDate[2:4])); err != nil { return &FieldError{FieldName: "TransactionDate", Value: addenda02.parseStringField(addenda02.TransactionDate[0:2]), Msg: msgValidDay} } + if err := addenda02.isAlphanumeric(addenda02.AuthorizationCodeOrExpireDate); err != nil { + return &FieldError{FieldName: "AuthorizationCodeOrExpireDate", Value: addenda02.AuthorizationCodeOrExpireDate, Msg: err.Error()} + } + if err := addenda02.isAlphanumeric(addenda02.TerminalLocation); err != nil { + return &FieldError{FieldName: "TerminalLocation", Value: addenda02.TerminalLocation, Msg: err.Error()} + } + if err := addenda02.isAlphanumeric(addenda02.TerminalCity); err != nil { + return &FieldError{FieldName: "TerminalCity", Value: addenda02.TerminalCity, Msg: err.Error()} + } + if err := addenda02.isAlphanumeric(addenda02.TerminalState); err != nil { + return &FieldError{FieldName: "TerminalState", Value: addenda02.TerminalState, Msg: err.Error()} + } return nil } -// fieldInclusion validate mandatory fields are not default values and rquired fields are defined. If fields are +// fieldInclusion validate mandatory fields are not default values and required fields are defined. If fields are // invalid the ACH transfer will be returned. -// ToDo: check if we should do fieldInclusion or validate on required fields - func (addenda02 *Addenda02) fieldInclusion() error { if addenda02.recordType == "" { return &FieldError{FieldName: "recordType", Value: addenda02.recordType, Msg: msgFieldInclusion} diff --git a/addenda02_test.go b/addenda02_test.go index 9ba6727e7..dcc0b26b5 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -479,3 +479,211 @@ func BenchmarkAddenda02TransactionDateInvalidDay(b *testing.B) { testAddenda02TransactionDateInvalidDay(b) } } + +// testReferenceInformationOneAlphaNumeric validates ReferenceInformationOne is alphanumeric +func testReferenceInformationOneAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.ReferenceInformationOne = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReferenceInformationOne" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestReferenceInformationOneAlphaNumeric tests validating ReferenceInformationOne is alphanumeric +func TestReferenceInformationOneAlphaNumeric(t *testing.T) { + testReferenceInformationOneAlphaNumeric(t) +} + +// BenchmarkReferenceInformationOneAlphaNumeric benchmarks validating ReferenceInformationOne is alphanumeric +func BenchmarkReferenceInformationOneAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testReferenceInformationOneAlphaNumeric(b) + } +} + +// testReferenceInformationTwoAlphaNumeric validates ReferenceInformationTwo is alphanumeric +func testReferenceInformationTwoAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.ReferenceInformationTwo = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReferenceInformationTwo" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestReferenceInformationTwoAlphaNumeric tests validating ReferenceInformationTwo is alphanumeric +func TestReferenceInformationTwoAlphaNumeric(t *testing.T) { + testReferenceInformationTwoAlphaNumeric(t) +} + +// BenchmarkReferenceInformationTwoAlphaNumeric benchmarks validating ReferenceInformationTwo is alphanumeric +func BenchmarkReferenceInformationTwoAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testReferenceInformationTwoAlphaNumeric(b) + } +} + +// testTerminalIdentificationCodeAlphaNumeric validates TerminalIdentificationCode is alphanumeric +func testTerminalIdentificationCodeAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalIdentificationCode = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TerminalIdentificationCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestTerminalIdentificationCodeAlphaNumeric tests validating TerminalIdentificationCode is alphanumeric +func TestTerminalIdentificationCodeAlphaNumeric(t *testing.T) { + testTerminalIdentificationCodeAlphaNumeric(t) +} + +// BenchmarkTerminalIdentificationCodeAlphaNumeric benchmarks validating TerminalIdentificationCode is alphanumeric +func BenchmarkTerminalIdentificationCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testTerminalIdentificationCodeAlphaNumeric(b) + } +} + +// testTransactionSerialNumberAlphaNumeric validates TransactionSerialNumber is alphanumeric +func testTransactionSerialNumberAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TransactionSerialNumber = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionSerialNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestTransactionSerialNumberAlphaNumeric tests validating TransactionSerialNumber is alphanumeric +func TestTransactionSerialNumberAlphaNumeric(t *testing.T) { + testTransactionSerialNumberAlphaNumeric(t) +} + +// BenchmarkTransactionSerialNumberAlphaNumeric benchmarks validating TransactionSerialNumber is alphanumeric +func BenchmarkTransactionSerialNumberAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testTransactionSerialNumberAlphaNumeric(b) + } +} + +// testAuthorizationCodeOrExpireDateAlphaNumeric validates AuthorizationCodeOrExpireDate is alphanumeric +func testAuthorizationCodeOrExpireDateAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.AuthorizationCodeOrExpireDate = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "AuthorizationCodeOrExpireDate" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAuthorizationCodeOrExpireDateAlphaNumeric tests validating AuthorizationCodeOrExpireDate is alphanumeric +func TestAuthorizationCodeOrExpireDateAlphaNumeric(t *testing.T) { + testAuthorizationCodeOrExpireDateAlphaNumeric(t) +} + +// BenchmarkAuthorizationCodeOrExpireDateAlphaNumeric benchmarks validating AuthorizationCodeOrExpireDate is alphanumeric +func BenchmarkAuthorizationCodeOrExpireDateAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAuthorizationCodeOrExpireDateAlphaNumeric(b) + } +} + +// testTerminalLocationAlphaNumeric validates TerminalLocation is alphanumeric +func testTerminalLocationAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalLocation = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TerminalLocation" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestTerminalLocationAlphaNumeric tests validating TerminalLocation is alphanumeric +func TestTerminalLocationAlphaNumeric(t *testing.T) { + testTerminalLocationAlphaNumeric(t) +} + +// BenchmarkTerminalLocationAlphaNumeric benchmarks validating TerminalLocation is alphanumeric +func BenchmarkTerminalLocationAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testTerminalLocationAlphaNumeric(b) + } +} + +// testTerminalCityAlphaNumeric validates TerminalCity is alphanumeric +func testTerminalCityAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalCity = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TerminalCity" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestTerminalCityAlphaNumeric tests validating TerminalCity is alphanumeric +func TestTerminalCityAlphaNumeric(t *testing.T) { + testTerminalCityAlphaNumeric(t) +} + +// BenchmarkTerminalCityAlphaNumeric benchmarks validating TerminalCity is alphanumeric +func BenchmarkTerminalCityAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testTerminalCityAlphaNumeric(b) + } +} + +// testTerminalStateAlphaNumeric validates TerminalState is alphanumeric +func testTerminalStateAlphaNumeric(t testing.TB) { + addenda02 := mockAddenda02() + addenda02.TerminalState = "®" + if err := addenda02.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TerminalState" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestTerminalStateAlphaNumeric tests validating TerminalState is alphanumeric +func TestTerminalStateAlphaNumeric(t *testing.T) { + testTerminalStateAlphaNumeric(t) +} + +// BenchmarkTerminalStateAlphaNumeric benchmarks validating TerminalState is alphanumeric +func BenchmarkTerminalStateAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testTerminalStateAlphaNumeric(b) + } +} From 0092fd430fe86bc55547358928c89f10bd6f1c52 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 21 Jun 2018 13:53:44 -0400 Subject: [PATCH 0257/1694] Add back in error trapping on writes Add back in error trapping on writes per IM discussion with Wade --- writer.go | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/writer.go b/writer.go index beaf46f39..688966608 100644 --- a/writer.go +++ b/writer.go @@ -35,29 +35,43 @@ func (w *Writer) Write(file *File) error { w.lineNum = 0 // Iterate over all records in the file - w.w.WriteString(file.Header.String() + "\n") + if _, err := w.w.WriteString(file.Header.String() + "\n"); err != nil { + return err + } w.lineNum++ for _, batch := range file.Batches { - w.w.WriteString(batch.GetHeader().String() + "\n") + if _, err := w.w.WriteString(batch.GetHeader().String() + "\n"); err != nil { + return err + } w.lineNum++ for _, entry := range batch.GetEntries() { - w.w.WriteString(entry.String() + "\n") + if _, err := w.w.WriteString(entry.String() + "\n"); err != nil { + return err + } w.lineNum++ for _, addenda := range entry.Addendum { - w.w.WriteString(addenda.String() + "\n") + if _, err := w.w.WriteString(addenda.String() + "\n"); err != nil { + return err + } w.lineNum++ } } - w.w.WriteString(batch.GetControl().String() + "\n") + if _, err := w.w.WriteString(batch.GetControl().String() + "\n"); err != nil { + return err + } w.lineNum++ } - w.w.WriteString(file.Control.String() + "\n") + if _, err := w.w.WriteString(file.Control.String() + "\n"); err != nil { + return err + } w.lineNum++ // pad the final block for i := 0; i < (10-(w.lineNum%10)) && w.lineNum%10 != 0; i++ { - w.w.WriteString(strings.Repeat("9", 94) + "\n") + if _, err := w.w.WriteString(strings.Repeat("9", 94) + "\n"); err != nil { + return err + } } return w.w.Flush() From a57d6b3a0e8cc6657a43e9b692b5ee4ea15b1d84 Mon Sep 17 00:00:00 2001 From: Brooke Kline Date: Thu, 21 Jun 2018 22:23:46 -0400 Subject: [PATCH 0258/1694] Rename BatchARC_test.go to batchARC_test.go --- BatchARC_test.go => batchARC_test.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename BatchARC_test.go => batchARC_test.go (100%) diff --git a/BatchARC_test.go b/batchARC_test.go similarity index 100% rename from BatchARC_test.go rename to batchARC_test.go From bc5c3e4f1f4d58cf9e63c1e65345e502402707ba Mon Sep 17 00:00:00 2001 From: Brooke Kline Date: Thu, 21 Jun 2018 22:24:33 -0400 Subject: [PATCH 0259/1694] Rename BatchARC_test.go to batchARC_test.go --- BatchARC_test.go => batchARC_test.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename BatchARC_test.go => batchARC_test.go (100%) diff --git a/BatchARC_test.go b/batchARC_test.go similarity index 100% rename from BatchARC_test.go rename to batchARC_test.go From 7b8e07f8396055eceb6e805d32f2a2f6eec498a1 Mon Sep 17 00:00:00 2001 From: Chuck Phipps Date: Mon, 25 Jun 2018 11:07:21 -0500 Subject: [PATCH 0260/1694] Additional docs for file fomats The nacha-format-excel-sample illustrates the file structure. The second tab compares the original 6-record with the return 6-record. The rtn-check-digit-calculator finds the 9th digit of a RTN, and shows the formula. Code in originated files should verify the check-digit in every Record-6, Field-4. --- documentation/nacha-format-excel-sample.xls | Bin 0 -> 40448 bytes documentation/rtn-check-digit calculator.xls | Bin 0 -> 20480 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 documentation/nacha-format-excel-sample.xls create mode 100644 documentation/rtn-check-digit calculator.xls diff --git a/documentation/nacha-format-excel-sample.xls b/documentation/nacha-format-excel-sample.xls new file mode 100644 index 0000000000000000000000000000000000000000..0a4269ccfccb5e4c39dc642e7049099af51b5685 GIT binary patch literal 40448 zcmeHw33wF6)^<(S$wm?g5cUxQ1SFW11VJ)cO(2^|P!JG977_^wCIP_<5?KVn3kZm^ zsJP$)ii)DRAc(u7B7%arfct_9Zd?@2|Grh-Ju{t244?P=pXYwh?MY2dojP^SsZ-}v zRaaGa_PrH)^o~cm9uuZ(gzyx9Hu(u3C%6T_8RQir#7O*L`p>4OCIu7%KmU9E2Wj9B z$eN4B^F;7M@J3*p`yyBo{1E&R0uTZbf)Lsv1S7x%L?}XggboN{2pthRA#_FvN9cmk z6`>nKcZ41YJrQ~#*bvS^=#9_^Ap)T2q_4u2x$oE2pI^O2p1w`A!H-az8P|y ze*XtGajT*hiduXc&=wh@263I3g<7`#>I93?8F-+W&B|{fcFS+6O7#oh`%b)@us-mB zWfUPaI9!*2d!3jfCP_J_DBxLqw-+83+N~}_TZ&S`{ACK$TNH`uVk$l*vhLZ?LWPux zkOonO-!-iejRb!S@>w0q&BdfnT~pFWj!bQ#r|Bc=Y7M6{o|8s;I<6)P0 z|CHao<##)LTf`Hx%=;1&gMR82;5W@(p|_1C1vXIyJE%rK^S_z<0c2^K`wXju;BDf& zu~n%)Xf^k-R&#G?H8K|17_{NmX;7ylT9tzs@qdXi{4X#p z7~NabSf!h~Ox3J|w%b#%F4k6QP*R6|HrIMrNh3zl0IsWzD(lzmt66SuVMh;?tZIo| zKTAG(V2%zpj8-Lx>t%mLDg6y8ojLUM>W+~GbFkhKz55L6(_+(_pHXH~8v>C2QxB;o@%2iFMTt8y}_r?CI4*Y~tKl2MklXazf}S?%~8) zM*tJ2@<{dc>Lc#uG};-%L`9+{i2FE8o=Dod-l7yOM^ekB?xKcGjIvLR>ghE|+|Mca z#3%>ac&FclyWn3SHgme|%m@O2o~*YZ*2j-r3H9PbRK8{U&+HeV^{q=BJrEUl|0SiN}lF$B~NpB zOZ3lHo>eW#*%JKm7Vuk=XS>7?Yo?zEC7de>Qg5q6LvIeaIY757t}ZBeMa8aOyLt() zCnUZFeYJ%D3kdSk`+)&1zz4MeALtg&6%9$>)SsXyS8&7rH%ocA@*(jLhyo!Rfu`i) zYKQ~Qg%t(o3W)}{IpZ5}6JLzp3g6I=YG1CfXml>ePq0(;x#}X}hMdZ-xS}KB8$v@n z1$2U*l|OQYM#4c~=~?+B=c5vC(z8v+H_3U94nKgYi+IG)1Evg$jtQTuPZqBEP_gn- z@*D7^&``V0Zqw_plv=bD$~8|I47l{%?66JLvtKigul3p=nBeFxmN zzFh5-{k%zUv=jQ+U+U9G=`+aStI^lshCX%uH^bND)Zkogl=4uq^3v_W)Q%dyp*ItK zN4r=;p$D6;2Lpe&E8G|I`_Zm+I+~pr`q$w`y)^yLbggeQIvPGK6ZL|3j8U}`9(uhr zIyziyFT?(I`WoD5cN5&Oe-oW%?cy~++J8i|_EPX>_0{kVzcbM{^xP8MB&VVO@tPjA zdKqwC&l=p|o74jSa5J3xWIMXTnUZ#e39`DB{`p(tyM^0?z@NID+ba@FYOa1{v{zC{ z2+E+8!dLo1|CSUIDxCT;(veHFU_x2KWS?QceIyA-{?0? z?V`bzJfJhp72kpk+0UE!DtL3g4*6aCH$|WIb+to>eq8YlJ)8I{I^b)P(}Aziaj6&d zHcfALHC{BcPYqw$XY+bF@ZHfzy{2jQz_4zN4vMwzOHbiy^>mJcawgUoRG)lFGhQr{KcTJ z+mEI<2i%pfL0@k#4Zm4^wehH#-)V3MzDC~#AMHL<(}M;#^k$ON0e2^-lU*78yveVd z!QIiJK3&_z7h3}E`)$H)PV*DzejD=W<7Tt|Q?o-2ZqzHu5@Hu5=sXT087((@-N0SP zHKQGqTJV=>h%3OM8GTnj0)ncSNe@Q9G}~29CBKdS>oU$d=xF_b##i+l(6gIyP4%m~ zJ(%^W>A|eeBv<_?`)`RpuT+=V(j51Gs-KtrOK7Si34o5=0v#Z-v%O- zv}Gb(A8G>;npMk0xcJcqB5aeEiSS0M4MeENmWgn=q76hiTW^^N*Ear=$ljdRTZao3 zZ6MNm>u~be1|qGu4wpdMK&187;d)6Mh_v21TtsODk=9#>H{uQ=NVxNz*tphPhs!f< zAkup4a4n||L|ShhF7UL0Nb9X*Ya5Z)TL+z4TO?_{bui-nH4$~lsbvS`P5EEygr_!I z^WuoM5ox`3`nHWo>#ftTZA4mco&Iej(t7Je8bmzsYlHA0n;flkqiOIO z01RI_coFxvIl?`WAAkJOAmWPwjmUH7&TX=3m(>l-&j~gcj@@nfkS2d8*k@M_X#yOB zp;;)5^qNqf_{Ga@%{Fb?)J&-4jEv(eK7T~;q887|d+&<>X1rOsnyoc&=;Yz&|*m?&U+ZG~C$*mKKYYP!`S8Y~e zJcuYIQtD>aX>JNUeCV*x;;)3+O(h|Dw@B^T_x(fZ1inws_+>e0P)+qP{@LL;IJCSjU-bcb+;>0WBU z;3>_UW~ixYk`3O*yqET^bsdtoE|lhxlD96D29vyXq0Zh*c__7I1GCJrjy7qKOPgLT z&)zAqlY>cN`K80q|4CuzSk0!+W3i6@q6rukJ%OS5chYE8crB(A!rvmE7M-ZI@% z16TI*P7U2$h6WehxVX4h+IYIeb@+B`^3HIf>uhYzWtd~4>oCmbxC>3Vt;ulRS>P@@ z3*5zLfqVT~;4V1}+@)uMyWGJ2Ynxng7PvQ_1@6kTz+GkF{WD$ z7Pz;b1@3KUfqVN|;BIUYca=wbEOcR&H8#4hEy|WsT3TMy5VO*nD^{}0(JqTu&!!-p z@r%Zj95y_yVG}7h-CinBFE(HYiqi-So+R=0DzEt9i<3>>{LN)3^hABR0!22VW;U_S z+4OFOjhaGAHs0kGKYnn;J)8DsHgV0_^lOEUnq*2gKIIkPp8Uu?n+|3+@y*!`Y=sS{ zp_jgIPZLqYxIXm`#=AKPwGaEOS)~6LV&X%?}=ZhUrxMve?X5+@v z`nSTy+0ypr9Qy1T_iVbD*|@Q^b6a8KY-xLQ_U?Y)J)5p(Hf}8KyjIvaTiV{7CqDYh zJ)3T3Hf}5}t`#=UmbN$N%)!Upv*}JYLt3{q8`unKgQdmB-Sp^tiVZt7Tt=VP!_3Bw zrA4&D#@W(hl$H4P7ml|!y6m>QAL7_uQ&XRHGNl^fQ(5YO}$NGcP~X4NI#R*4DyMhq= zNTxc%RUYkdlYuPfi)*FpW)qm6dLoh)?zm!u|Go`#t^Z!vMVZzZb<;aGff<`ZM=>a?qYc%>Iz4zhL zv=0;)h^P87g65S^Z>TFl_hRapE#1*E=YcS5S_hkA$D;0N*s*X~S+DN~EMRK}50h#X zaL3-Rvf_DM9}uJ0u!A5ey1O5qazcr`+J^F#)=92KJpADvEYbiOxYDh6XRe~s=PPCT1Cz^%N~C6z_uw#7hWD>T1|S zsopf%RZx!rbj+84Lmk{m@1)%IqO>uo;bqn_VwuyL2-nR*@!=EFlVoj+Ao48vD;ITV z-^X_l422e}#0+Uc4h%ZXWDJ^IJsBca;)#59@EJE3;zO&C==DSx=pPVge)1MSS{Deh z=k=KrvjfQz(obpdr=Vq zpWuf-@zp3l1Oo?f6q4okqCD27#ag7B)}k!vqF)hl6-^>;?_R)x&9D}s0^}>!iZyH2 z;B_4pB5o}J=rZFF*2(}_9LfSPKBOIIOXMX*5aMzR*ZUUYy&;8T2=9yzue5e5TP?Gy zy4+UOP&d7_VR~KpLOikh%r!*mC<@B~sWBm}vxbXxPOU>!UKWJA@HT|IOnEm5zJ0(V zw&E=WIC1CI85X6joy$@dJP^?hU74_;`;$}<`EYATzW5~6C0i@#GmP9iB7}(NELH>| zPG)vG->iTFAAf>;zYoS_DJsn)GDsEq#%2@#g1>C8%kLr zpcTiGtmJk^qpiu0MDnO~TjQY@tc0Vi2(c`3OWKu}`H6x36g zktuP}`(0d8J-r;-ijMISV(i%5-1KzlH7hGO7em>&Ntnq^nSh3i9vTm%m}n>_2L7Cx zk)BbQl4C2(D9R|jIK!5jk~a$Fb29QSEY1Rhg8ZUvssaq6W1^yC7{^9M$1#qNieVfR z9~H|u7V%KTanVt6OvlAX#lA;OoD>zu{P^go_-Mv4QA3C~Bt9x>2;!u~sG;#t zB?ZY&gNk!fii%|Ia55_`Ker$yZ>%jny9lbML@9+xr4*;|RzS3m;%p6o<|)c3F3!owMRAGJN!NY~+R9c`oKiem z$p;1Jr)Or{veRLIsVT*2S+=~&hB^|MPKb$0 zpjr~*4fPD6+7hFq5)&8?iAqdjJTxki@TBOdB&s(l3F-s0;=+_XB^J)26;iCi{L!%J z3vDtdyI7MAjjwWx^K5BZ8EK=S+Y7Ucp=)?b2DDJvzsQ!7mX<#n)mPQA!7@^E34jM> z=cQ*~oSi-z#*>}i0k&$iOqAXF|o0+ zaY+eDGu4G!(a30YF|koGv5ey!My4540=1ct5S2(HN=#JRfmVjb!>?7-pJ&tMkkx^f z6g(@XC<_)>oL`Juq-Uf;IjVlBj{?Hj(hVrax!jDRqLd3WY$>CQv!t0xCzQUEj@D+w zpkZU!kTEev1IJPHxVWfziW;8~Wr)m%PK-xG!n2j(NCBB**8mPsHt4ddG#oZ`bdtmG zsG8W=s5r)Pu~G4)7at#$Kto9w0u`Xe*b|H$3psV|f?f)$lAV{CZ%c}fhGHRE!jMGZ z7M7RR)|Ej&nN{W0WwyMMspaT1VxyzcXT-(=4qT!p4IPR^G_QoXp|P=`o|uGBOiT=% zVT`&!Ph!NNvE>i#3&`597*tgKlvEseG6^FanV(T)E69N}!GPh>s`sM-!8mpIX3uyo z>IAowLv&_s-PDo>Y#pUn)i=~tO`6_-8wJ#?ylMvCBSaLmIk5p$89%Ug*L| zc?j&pA(o~cG?VU5v?0_y5v+#}g{2fm$8vO``N4Ce>5}Y_z%Rp5a&W+ow9_u8}YMQ zDr`3r>vxE+K>RPnrPfXe5Ky~NEqUz`>oNOV@00@}#SMVn-~=P;8ta!pFM$>;@*CC>a8!nbmYUxZ(o`G>5Iqn#g?Y)|9mt4 zr;p#A2vsy;vDH;so8GiY8r^LxSax1glJML#UuIsffB7wvyO zyzh+jHmyiI@2)xGSmfNw-rbIL`}Row%Rc^3TNm8@!>Ri(_;c&h)5lhSY3sCZv(NLN zq$R&zd2Gf7yXMY&b>ZFj_O*_iIqkZ;{&8gc#AgcLzH9g!G2J$0Jsy2i`{2cg^Pef) znp*but0DVGW*mv16fyVS8?T)BUawnj>vD8R&O5(!Dts^T@~zA7d$sA&=cD)DmjB$` zhX#F~;Z>8kI;ZNhNq5|L-ls)7SKL>=`9RNo+jpFP_YYL1c}1tpTGMGhD!2rJXW1Jn z%cqu~=k%S6WyQ(mbu^E_y}AC++d}Ne=G<}Bio=PePe*1<+V=X^r+*b`8@i^vym9~S zN8bDV)A@;SejIji{ivX^gEsgEEh&Fv@Zu*<%pP=axBK4D>i*)O>SHf={Nn4G;jcdS z&ki4FzfynEoIVFvEPQbLhqiaOpL_6}9|nDR*^?1hT(I=QOTK#N)PPSizCE{}zekdL z;iH2WPYH$leAGj|VA4r!RfQOeeSB(c*Z(Dn->BGOaFuh?;=-M;DKPYOrmZnQ2r@X+q{Yd`!YZ+Ojy z_I7)H<<3WUo%&+u$G^wT{q>vepWbpuTJMO@v*Yf&Y^&Geh;w@FFZ&?y`(K~Fd5A|u z$Gq!qIk_Ny{(u{w+y3BHA3wPyadi5mdG--EuHVrmedMO~=bjt8pk&Z_Kdp{9QS(B^ z4IPIsIr&<_(LVdSJ$lo@k>zvxz7+3QFt4=WACu$nJSSz&gRzHRyXmU%;r&K_bM1=j zZ@W0RyWjjCJ&O}k^UBxzH7p(**Es9CGxy~$8{HVb#{aVT`Abd*T>SXftw-O;S$x0u zw!TkpJ^lJ;vDY4ZZu8or8+>29`(Vw3SC-$quU6!|@#*=u<~>~$^>Y`|F73pD_Y2=0 z`)y6}xF_Fi|H_{m-urOtfpZf&Pu#z2$mIIF4qkDv+uEp$@3$Pjef!WendLhl-}%ka zpjSTmw*Ft2PI>yn%dR@%Q+5)G?{9kP>Nj5OR`5b)UG$=NE#J+E`_8-Ug~LJT&GtOn z?WVvHX(>a(4sW_Q{A7C6Yum$AuBlpS|R*U2}dpGvMl9&b<2S^OO4r&rO>3^H-l8J9Y8R zeP(+0|8(2Y*B?qg{m1LUZ`bDUy886L;*u|0b254Eskif!uldF|)%%S>SD!q+YT|7d z%sTnv%e$`m>5%8gon{tXcd9BU;;Gza`$jB(E-~$fDMPy~{we$CJ~=)7x?fwf{F`M< zx*z>+K*fmhOLu(sVTXOgXYc-YmG80#cV^{;{jzs<+>#zg2Y>m%iwkdVTz~b}_Rs&3 z9s6tk;C|&FjCg6$vrisLx-#|1+Pn89{B(E2j@k=fh^b$GEd5wa_TtpvdL-Lk`1Hd$ zBYRwSRaNAU&-yImJX#8a_cUV|@`)ebXja_?U+?b3Jeg07ty5`Cr_uKZD?n(M4@cPvw)(-PH z6XpF!zw55~Jm~kq>t7mN7k@|Do3zq%?&(B%jzW)c46X!kMaQv3|Q6Ya+^dA1mGkH5!?b^6x z$NZ+*i!LjEz4OO6Pku1`smDHAacJk`hiAPUkoZxb*T&@Mc6qYz8y}6^;}L&+&&D-z zi}DJ)zp&qW%{L>j^XvY>n9KXj_qwE4LC0TuZ`kwf&aRbxOV{4tbJXfOzb`Z5jvsvR zf`Sr1}?|m6^WN5_2PtUGg^xn_YuK6JHK=_uIzQ|dh|JXep2Ms&?{!KXp{`G6W zRin3GH0rI36Yg1i&sTdd-toYIAz#*Qd}dmZPr#6p$@4Ey_8Roi^N&o-5Bjpl-d75K zswrK)=*9G75BXj6#rEkDd(tMQeHM80p$FC-`g}nAm$%P&>x)-T9~v~qbN`z|_T2sA zCuhd4{A~PlH7TOd>hl&K4&S;o zwQu#iw~bwTPuAp~iD$-+UikV_Z|_f6UpesZ_n(X178C!_wt`2}-zxiV;)CxsY@2X{ z=WP|+HgufQZAI^~*kji;9!YrP@mZh$`tAeotp4G#z&DqbuE|XLcFk`y`aP5skod>l4?j2j zroEmYKl6CEFW>xO$<}`@t=qKWrKY$`ey$UV)+uQ{t_>M{m3QV-h3hPx2Y#||;r_+f zO|X3X?AJY3L`_Qzc<9>MJ0=Zj3aGoU>4}8N({I^!-TaO3-&-0HIjH}@_6v@ujEg;d z)pgshc>L!vHw`K(i``tdHRr*@o^@GcCZ5_o{lod5VJVx}*R0%bnLq5-x!>1coHGBB zkM3Ii?Hga0y|}+>%F!P_+Hq)-&%Jry^=FEATrwn^{!^>NK`5aQYVjs__IUBuDn;nZp-O$_doWOz1J9g@8SG@-_4uyXx{B5@hkiOUbf(?6G_Q) z!*UL_-!?Jp$Ea;7Q(|tdTzGWd5BrY#SImsM;*!@>XZ})sEOpP`z^JD~hIGDSg!hR# zgQk4&Tcz*JBV`Z1_tcKMd&c}YIda^dnaSlJt$J(Q>cjItnd142&+7X>eXnRujQ3}6 z^zS+QSXlV7%Px3lZpqt^tgR`W@hxW3Ze{>MZ?CxhapZKv{Gh*S2B4-ra#(em2WZov zT>m}MVGB;pyUJQy`_5A#H}?B*@xBKScrN%XA!tZ)#6`!y3)(u!I(SjZvumrKzWLTy zJCw$L6jQmmtM&Y^Z@O~bb5nO5UuG}tlz08%VPy|rK5toV;&&Y<-#98Vw_ikIe}@mxoX3{CB>&3w-FyJqmSU`Ab|rR)`tZTv5h~ zI(A?@um=(>e}~GIT}s0=OO3V$!#wf4iqrv$`#RE|SkE7g zG>J0bi|g^hjx;sDIlz(j0i}aZXq<%7|J&gDaRRw2-l&mFLVPWbI{XL=C)d9i+B_^^JuKG1>|Jzro) zo)>LLwXM|y)aCC=Dj&GPf-u&mBP_ughMRc6Rx?ZNwv3Q?w3m)zki@fVWpNV^SPu!? zZMjh5Q5v0Iqh+LoL9c+_C+X>BX;C^4T^6i(0Orbr9-`CJ@#s2c812Q(1NsL{)k7~! zy_k7Wr)GN8R(q3PxIft5R1d620p`ksD+uPY^f5DxK4*q;b;Ha9J3)ZC^1yN%V6M8t zI$JY(d>Y)D#~-rK;lBH9t zc*<#2o+nZ?1C{bZid0p~8!6IKDIcV0ODg4y6s<_5tVq#*RLT!2T8>KjBSo80sQ{#C zEh-f#?vk~l{Cw!{ZVlc}G|B~vXQlkX*wftvA5&#QL4h`-%7kIJm~ie~rpSFeYJmHf z1;9}n@B;1e@L|Q3y-TXN-AH+hv0|f<@Yre%g~tS&^Dzr2LSg4XHeTq-Z@V z6@V1&Mx_G9O*q%4B<3@1w`lNoVil|x>uFN{VC)m#qt((2JxDr&DnmWnWf`8d$dLba zVkb@$;TZz-%B&^7g`M9KPT%43cZmE}I9B;vVV+370r-h_^v@nXpcN$Ukk;c3O)Zc) zl*=31n2%40mKQ3>#b#$2ANr~1IDX!P3lrK_P1JP1EWyZGws63ui>t&>P*v@98cqH%~BNZr?Vf<2>^g%7})F`wQ z^Z3p$seK=#mWp~PC`2HSwnW>tBZS$RXGKjN-eg5h9o}Sxoh8fv>!C2caDMI|D5+B3 zV!8Z(AJm)`I$EK@eF5hTRl)J5Yz^**(ve1Ke_#zTumTLMKyVyrUMqqIM~a2-+#D?lvOumXWqWncv%rTZsO?G%Xh zMqaSE*zh_(SZpeG`*le}MIm|$mGVaobua3PIy!ri@{njd<%+k%-%uA?^g3w^{-{eM zaw4St(;BW7JJAbJPxK3`ot_h62M1X$K<4wbXrRnj9;v<+C*_wmJ4<)Bhsrt!xOh_l zN^ha(pNGDp}yl}t}5q^6zQmx4^k;c$``3rBV~pEq#;Gk1fkx_%YwjtIjmOo0zuFS zP7$H46g_%VxyHs3h*Gj>wES#vW9>qqzcew@ zNO2q)Wu!Qcg5Ui|UhPq@Wb|Bm$_IX2gcN0OkG@EG zXnXWSjhOrCaJGly>JPX+wzQXZQgGbw;C@ZjmCpvUos^xlM}3mfkLnzq#|b{J=olwD zpiYi)g8!eU0EY)vxeln4W1Q%KI$eZwe|nxbe&1=Nd~nun22!k32h{00DSHRhX%UWZ z>u^87eK1q#0BtS-l$Um3?hO935C)muz8-XEr?W1KqCS#zfF&-Lb1JqlA62{_A64rP zsP$->NBwkg89O=v?if4xwtao<2*+sS>@TWE54U^C8ib+kazIP`2aJr_8^bzcgD9E7WpCQP=S!Vko344c=|$M__qYCHN^7-~WPRO5^CTYXIF2)P|I zw2qM5;aMG}XX#@~N9kE(K!>{Mh+5=ghODPpi%XGWEjk(&*b%juhyGTF>lWA%6y{;> zt;6*(r6by6zI?KPZNUdE9TwOTwMY|}862HQk&b8!$0)+PD?37OJ09Iy@&Fk`~~+tkQ5eBz5>oxQpk6qb-WWRR$(Khkis!!wt^~holaV3Wuc1 zqrxG_BKd5F!VfUu&VIq?HywV_#l_nB|EOqRYAn#_GRoS!NNcD6(@MK&)~>9#i(&0u zT&%r|F>-W)Y_xXe9bF7-?;`C)G^R;w=R=VbNWsMNa}nT1HoC(WOR; z7F}kfXwl_JQ7c`I_UxCk4>PA{aU=J$ zr5)PnXZ)|6Tf`T@I)T&88xe#!B(`A$c@4il5xOZMPu-Y!oM zi5ntM%;JBoiMKs`F(2VOS^UKjlngz=@)Iyy?TC?#mKACzM8AS?mwWB7x3Bp8$=>!` z9(Z7R{momxX06qA95P@{RnmCe(q6QuVDhTbaE9|PrJ%C5rrcIDed;6}yK1P!k*w0` z^$oRCkt!{zL8z{_;baXKf@j$(OJ6t~p^SZAx9M!XWxsHvzO>_1$x4iMj2g5+`wHFR8DBb&ro#hy?-wBV%T-M3xn z`FB<9B{q3d#Xcg!!z$he3$uzn-KcnJL*kv=-o86^OK#-hVWWF5(;JzRx1?hA7Eu0^ zN!$h$Lp%9(RguPoT2)ug_{_-l$KMZKTHXX5 zE01VRSw--AUc)&*OVh0nN3!#jRo1$2vp|6@T0K0z|dcXAssVt+FpO>(kqf zKIg7qeWpkJU770_esj_G;9jTOqR%YDzcKgEhTrj|CA?6XTxEInw0dc2#%asxHD%>> z^$jI8Wwxu>1JIqPmCTw7m$uPDYwH}wm=5J-X-)1Z#K4<6XFOE!eO2S;g|D_nd3lENa5Ix)D=TS0msek3TUv!*&`(uE zrL%qpL1R^=l{Tf-sxqjos$!O{s)1T_(XvB@#ZX}$>@N_$OV`ZW^M%+mYU|qlMc-W3`SG@>P^|~kpD|<1!!xP2Nz?1A zYRc>Dq0U*hl8Ocl487$k;Iz7OWt!>GZf_i}wbiq4)bJ%b2A?9r(oq!Wfz=2SDC(fZ z0&xzWz`1eMhj7{wfz3;yJEiO_dn-} zr5KKKnm9;@irtp)zPZC`sK#I<^r6~cT!6<9-$w(cd2r#qxiwndsPdTyvi5IEPyc$^ zhY$AuCGpM8*d&w_gx0#o3k+)No^rV8(fA49Rtv#_;hmZBS9#3K3tz5~2(~E2OqoAgyc5U(?n*&=wa)8pZBU z7MXfG`m$>2|GC8li$&=7G9k@83oWs^#reC@*&`*_y*ugIjE+kS&J6uHVC9CkIB%Gt zGiJZOb; zm{80sLNTl0I$S6;(z14VKf*rVVK`#MB2yp#@6e8q_+DzqDoQ=P#bcE51f_D^RRp@x zPJFKkM<@Gc4_R>2!CzN?yzGUxXovT1yoR&h)J|r3863-5IXO^D5R}7z+}utg^XTz8 zLLO$o)C?Bq_TNfpDPhNpl_ho6<@L6L%BpG8>Px%hhSN{p&T-=B zK9?J^UM=R*@3`^jzASGtx!CMJmy7v)fW3L{Igko)*g`9{=ToM<)v!WIB6XxKtG1!O zVR}VH1r^gy8k%3rhqnfJwOt-r@Z*D zw)txpIAFo|TzRGofIGNk@r(toN^5@v{!$ms5*Obd%ZnyLvteoj-lMX? z2~{FEeW?-z&^7<+q!|{N#JaiY|8DsIdD5);$uY{yi`rl?pB!VJx{fC6vH9fKXiwLZ zW6meHq`Usb$uZ}XVRUBalU;1uf$be9wRm!@G02hrpPU@yNj7#OJlREObUqnICmiT> z@{F$MoX^R)>q)WZC&AdZ9DUJ4dSGmpznh-~3zaAKkg1*UqUqs*d})?Cj5cM4aojP( zu#5&99+z0Ii#3joR64c z+!eG6FG;Tv_261Cy)aLX5lpe&Cg9W&)P~hxo}ju+V$sef0;<-JJ>atwjXh`&P?(5i zJw4AGDZ7#KK?;tjQSn78)ksibVVERQG3vQQT64< z9yAB{%Z)u~z8&E{xgM|Rh2S4qbM2kYW=Bwa>+G3IKt)N!(_fQ}9clJOlZ+i{_Df~P zjx=9VQ;IHGFXscyYmJmIFzbwz6|2?tNKp$h z&ogKQiwCeEs?!J&4@zpZ*m8-_^Ia9VRT9`u=6jnwk5SbL*asxo%0X~U0T$3p4GTh@ZM>y7Y; zv19FR)I#M2fkM7Py&Y2ejx|qoZNu_{PLFkB8G85(xwpu6WjX1BKDY6ec96{8@|8^O{3upzNnMhvG#==_hOey`GT)Yo%rtt zMj5`q38%vsd{L*nu)C}3Vct@a)!A*!iM;abn|z zz310De&aC{kT5$ZV%-4s;qDvevc}%?AJ}`w_6GKz`Wje7T7ouNMIcyvYYe zjO}Xq^sju{T;W@JBNMITZFUPhwyI031A-hV)!9*S!O*7n+?&Ua8!UJHA10{47$AJ>o-*KRX^;f3MS-j&w z31^r}cIt>no*V~Ccx=dVpo9l!<{9qEqbTz+Fhrs}9<0XkQ^%(ozo*K;Z+>ckP5OMy zK+UgIiY~hpLcsFj}b!hy<_ zNYP4pkG2QDc`2LsS5_JfE9E&B<9!p_6ZWhkZ{(hr!Uds=0nf?pTT) z9ZMbYV43b!;Yhi96}R23cC``Dqhzn*h?r_o3ihn*((rOHd891dPLHzL)!{83AXC1^ zyT%>J$+YVS$etAZJOys-A$T`Qs~K#k8hNLyJd6j(93IB0u*1V}qm7h40Adjh{{eb6 zXs|}XkaHJ~q$xT1FvM;R?u)6#8Uu^ob+dt`TORLamtiYiSsss?JtseuT~T-TBHq{1 zi)ew~n5yvOE8fEq-+Z{$Agmf65PL!G0O%bM^%ic&4-y+itYsZ03fzY0y)gbAyuS}h z9*1|Lo^l1*rftK^IVR?>+=gf8-R5n0l;LDTE@L28e$nP-jIO8<6ew?QLqharjPou1 zApjN3(|OLf|7=8lI9Snr=nu}z32J$P_c6S4VLoFHGmL2j?7Wy}%#McNq{nAyh|ie$ zjET>f`7FVd%fBQJzMv6kb5nDbuF6M5%K7zbL$|GoZi zY2Z&xi#`2iM~xPCT!b85c>9m1^D9HP+-Mc&_J8y+yYNE@vefO2xfnM4PKeF(ICfZf8wWQG z?iRA@X6SFw>8;eC6^f%zQGe7MXIf(rc&|JgfqLUqggUtaf%@X)fcN;=&BGXkw+$gs zub&_|>yi2>D5;)mOE0f4uWP_-!nBiEsd&jC@>E?pEl^iWaZfNOUFyGuw`@vz^w)H` zynj;!YNgpi{mrln nlR@sVT88n|zH^V*GNvPk6NPDxu(I!*dv}-J%kE0vd;j;n|9zQd?%p|b=A1Lz0-bzbaqz`ZqQZk4r3;aEK zyn(;0?=9tz`f7(lL(<{HDCTJq*NiIdUyC?|2Lt~eVUr!)<8US9KIAx(h2uIbRhXTb zlbIwIL?k7p3PplJIihUAzbOS#GQ_h`24Ao+3(Dn?jUb{R4D1fqHq>=Hg>{(5g_@Tp zv$QtGTKIVb20o#NumdQ(+0@mFy0(HVi|nJ?+@v6`5U0)pzND~3sv9jp=1V?XtRPTc~fCTq3o^@Z4pD~ix{oGfWaajZt9HPb+i`Dv5`F+9GA(fbP+EaI=RFDWYeGFoccc&Tk! zV13P6$0xXYt#_befwZ^KE2h`K1Z_zQWgJx1>dN^m)^_4rz_S770ai9v){fTJE+ZUz z#o5aWaSolhonfY7p;SLCN|ZQU8OaK!A~0!?wR3h+rUPXp)diGb#l&JzlcIW&uEdRy z5hzMmflM-KG0vU>#5wem0<4^@K<*Lt0V9BmGjV_qG*xWZRiN0Yt3auRKXD{Im6_2! zQ07O8rohtQzlo_Q=?0c=Vk&Dd=PPSWv2JQvOU=g!`BNeQtIATP0O1(mD7fq~L^Q*3 z*sH=<2S;wI@c%!}b#ykV!B)*sz zCr^bMI_l`}rtoovp{$?fYT(P%z-f7!(g1vO zi>ohX{cNM?;EIfbFEldxLGK4W!lnC*t27#(lyuDUm?h!vqwrY}Ln!z=K%kOrygR6Y zJF0)N-2D~erWsRN>xf{B;3Nnl%|8LR|+nda|tCUu7oLgxsj2Xo*C$Q3k5fk!GS)l zXIlR_1ETO5dd5{T1!u^KD`g6P4JK{GPp$`CJ*&cR2XGdyyYU7So0cDMS@-}%MNx@{ zv^dQ>k7x=#y;V6;$A1byv`M5;nHTL&Gp1w=Gk2c5SCL^N9mUAP5AG+PJ#y#+)x zTL-tcT0lgzbzmN>Km-b^#*Ul2X6xYQT9YJdiDw%9juU?xhQE;>^#Q9%K7huKd7mD^_PA*^xtl+b# z4kfOrcLZte0or(6{(2aRknNQzi;p_;pgmlBb3ks9}qJS$fqlFx8TZy=%t)~8)bBB_$A zM>a}#?b;YG1PX1w^yOl?t*kVzO~-&c;du z8#>fbY_vp4uW#Q{&BmC?#=SWkI}L2;U_`OuiISeZ`b{;PHcU1i&DnI-zy`w=2aE!W z(iSDv-F>c_jR})YkLGNgHL$@TCuPGICEfhvjcPWgOg5g)*|=$7Lx(M@H#(xE&rfcs zW@CnIJT!ZnC9v^mfu~i4?yss-&897rjS5e**1$&5)2c!r?%$`HO*6g{mfbl-1}RkJZ?vQgn_ z?i$!AdRkTJr|KV7v#~%no|-*P0Bk&4;Aw8|i*{e9*7O#&ffyL|g5%l8}fA zM^qEFZXyaE#U$F9K~&L;U;{QXOf^xPCZga!Oro|7qKZC4$kIdKs3vOLL=?P*Nz{Qs zRMA5SDN0&gc6mTb^Yu;gBF$nkCAgz*;y{gC#T0j_@AW)zU~}2ya1QZ$3CaU6`95FHX(O zn9rY}Opt9R(IfOx1U%f54y2n7(`ZvO2_%YhVi^Nmb5jg>aKa2LabnH_H7J(&N+lho z(gJpCc()7hMuJmB!Zbm!C_Q~XzmOqUTUdGrlN6W?3gOt10PaN~T{?8TNh(}VQsZ7CTzdvC z56)sX;R?twcoGc9CR7`zA}+MCW3=H)_|PU7&Vgi8b6=tKY!`BIF4Be;&Wsk?68&Z^ zlzEww?)Z|SZ+=#Kd`7%DGkcsMMwBm}&;Lr96uXDS5RR1P!_gtMeFkNJJnA(LbRGlZ z;Eb<6CAadGF?^&3732YO3P%pD()W0XaO@Btyb{v9CnZT zi{cYgGm-_cR$vIqF$B%_2Z7M{Q$f&V5RhIYP%Z!i#&SRbO%7dA8ziFx;R1FmSY#*= zhuFs?Zlr=Z9skr2m-ufuhI4o%?yg82Vjz>au?pgJ98^P`o*J+z;vR~`Ar>+M0;i1% z;usHUi)kyWWdU24^Z-MW0U24j;0n3QCS#jGfACyg7Wg6>y=kD~ZiUQfxTWzw9R7wd zL4vpxnwpU&N|cWO5QZ@AATEVchE4;cCn`k%j@%pNu}Btua0`P6iqj`vAU_Eg<%+Z8 zA-pgGGs^-3b2t#jo@RqjVPK(GG*mo=Jq%fY%VDEK>hR!FMFfVUm9ngG?uG}%aMa*o zL?ng7T?Y;?L5uKAv1mR&LAeQA7lsEm5rYi4BZ7+@iZkqp$RZf-W;DfBx+IHWbV(M$ z=+XkV9`PZtr5uqfrVKom!PFE;M^IgEZeqmp-VDJ=n_V*o<&-q zDjQ`Hw6s9)kCsG2vGlEYH5J{kV8x10lqwd0>?-={=9IOv$F_q*| z9@K<^111?Bnqr8Io@73p3dQ|4YIQ;G@Oi{?p9z$%kxy^5$ZP&&Lbg05$dM%C>)aAj zGNvaS1zrbkUPLM@E8(k%2sby9J5Fvy^5-lPem|BZ8%apxhJIvfbSznYq~weBq)GyR zn5$rwQ%1_5EGerZ5K;)l72=*GA?{UG00%iSmqAV;&u(AZWat^7JQDg_>c*CHGoZO0Ix;?4~9JXM5^GR8+|e#<~dO@;ey1}1mhzYDmoi|HAsns=SO&qJ$Tj!l0b<)MKZ^M+vNtgHjntjlI-LDr{&mC@A{FcKTmtOpSQPVPPK z@xk#Y%Ia)LXKwbG?BcEGxH4(;lHCWV2OkvNj($_@JnU@tz>5zz zjqQBLQRl_3C67DASQ@@J=y%V>G5G$aH>Umj6~7lfbiZ-+o$)$(?j=x?{-n|vE9ir?Q`_()*3WHayN z{b2v|DfjYxj}=ZlH)ZVxTmG>8tf_0hzqMz?;m9j%dS7rgU)O)H%Oc~}({Dr^j;aVs zymHR)>eqd5c_i2rZkRV_#C6NX%iGs_hW_%WS=4o}kri_{o|Ei8=~A^k;&|b9$KU&M zGrSgtrq(5_+SvU;^pTQ{qRrPjUEZ_*ea&a+O7o7UjVm>q1Rb0S8Q;W%Lys19SGb13 zWH(uqjqage6{dSqU>I<(VAa@?8(zXg_I(p}ov%3b7YQ!!5O`+g)fKm{|NdZ-*TuUg z)k{MRzHuzqHkc{8;5>cb%khpI%s1ZbZ*j^o{oWbVhfng`o%?Zio4W(f<_s*bt}dCf zZO;X&*OxYmV4$U4Eg$|<0hjXCl|Y0lq?)w|3OPj zcWqxUt%J{2FD3O}>r#H1``FgUy~EIjP8-6D{fq@yvMwGueCgzw-s8n(F5>XXYkSx8 z{`|Oy%=4&=UefPQ?fd-v%q{^@_rx>XM-1>9V%Wa-&5!TaOv{fwWz(5o7k!}kP~g^> zy@&SL2Ddx?+V4_--4{u5HbaiDnZ9>DdqmN1lSlU$wWod?_ZN=~*p0ff!c8kgoYx3`RYS z*s-S}OYg-dFEL>1CZQI33lcJ8XT=vW~*XMl>JaR3W zcZ+7+*n~F}-GtnYGD!g79+vK;NRvgC<0@ zUHm@Fmmg%>5JdL9E1LEucFwjnL#j;wtomdVZc*#{XzQ6NUL@je+#>$mN2zry;;Odn zkDhg@Ag5u>)3y07mx^LmCB+Kw&YdBAsB?1D38MisfhAC~{;9#a#o^^aLv?-E>(q(vxf@2+|Mc*k*3&T$-+U-4Kh!@(zpD4?xjH!~@~1!2 z?c%pcSiWOa<=h=J3;NA5v)X!}7ZteP`h<6f&>0K;%97ciw%L5v;q`p8{_|odkLon=dpe77rai{F}Av#(8@XPuh&%(!Ol6GPsfa-a3{Uq3EA^)h;_TWD>q z)r8sl#&HGx2KAmfhG+iqyOoRjxW#rHuyBmd#{&1~)!SZW4UPP3{LPv!ZO>a6ZxQ3N3f^8`QN${M(G!&G;}q{Ph+GIC(bk%wm;rqk$0))Z2jtQ`WZ~j+ikSovF7@vse#TWj+68!#vYonuKKqGhl7uK%ktKA zTxpnmXCyCK_^vFm#ADOGX^Z=29XIJ3zvZvf-+F`{e7pa__RT%x#%@h*w_&z>=W15z z&I_x?E!r@1-FJ3rg=_9yJ(cIxD*tSvd$%RMBCQISnwE_6+LP#My=t@FA-C1SWXl}8 zx}3g0*)RWgrOwjgX#FSC7LO`iA9KQXYHG@7(lDmy&4-K5Z}a&jCiA%d#W}*#Q{KrT z1uLS(+FpP9z^y~8do@*|qHU;+*-c7XXvnJ#XF-rbPkO78MsqW z$4xmGbJyVKr|+){2knZRw`t?d18%#MBF_563olH#wPX^NK!coV3LI*o9+pFF9bawIRq^zeue;af?(@1jlr|jGj75^Ii+w`_+iPc7*6Apxo zG97-;@n6fr{i?rTvf~^(?wd3x;kNwSDQ!;&n#KA~J)Z5q;CQuRoKClmGfHNb+~{uc zU0}yI(Y1rQmX7J43}gQEy)Y^=aEYLPyUz3PpRaVX+xGKR&%5s*Z!|6bvtwq=`ElF1 z!-nl0a5S_1^XxXihd+9Ie>kt#+#PMwdhPYRR&ryDlWy?t`xXhtLciIAj(z*;T(|yj zS>wAbcK_%blJqk1gP@nu`h#}k51uV_OKlrBEcb){nt6`Cw(fm4{BY|1npu-acm5c* z?pG`GAI*N5GFaEMU>YyhEfzH5hkNDkUN;Z!|eY!AEk{p8T$-&*eK9c6g9`oyHn)$Eg3(h}db zHS0L+$@;{GMtD>YRqAtfQOS5AExy5hk_`~2c*-XFx?X_CX*W`j^$@& z{&LW8p52}4m$zNxOs?x;;OTEO@OiyKg(Kg2TKv(n)I&ckJJ&|&_M2};a!on04ax9alsg7V8VW8N24c(0i| zxXxdX^1n=LgOE)f}`1zRS#2ct!XL+{OT z293MZ(%)PfD*fhCkBuv6I^jxXBK&HoH>@cQU{3<~aM-XyZ%y4B!4(gB^U0@8udaqA zMEZ}Q;9V(rH|jc(f)r6}9-5{t?3}>)4%P&yIS%U~`j^_1DD279)lQE6*dKpRMW@I9 z_VW9e{tgQFVerdBjsQ9a^1(tM@(>UVvkbNh-ZrtI23NND`^_P2ti@F8FZ*VZK~SSh z3UChuQZ*lc1Hh)r*s7~>Yj97x%!Vz?t~}HNT?SpUWcUR3OEIC`ENZdBXTv@MdSt8;9`4%dkrPTVT&3&LzonDY!#y@VqRfM*qAl)v>XAiq zdY_@!D2pHTj}W?~3pR$CJXJ!OP#1(8g=szZIY9aegm-lK!T-)+4scy~b3-Sa0P6cQ z{Hj=+uq%-$cB3_9LXN=J4F}Ls_xu37T2R-Q{+b~=lsMRRR6TBTbcXE;nhq4H#3h_Y zJ<f|8W>oW)tNDy-9p!2rrD`}VB&$Ycqc60ED=NtvzLk{(ehe>g zZL#0%N}bB~hSR~cCV+-&QvA>wM<;3xbx6zwauYE$GuT(sfppR(omiv~aH@n`o+&H= z1FMNC$5aa`^VCpAS8NSZ(6$6h@;)JDa46U#?*j}gDZ*SNrLBV0b$};;e6xU%1-z8$ zPi$FXWGL~2<5PRlsli_{mBNRRV%V=KmW34xKA)@vAZ32lil%fEM#TT!$cu56 z1F;;V2a7VJO!8VB$&m0{DXvQ6Bxph!CuuyqQr4o10o0YgG`E)2N^~_9#o+eeW+eqt z{<-}(Y5)z1K^?;k{{Iy^c42s-pQQ-HQG?(Ar2l^!fG02E=*osO7+RVTQafeE>-P~U zhFj+GNw+S$Z@2*LC&=g)O92$M6P{TUYCzx7K3;Hc+s(zrLm=l1k9atg1h1gH;XN82EdeV!Q*t|d zlea#*#{3O&{z3hl8emCJ6=lH-`~))UZ?>lY507G0YzYAzKUpn7Y$49y<=;ZGEk^rK G=Ko)6T4J#P literal 0 HcmV?d00001 From 8a127066208dee9e499c55973b2cd2389dce157d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 13:09:52 -0400 Subject: [PATCH 0261/1694] #211 Support for IAT #211 Support for IAT --- batchIAT.go | 51 +++ iatBatch.go | 382 +++++++++++++++++++ iatBatchHeader.go | 423 +++++++++++++++++++++ iatBatchHeader_test.go | 811 +++++++++++++++++++++++++++++++++++++++++ iatBatcher.go | 55 +++ iatEntryDetail.go | 269 ++++++++++++++ iatEntryDetail_test.go | 490 +++++++++++++++++++++++++ reader.go | 53 +++ validators.go | 25 ++ 9 files changed, 2559 insertions(+) create mode 100644 batchIAT.go create mode 100644 iatBatch.go create mode 100644 iatBatchHeader.go create mode 100644 iatBatchHeader_test.go create mode 100644 iatBatcher.go create mode 100644 iatEntryDetail.go create mode 100644 iatEntryDetail_test.go diff --git a/batchIAT.go b/batchIAT.go new file mode 100644 index 000000000..e6a0fcbed --- /dev/null +++ b/batchIAT.go @@ -0,0 +1,51 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +// BatchIAT holds the Batch Header and Batch Control and all Entry Records for IAT Entries +type BatchIAT struct { + iatBatch +} + +// NewBatchIAT returns a *BatchIAT +func NewBatchIAT(bh *IATBatchHeader) *BatchIAT { + iatBatch := new(BatchIAT) + iatBatch.SetControl(NewBatchControl()) + iatBatch.SetHeader(bh) + return iatBatch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchIAT) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // Batch can have one addenda per entry record + /* if err := batch.isAddendaCount(1); err != nil { + return err + } + if err := batch.isTypeCode("05"); err != nil { + return err + }*/ + + // Add type specific validation. + // ... + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchIAT) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + + return batch.Validate() +} diff --git a/iatBatch.go b/iatBatch.go new file mode 100644 index 000000000..9a3b7ae09 --- /dev/null +++ b/iatBatch.go @@ -0,0 +1,382 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" + "strconv" +) + +// Batch holds the Batch Header and Batch Control and all Entry Records +type iatBatch struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + Header *IATBatchHeader `json:"IATbatchHeader,omitempty"` + Entries []*IATEntryDetail `json:"IATentryDetails,omitempty"` + Control *BatchControl `json:"batchControl,omitempty"` + + // category defines if the entry is a Forward, Return, or NOC + category string + // Converters is composed for ACH to GoLang Converters + converters +} + +// IATNewBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported. +func IATNewBatch(bh *IATBatchHeader) (IATBatcher, error) { + return NewBatchIAT(bh), nil +} + +// verify checks basic valid NACHA batch rules. Assumes properly parsed records. This does not mean it is a valid batch as validity is tied to each batch type +func (batch *iatBatch) verify() error { + batchNumber := batch.Header.BatchNumber + + // verify field inclusion in all the records of the batch. + if err := batch.isFieldInclusion(); err != nil { + // convert the field error in to a batch error for a consistent api + if e, ok := err.(*FieldError); ok { + return &BatchError{BatchNumber: batchNumber, FieldName: e.FieldName, Msg: e.Msg} + } + return &BatchError{BatchNumber: batchNumber, FieldName: "FieldError", Msg: err.Error()} + } + // validate batch header and control codes are the same + if batch.Header.ServiceClassCode != batch.Control.ServiceClassCode { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ServiceClassCode, batch.Control.ServiceClassCode) + return &BatchError{BatchNumber: batchNumber, FieldName: "ServiceClassCode", Msg: msg} + } + // Company Identification must match the Company ID from the batch header record + /* if batch.Header.CompanyIdentification != batch.Control.CompanyIdentification { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.CompanyIdentification, batch.Control.CompanyIdentification) + return &BatchError{BatchNumber: batchNumber, FieldName: "CompanyIdentification", Msg: msg} + }*/ + // Control ODFIIdentification must be the same as batch header + if batch.Header.ODFIIdentification != batch.Control.ODFIIdentification { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) + return &BatchError{BatchNumber: batchNumber, FieldName: "ODFIIdentification", Msg: msg} + } + // batch number header and control must match + if batch.Header.BatchNumber != batch.Control.BatchNumber { + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) + return &BatchError{BatchNumber: batchNumber, FieldName: "BatchNumber", Msg: msg} + } + + if err := batch.isBatchEntryCount(); err != nil { + return err + } + + if err := batch.isSequenceAscending(); err != nil { + return err + } + + if err := batch.isBatchAmount(); err != nil { + return err + } + + if err := batch.isEntryHash(); err != nil { + return err + } + + if err := batch.isOriginatorDNE(); err != nil { + return err + } + + if err := batch.isTraceNumberODFI(); err != nil { + return err + } + // TODO this is specific to batch SEC types and should be called by that validator + if err := batch.isAddendaSequence(); err != nil { + return err + } + return batch.isCategory() +} + +// Build creates valid batch by building sequence numbers and batch batch control. An error is returned if +// the batch being built has invalid records. +func (batch *iatBatch) build() error { + // Requires a valid BatchHeader + if err := batch.Header.Validate(); err != nil { + return err + } + if len(batch.Entries) <= 0 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} + } + // Create record sequence numbers + entryCount := 0 + seq := 1 + for i, entry := range batch.Entries { + entryCount = entryCount + 1 + len(entry.Addendum) + currentTraceNumberODFI, err := strconv.Atoi(entry.TraceNumberField()[:8]) + if err != nil { + return err + } + + batchHeaderODFI, err := strconv.Atoi(batch.Header.ODFIIdentificationField()[:8]) + if err != nil { + return err + } + + // Add a sequenced TraceNumber if one is not already set. Have to keep original trance number Return and NOC entries + if currentTraceNumberODFI != batchHeaderODFI { + batch.Entries[i].SetTraceNumber(batch.Header.ODFIIdentification, seq) + } + seq++ + addendaSeq := 1 + for x := range entry.Addendum { + // sequences don't exist in NOC or Return addenda + if a, ok := batch.Entries[i].Addendum[x].(*Addenda05); ok { + a.SequenceNumber = addendaSeq + a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) + } + addendaSeq++ + } + } + + // build a BatchControl record + bc := NewBatchControl() + bc.ServiceClassCode = batch.Header.ServiceClassCode + /*bc.CompanyIdentification = iatBatch.Header.CompanyIdentification*/ + bc.ODFIIdentification = batch.Header.ODFIIdentification + bc.BatchNumber = batch.Header.BatchNumber + bc.EntryAddendaCount = entryCount + bc.EntryHash = batch.parseNumField(batch.calculateEntryHash()) + bc.TotalCreditEntryDollarAmount, bc.TotalDebitEntryDollarAmount = batch.calculateBatchAmounts() + batch.Control = bc + + return nil +} + +// SetHeader appends an BatchHeader to the Batch +func (batch *iatBatch) SetHeader(batchHeader *IATBatchHeader) { + batch.Header = batchHeader +} + +// GetHeader returns the current Batch header +func (batch *iatBatch) GetHeader() *IATBatchHeader { + return batch.Header +} + +// SetControl appends an BatchControl to the Batch +func (batch *iatBatch) SetControl(batchControl *BatchControl) { + batch.Control = batchControl +} + +// GetControl returns the current Batch Control +func (batch *iatBatch) GetControl() *BatchControl { + return batch.Control +} + +// GetEntries returns a slice of entry details for the batch +func (batch *iatBatch) GetEntries() []*IATEntryDetail { + return batch.Entries +} + +// AddEntry appends an EntryDetail to the Batch +func (batch *iatBatch) AddEntry(entry *IATEntryDetail) { + batch.category = entry.Category + batch.Entries = append(batch.Entries, entry) +} + +// IsReturn is true if the batch contains an Entry Return +func (batch *iatBatch) Category() string { + return batch.category +} + +// isFieldInclusion iterates through all the records in the batch and verifies against default fields +func (batch *iatBatch) isFieldInclusion() error { + if err := batch.Header.Validate(); err != nil { + return err + } + for _, entry := range batch.Entries { + if err := entry.Validate(); err != nil { + return err + } + for _, addenda := range entry.Addendum { + if err := addenda.Validate(); err != nil { + return nil + } + } + } + return batch.Control.Validate() +} + +// isBatchEntryCount validate Entry count is accurate +// The Entry/Addenda Count Field is a tally of each Entry Detail and Addenda +// Record processed within the batch +func (batch *iatBatch) isBatchEntryCount() error { + entryCount := 0 + for _, entry := range batch.Entries { + entryCount = entryCount + 1 + len(entry.Addendum) + } + if entryCount != batch.Control.EntryAddendaCount { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.Control.EntryAddendaCount) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "EntryAddendaCount", Msg: msg} + } + return nil +} + +// isBatchAmount validate Amount is the same as what is in the Entries +// The Total Debit and Credit Entry Dollar Amount fields contain accumulated +// Entry Detail debit and credit totals within a given batch +func (batch *iatBatch) isBatchAmount() error { + credit, debit := batch.calculateBatchAmounts() + if debit != batch.Control.TotalDebitEntryDollarAmount { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, debit, batch.Control.TotalDebitEntryDollarAmount) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TotalDebitEntryDollarAmount", Msg: msg} + } + + if credit != batch.Control.TotalCreditEntryDollarAmount { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, credit, batch.Control.TotalCreditEntryDollarAmount) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TotalCreditEntryDollarAmount", Msg: msg} + } + return nil +} + +func (batch *iatBatch) calculateBatchAmounts() (credit int, debit int) { + for _, entry := range batch.Entries { + if entry.TransactionCode == 21 || entry.TransactionCode == 22 || entry.TransactionCode == 23 || entry.TransactionCode == 32 || entry.TransactionCode == 33 { + credit = credit + entry.Amount + } + if entry.TransactionCode == 26 || entry.TransactionCode == 27 || entry.TransactionCode == 28 || entry.TransactionCode == 36 || entry.TransactionCode == 37 || entry.TransactionCode == 38 { + debit = debit + entry.Amount + } + } + return credit, debit +} + +// isSequenceAscending Individual Entry Detail Records within individual batches must +// be in ascending Trace Number order (although Trace Numbers need not necessarily be consecutive). +func (batch *iatBatch) isSequenceAscending() error { + lastSeq := -1 + for _, entry := range batch.Entries { + if entry.TraceNumber <= lastSeq { + msg := fmt.Sprintf(msgBatchAscending, entry.TraceNumber, lastSeq) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + } + lastSeq = entry.TraceNumber + } + return nil +} + +// isEntryHash validates the hash by recalculating the result +func (batch *iatBatch) isEntryHash() error { + hashField := batch.calculateEntryHash() + if hashField != batch.Control.EntryHashField() { + msg := fmt.Sprintf(msgBatchCalculatedControlEquality, hashField, batch.Control.EntryHashField()) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "EntryHash", Msg: msg} + } + return nil +} + +// calculateEntryHash This field is prepared by hashing the 8-digit Routing Number in each entry. +// The Entry Hash provides a check against inadvertent alteration of data +func (batch *iatBatch) calculateEntryHash() string { + hash := 0 + for _, entry := range batch.Entries { + + entryRDFI, _ := strconv.Atoi(entry.RDFIIdentification) + + hash = hash + entryRDFI + } + return batch.numericField(hash, 10) +} + +// The Originator Status Code is not equal to “2” for DNE if the Transaction Code is 23 or 33 +func (batch *iatBatch) isOriginatorDNE() error { + if batch.Header.OriginatorStatusCode != 2 { + for _, entry := range batch.Entries { + if entry.TransactionCode == 23 || entry.TransactionCode == 33 { + msg := fmt.Sprintf(msgBatchOriginatorDNE, batch.Header.OriginatorStatusCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "OriginatorStatusCode", Msg: msg} + } + } + } + return nil +} + +// isTraceNumberODFI checks if the first 8 positions of the entry detail trace number +// match the batch header ODFI +func (batch *iatBatch) isTraceNumberODFI() error { + for _, entry := range batch.Entries { + if batch.Header.ODFIIdentificationField() != entry.TraceNumberField()[:8] { + msg := fmt.Sprintf(msgBatchTraceNumberNotODFI, batch.Header.ODFIIdentificationField(), entry.TraceNumberField()[:8]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ODFIIdentificationField", Msg: msg} + } + } + + return nil +} + +// isAddendaSequence check multiple errors on addenda records in the batch entries +func (batch *iatBatch) isAddendaSequence() error { + for _, entry := range batch.Entries { + if len(entry.Addendum) > 0 { + // addenda without indicator flag of 1 + if entry.AddendaRecordIndicator != 1 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} + } + lastSeq := -1 + // check if sequence is ascending + for _, addenda := range entry.Addendum { + // sequences don't exist in NOC or Return addenda + if a, ok := addenda.(*Addenda05); ok { + + if a.SequenceNumber < lastSeq { + msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastSeq) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} + } + lastSeq = a.SequenceNumber + // check that we are in the correct Entry Detail + if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) { + msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + } + } + } + } + } + return nil +} + +// isAddendaCount iterates through each entry detail and checks the number of addendum is greater than the count parameter otherwise it returns an error. +// Following SEC codes allow for none or one Addendum +// "PPD", "WEB", "CCD", "CIE", "DNE", "MTE", "POS", "SHR" +func (batch *iatBatch) isAddendaCount(count int) error { + for _, entry := range batch.Entries { + if len(entry.Addendum) > count { + msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.Header.StandardEntryClassCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaCount", Msg: msg} + } + + } + return nil +} + +// isTypeCode takes a TypeCode string and verifies Addenda records match +func (batch *iatBatch) isTypeCode(typeCode string) error { + for _, entry := range batch.Entries { + for _, addenda := range entry.Addendum { + if addenda.TypeCode() != typeCode { + msg := fmt.Sprintf(msgBatchTypeCode, addenda.TypeCode(), typeCode, batch.Header.StandardEntryClassCode) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TypeCode", Msg: msg} + } + } + } + return nil +} + +// isCategory verifies that a Forward and Return Category are not in the same batch +func (batch *iatBatch) isCategory() error { + category := batch.GetEntries()[0].Category + if len(batch.Entries) > 1 { + for i := 1; i < len(batch.Entries); i++ { + if batch.Entries[i].Category == CategoryNOC { + continue + } + if batch.Entries[i].Category != category { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Category", Msg: msgBatchForwardReturn} + } + } + } + return nil +} diff --git a/iatBatchHeader.go b/iatBatchHeader.go new file mode 100644 index 000000000..22d86253f --- /dev/null +++ b/iatBatchHeader.go @@ -0,0 +1,423 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +// msgServiceClass + +// IATBatchHeader identifies the originating entity and the type of transactions +// contained in the batch for SEC Code IAT. This record also contains the effective +// date, or desired settlement date, for all entries contained in this batch. The +// settlement date field is not entered as it is determined by the ACH operator. +// +// An IAT entry is a credit or debit ACH entry that is part of a payment transaction +// involving a financial agency’s office (i.e., depository financial institution or +// business issuing money orders) that is not located in the territorial jurisdiction +// of the United States. IAT entries can be made to or from a corporate or consumer +// account and must be accompanied by seven (7) mandatory addenda records identifying +// the name and physical address of the Originator, name and physical address of the +// Receiver, Receiver’s account number, Receiver’s bank identity and reason for the payment. +type IATBatchHeader struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + + // RecordType defines the type of record in the block. 5 + recordType string + + // ServiceClassCode ACH Mixed Debits and Credits ‘200’ + // ACH Credits Only ‘220’ + // ACH Debits Only ‘225' + ServiceClassCode int `json:"serviceClassCode"` + + // IATIndicator - Leave Blank - It is only used for corrected IAT entries + IATIndicator string `json:"IATIndicator,omitempty"` + + // ForeignExchangeIndicator is a code indicating currency conversion + // + // FV Fixed-to-Variable – Entry is originated in a fixed-value amount + // and is to be received in a variable amount resulting from the + // execution of the foreign exchange conversion. + // + // VF Variable-to-Fixed – Entry is originated in a variable-value + // amount based on a specific foreign exchange rate for conversion to a + // fixed-value amount in which the entry is to be received. + // + // FF Fixed-to-Fixed – Entry is originated in a fixed-value amount and + // is to be received in the same fixed-value amount in the same + // currency denomination. There is no foreign exchange conversion for + // entries transmitted using this code. For entries originated in a fixed value + // amount, the foreign Exchange Reference Field will be space + // filled. + ForeignExchangeIndicator string `json:"foreignExchangeIndicator"` + + // ForeignExchangeReferenceIndicator is a code used to indicate the content of the + // Foreign Exchange Reference Field and is filled by the gateway operator. + // Valid entries are: + // 1 - Foreign Exchange Rate; + // 2 - Foreign Exchange Reference Number; or + // 3 - Space Filled + ForeignExchangeReferenceIndicator int `json:"foreignExchangeReferenceIndicator"` + + // 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 + // Organization for Standardization (ISO), to identify the country in which the entry is + // to be received. Values can be found on the International Organization for Standardization + // website: www.iso.org. For entries destined to account holder in the U.S., this would be US. + ISODestinationCountryCode string `json:"ISODestinationCountryCode"` + + // OriginatorIdentification identifies the following: + // For U.S. entities: the number assigned will be your tax ID + // For non-U.S. entities: the number assigned will be your DDA number, + // or the last 9 characters of your account number if it exceeds 9 characters + OriginatorIdentification string `json:"originatorIdentification"` + + // StandardEntryClassCode for consumer and non consumer international payments is IAT + // Identifies the payment type (product) found within an ACH batch-using a 3-character code. + // The SEC Code pertains to all items within batch. + // Determines format of the detail records. + // Determines addenda records (required or optional PLUS one or up to 9,999 records). + // Determines rules to follow (return time frames). + // Some SEC codes require specific data in predetermined fields within the ACH record + StandardEntryClassCode string `json:"standardEntryClassCode,omitempty"` + + // CompanyEntryDescription A description of the entries contained in the batch + // + //The Originator establishes the value of this field to provide a + // description of the purpose of the entry to be displayed back to + // the receive For example, "GAS BILL," "REG. SALARY," "INS. PREM," + // "SOC. SEC.," "DTC," "TRADE PAY," "PURCHASE," etc. + // + // This field must contain the word "REVERSAL" (left justified) when the + // batch contains reversing entries. + // + // This field must contain the word "RECLAIM" (left justified) when the + // batch contains reclamation entries. + // + // This field must contain the word "NONSETTLED" (left justified) when the + // batch contains entries which could not settle. + CompanyEntryDescription string `json:"companyEntryDescription,omitempty"` + + // ISOOriginatingCurrencyCode is the three-character code, as approved by the International + // Organization for Standardization (ISO), to identify the currency denomination in which the + // entry was first originated. If the source of funds is within the territorial jurisdiction + // of the U.S., enter 'USD', otherwise refer to International Organization for Standardization + // website for value: www.iso.org -- (Account Currency) + ISOOriginatingCurrencyCode string `json:"ISOOriginatingCurrencyCode"` + + // ISODestinationCurrencyCode is the three-character code, as approved by the International + // Organization for Standardization (ISO), to identify the currency denomination in which the + // entry will ultimately be settled. If the final destination of funds is within the territorial + // jurisdiction of the U.S., enter “USD”, otherwise refer to International Organization for + // Standardization website for value: www.iso.org -- (Payment Currency) + ISODestinationCurrencyCode string `json:"ISODestinationCurrencyCode"` + + // EffectiveEntryDate the date on which the entries are to settle format YYMMDD + EffectiveEntryDate time.Time `json:"effectiveEntryDate,omitempty"` + + // SettlementDate Leave blank, this field is inserted by the ACH operator + settlementDate string + // OriginatorStatusCode refers to the ODFI initiating the Entry. + // 0 ADV File prepared by an ACH Operator. + // 1 This code identifies the Originator as a depository financial institution. + // 2 This code identifies the Originator as a Federal Government entity or agency. + OriginatorStatusCode int `json:"originatorStatusCode,omitempty"` + + // ODFIIdentification First 8 digits of the originating DFI transit routing number + // For Inbound IAT Entries, this field contains the routing number of the U.S. Gateway + // Operator. For Outbound IAT Entries, this field contains the standard routing number, + // as assigned by Accuity, that identifies the U.S. ODFI initiating the Entry. + // Format - TTTTAAAA + ODFIIdentification string `json:"ODFIIdentification"` + + // BatchNumber is assigned in ascending sequence to each batch by the ODFI + // or its Sending Point in a given file of entries. Since the batch number + // in the Batch Header Record and the Batch Control Record is the same, + // the ascending sequence number should be assigned by batch and not by + // record. + BatchNumber int `json:"batchNumber,omitempty"` + + // validator is composed for data validation + validator + + // converters is composed for ACH to golang Converters + converters +} + +// NewIATBatchHeader returns a new BatchHeader with default values for non exported fields +func NewIATBatchHeader() *IATBatchHeader { + iatBh := &IATBatchHeader{ + recordType: "5", + OriginatorStatusCode: 0, //Prepared by an Originator + BatchNumber: 1, + } + return iatBh +} + +// Parse takes the input record string and parses the BatchHeader values +func (iatBh *IATBatchHeader) Parse(record string) { + // 1-1 Always "5" + 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 = " " + // 21-22 A code indicating currency conversion + // “FV” Fixed-to-Variable + // “VF” Variable-to-Fixed + // “FF” Fixed-to-Fixed + iatBh.ForeignExchangeIndicator = iatBh.parseStringField(record[20:22]) + // 23-23 Foreign Exchange Reference Indicator – Refers to “Foreign Exchange Reference” + // field and is filled by the gateway operator. Valid entries are: + // 1 - Foreign Exchange Rate; + // 2 - Foreign Exchange Reference Number; or + // 3 - Space Filled + iatBh.ForeignExchangeReferenceIndicator = iatBh.parseNumField(record[22:23]) + // 24-38 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. + iatBh.ForeignExchangeReference = iatBh.parseStringField(record[23:38]) + // 39-40 Receiver ISO Country Code - For entries + // destined to account holder in the U.S., this would be ‘US’. + iatBh.ISODestinationCountryCode = iatBh.parseStringField(record[38:40]) + // 41-50 For U.S. entities: the number assigned will be your tax ID + // For non-U.S. entities: the number assigned will be your DDA number, + // or the last 9 characters of your account number if it exceeds 9 characters + iatBh.OriginatorIdentification = iatBh.parseStringField(record[40:50]) + // 51-53 IAT for both consumer and non consumer international payments + iatBh.StandardEntryClassCode = record[50:53] + // 54-63 Your description of the transaction. This text will appear on the receivers’ bank statement. + // For example: "Payroll " + iatBh.CompanyEntryDescription = strings.TrimSpace(record[53:63]) + // 64-66 Originator ISO Currency Code + iatBh.ISOOriginatingCurrencyCode = iatBh.parseStringField(record[63:66]) + // 67-69 Receiver ISO Currency Code + iatBh.ISODestinationCurrencyCode = iatBh.parseStringField(record[66:69]) + // 70-75 Date transactions are to be posted to the receivers’ account. + // You almost always want the transaction to post as soon as possible, so put tomorrow's date in YYMMDD format + iatBh.EffectiveEntryDate = iatBh.parseSimpleDate(record[69:75]) + // 76-79 Always blank (just fill with spaces) + iatBh.settlementDate = " " + // 79-79 Always 1 + iatBh.OriginatorStatusCode = iatBh.parseNumField(record[78:79]) + // 80-87 Your ODFI's routing number without the last digit. The last digit is simply a + // checksum digit, which is why it is not necessary + iatBh.ODFIIdentification = iatBh.parseStringField(record[79:87]) + // 88-94 Sequential number of this Batch Header Record + // For example, put "1" if this is the first Batch Header Record in the file + iatBh.BatchNumber = iatBh.parseNumField(record[87:94]) +} + +// String writes the BatchHeader struct to a 94 character string. +func (iatBh *IATBatchHeader) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v%v%v%v%v%v", + iatBh.recordType, + iatBh.ServiceClassCode, + iatBh.IATIndicatorField(), + iatBh.ForeignExchangeIndicatorField(), + iatBh.ForeignExchangeReferenceIndicatorField(), + iatBh.ForeignExchangeReferenceField(), + iatBh.ISODestinationCountryCodeField(), + iatBh.OriginatorIdentificationField(), + iatBh.StandardEntryClassCode, + iatBh.CompanyEntryDescriptionField(), + iatBh.ISOOriginatingCurrencyCodeField(), + iatBh.ISODestinationCurrencyCodeField(), + iatBh.EffectiveEntryDateField(), + iatBh.settlementDateField(), + iatBh.OriginatorStatusCode, + iatBh.ODFIIdentificationField(), + iatBh.BatchNumberField(), + ) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (iatBh *IATBatchHeader) Validate() error { + if err := iatBh.fieldInclusion(); err != nil { + return err + } + if iatBh.recordType != "5" { + msg := fmt.Sprintf(msgRecordType, 5) + return &FieldError{FieldName: "recordType", Value: iatBh.recordType, Msg: msg} + } + if err := iatBh.isServiceClass(iatBh.ServiceClassCode); err != nil { + return &FieldError{FieldName: "ServiceClassCode", + Value: strconv.Itoa(iatBh.ServiceClassCode), Msg: err.Error()} + } + if err := iatBh.isForeignExchangeIndicator(iatBh.ForeignExchangeIndicator); err != nil { + return &FieldError{FieldName: "ForeignExchangeIndicator", + Value: iatBh.ForeignExchangeIndicator, Msg: err.Error()} + } + if err := iatBh.isForeignExchangeReferenceIndicator(iatBh.ForeignExchangeReferenceIndicator); err != nil { + return &FieldError{FieldName: "ForeignExchangeReferenceIndicator", + Value: strconv.Itoa(iatBh.ForeignExchangeReferenceIndicator), Msg: err.Error()} + } + if err := iatBh.isAlphanumeric(iatBh.ISODestinationCountryCode); err != nil { + return &FieldError{FieldName: "ISODestinationCountryCode", + Value: iatBh.ISODestinationCountryCode, Msg: err.Error()} + } + if err := iatBh.isAlphanumeric(iatBh.OriginatorIdentification); err != nil { + return &FieldError{FieldName: "OriginatorIdentification", + Value: iatBh.OriginatorIdentification, Msg: err.Error()} + } + if err := iatBh.isSECCode(iatBh.StandardEntryClassCode); err != nil { + return &FieldError{FieldName: "StandardEntryClassCode", + Value: iatBh.StandardEntryClassCode, Msg: err.Error()} + } + if err := iatBh.isAlphanumeric(iatBh.CompanyEntryDescription); err != nil { + return &FieldError{FieldName: "CompanyEntryDescription", + Value: iatBh.CompanyEntryDescription, Msg: err.Error()} + } + if err := iatBh.isAlphanumeric(iatBh.ISOOriginatingCurrencyCode); err != nil { + return &FieldError{FieldName: "ISOOriginatingCurrencyCode", + Value: iatBh.ISOOriginatingCurrencyCode, Msg: err.Error()} + } + + if err := iatBh.isAlphanumeric(iatBh.ISODestinationCurrencyCode); err != nil { + return &FieldError{FieldName: "ISODestinationCurrencyCode", + Value: iatBh.ISODestinationCurrencyCode, Msg: err.Error()} + } + if err := iatBh.isOriginatorStatusCode(iatBh.OriginatorStatusCode); err != nil { + return &FieldError{FieldName: "OriginatorStatusCode", + Value: strconv.Itoa(iatBh.OriginatorStatusCode), Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (iatBh *IATBatchHeader) fieldInclusion() error { + if iatBh.recordType == "" { + return &FieldError{FieldName: "recordType", Value: iatBh.recordType, Msg: msgFieldInclusion} + } + if iatBh.ServiceClassCode == 0 { + return &FieldError{FieldName: "ServiceClassCode", + Value: strconv.Itoa(iatBh.ServiceClassCode), Msg: msgFieldInclusion} + } + if iatBh.ForeignExchangeIndicator == "" { + return &FieldError{FieldName: "ForeignExchangeIndicator", + Value: iatBh.ForeignExchangeIndicator, Msg: msgFieldInclusion} + } + if iatBh.ForeignExchangeReferenceIndicator == 0 { + return &FieldError{FieldName: "ForeignExchangeReferenceIndicator", + Value: strconv.Itoa(iatBh.ForeignExchangeReferenceIndicator), Msg: msgFieldRequired} + } + // ToDo: It can be space filled based on ForeignExchangeReferenceIndicator just use a validator to handle - + // ToDo: Calling Field ok for validation? + /* if iatBh.ForeignExchangeReference == "" { + return &FieldError{FieldName: "ForeignExchangeReference", + Value: iatBh.ForeignExchangeReference, Msg: msgFieldRequired} + }*/ + if iatBh.ISODestinationCountryCode == "" { + return &FieldError{FieldName: "ISODestinationCountryCode", + Value: iatBh.ISODestinationCountryCode, Msg: msgFieldInclusion} + } + if iatBh.OriginatorIdentification == "" { + return &FieldError{FieldName: "OriginatorIdentification", + Value: iatBh.OriginatorIdentification, Msg: msgFieldInclusion} + } + if iatBh.StandardEntryClassCode == "" { + return &FieldError{FieldName: "StandardEntryClassCode", + Value: iatBh.StandardEntryClassCode, Msg: msgFieldInclusion} + } + if iatBh.CompanyEntryDescription == "" { + return &FieldError{FieldName: "CompanyEntryDescription", + Value: iatBh.CompanyEntryDescription, Msg: msgFieldInclusion} + } + if iatBh.ISOOriginatingCurrencyCode == "" { + return &FieldError{FieldName: "ISOOriginatingCurrencyCode", + Value: iatBh.ISOOriginatingCurrencyCode, Msg: msgFieldInclusion} + } + if iatBh.ISODestinationCurrencyCode == "" { + return &FieldError{FieldName: "ISODestinationCurrencyCode", + Value: iatBh.ISODestinationCurrencyCode, Msg: msgFieldInclusion} + } + if iatBh.ODFIIdentification == "" { + return &FieldError{FieldName: "ODFIIdentification", + Value: iatBh.ODFIIdentificationField(), Msg: msgFieldInclusion} + } + return nil +} + +// IATIndicatorField gets the IATIndicator left padded +func (iatBh *IATBatchHeader) IATIndicatorField() string { + // should this be left padded + return iatBh.alphaField(iatBh.IATIndicator, 16) +} + +// ForeignExchangeIndicatorField gets the ForeignExchangeIndicator +func (iatBh *IATBatchHeader) ForeignExchangeIndicatorField() string { + return iatBh.alphaField(iatBh.ForeignExchangeIndicator, 2) +} + +// ForeignExchangeReferenceIndicatorField gets the ForeignExchangeReferenceIndicator +func (iatBh *IATBatchHeader) ForeignExchangeReferenceIndicatorField() string { + return iatBh.numericField(iatBh.ForeignExchangeReferenceIndicator, 1) +} + +// ForeignExchangeReferenceField gets the ForeignExchangeReference left padded +func (iatBh *IATBatchHeader) ForeignExchangeReferenceField() string { + if iatBh.ForeignExchangeReferenceIndicator == 3 { + //blank space + return " " + } + return iatBh.alphaField(iatBh.ForeignExchangeReference, 15) +} + +// ISODestinationCountryCodeField gets the ISODestinationCountryCode +func (iatBh *IATBatchHeader) ISODestinationCountryCodeField() string { + return iatBh.alphaField(iatBh.ISODestinationCountryCode, 2) +} + +// OriginatorIdentificationField gets the OriginatorIdentification left padded +func (iatBh *IATBatchHeader) OriginatorIdentificationField() string { + return iatBh.alphaField(iatBh.OriginatorIdentification, 10) +} + +// CompanyEntryDescriptionField gets the CompanyEntryDescription left padded +func (iatBh *IATBatchHeader) CompanyEntryDescriptionField() string { + return iatBh.alphaField(iatBh.CompanyEntryDescription, 10) +} + +// ISOOriginatingCurrencyCodeField gets the ISOOriginatingCurrencyCode +func (iatBh *IATBatchHeader) ISOOriginatingCurrencyCodeField() string { + return iatBh.alphaField(iatBh.ISOOriginatingCurrencyCode, 3) +} + +// ISODestinationCurrencyCodeField gets the ISODestinationCurrencyCode +func (iatBh *IATBatchHeader) ISODestinationCurrencyCodeField() string { + return iatBh.alphaField(iatBh.ISODestinationCurrencyCode, 3) +} + +// EffectiveEntryDateField get the EffectiveEntryDate in YYMMDD format +func (iatBh *IATBatchHeader) EffectiveEntryDateField() string { + return iatBh.formatSimpleDate(iatBh.EffectiveEntryDate) +} + +// ODFIIdentificationField get the odfi number zero padded +func (iatBh *IATBatchHeader) ODFIIdentificationField() string { + return iatBh.stringField(iatBh.ODFIIdentification, 8) +} + +// BatchNumberField get the batch number zero padded +func (iatBh *IATBatchHeader) BatchNumberField() string { + return iatBh.numericField(iatBh.BatchNumber, 7) +} + +// settlementDateField gets the settlementDate +func (iatBh *IATBatchHeader) settlementDateField() string { + return iatBh.alphaField(iatBh.settlementDate, 3) +} diff --git a/iatBatchHeader_test.go b/iatBatchHeader_test.go new file mode 100644 index 000000000..db223ec95 --- /dev/null +++ b/iatBatchHeader_test.go @@ -0,0 +1,811 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "strings" + "testing" +) + +// mockIATBatchHeaderFF creates a IAT BatchHeader that is Fixed-Fixed +func mockIATBatchHeaderFF() *IATBatchHeader { + bh := NewIATBatchHeader() + bh.ServiceClassCode = 220 + bh.ForeignExchangeIndicator = "FF" + bh.ForeignExchangeReferenceIndicator = 3 + bh.ISODestinationCountryCode = "US" + bh.OriginatorIdentification = "123456789" + bh.StandardEntryClassCode = "IAT" + bh.CompanyEntryDescription = "TRADEPAYMT" + bh.ISOOriginatingCurrencyCode = "CAD" + bh.ISODestinationCurrencyCode = "USD" + bh.ODFIIdentification = "23138010" + return bh +} + +// testMockIATBatchHeaderFF creates a IAT BatchHeader Fixed-Fixed +func testMockIATBatchHeaderFF(t testing.TB) { + bh := mockIATBatchHeaderFF() + if err := bh.Validate(); err != nil { + t.Error("mockIATBatchHeaderFF does not validate and will break other tests: ", err) + } + if bh.ServiceClassCode != 220 { + t.Error("ServiceClassCode dependent default value has changed") + } + if bh.ForeignExchangeIndicator != "FF" { + t.Error("ForeignExchangeIndicator does not validate and will break other tests") + } + if bh.ForeignExchangeReferenceIndicator != 3 { + t.Error("ForeignExchangeReferenceIndicator does not validate and will break other tests") + } + if bh.ISODestinationCountryCode != "US" { + t.Error("DestinationCountryCode dependent default value has changed") + } + if bh.OriginatorIdentification != "123456789" { + t.Error("OriginatorIdentification dependent default value has changed") + } + if bh.StandardEntryClassCode != "IAT" { + t.Error("StandardEntryClassCode dependent default value has changed") + } + if bh.CompanyEntryDescription != "TRADEPAYMT" { + t.Error("CompanyEntryDescription dependent default value has changed") + } + if bh.ISOOriginatingCurrencyCode != "CAD" { + t.Error("ISOOriginatingCurrencyCode dependent default value has changed") + } + if bh.ISODestinationCurrencyCode != "USD" { + t.Error("ISODestinationCurrencyCode dependent default value has changed") + } + if bh.ODFIIdentification != "23138010" { + t.Error("ODFIIdentification dependent default value has changed") + } +} + +// TestMockIATBatchHeaderFF tests creating a IAT BatchHeader Fixed-Fixed +func TestMockIATBatchHeaderFF(t *testing.T) { + testMockIATBatchHeaderFF(t) +} + +// BenchmarkMockIATBatchHeaderFF benchmarks creating a IAT BatchHeader Fixed-Fixed +func BenchmarkMockIATBatchHeaderFF(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testMockIATBatchHeaderFF(b) + } +} + +// testParseIATBatchHeader parses a known IAT BatchHeader record string +func testParseIATBatchHeader(t testing.TB) { + var line = "5220 FF3 US123456789 IATTRADEPAYMTCADUSD180621 1231380100000001" + r := NewReader(strings.NewReader(line)) + r.line = line + if err := r.parseIATBatchHeader(); err != nil { + t.Errorf("%T: %s", err, err) + } + record := r.IATCurrentBatch.GetHeader() + + if record.recordType != "5" { + t.Errorf("RecordType Expected '5' got: %v", record.recordType) + } + 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.ForeignExchangeIndicator != "FF" { + t.Errorf("ForeignExchangeIndicator Expected ' ' got: %v", + record.ForeignExchangeIndicator) + } + if record.ForeignExchangeReferenceIndicator != 3 { + t.Errorf("ForeignExchangeReferenceIndicator Expected ' ' got: %v", + record.ForeignExchangeReferenceIndicator) + } + if record.ForeignExchangeReferenceField() != " " { + t.Errorf("ForeignExchangeReference Expected ' ' got: %v", + record.ForeignExchangeReference) + } + if record.StandardEntryClassCode != "IAT" { + t.Errorf("StandardEntryClassCode Expected 'PPD' got: %v", record.StandardEntryClassCode) + } + if record.CompanyEntryDescription != "TRADEPAYMT" { + t.Errorf("CompanyEntryDescription Expected 'TRADEPAYMT' got: %v", record.CompanyEntryDescriptionField()) + } + + if record.EffectiveEntryDateField() != "180621" { + t.Errorf("EffectiveEntryDate Expected '080730' got: %v", record.EffectiveEntryDateField()) + } + if record.settlementDate != " " { + t.Errorf("SettlementDate Expected ' ' got: %v", record.settlementDate) + } + if record.OriginatorStatusCode != 1 { + t.Errorf("OriginatorStatusCode Expected 1 got: %v", record.OriginatorStatusCode) + } + if record.ODFIIdentification != "23138010" { + t.Errorf("OdfiIdentification Expected '07640125' got: %v", record.ODFIIdentificationField()) + } + if record.BatchNumberField() != "0000001" { + t.Errorf("BatchNumber Expected '0000001' got: %v", record.BatchNumberField()) + } +} + +// TestParseIATBatchHeader tests parsing a known IAT BatchHeader record string +func TestParseIATBatchHeader(t *testing.T) { + testParseIATBatchHeader(t) +} + +// BenchmarkParseBatchHeader benchmarks parsing a known IAT BatchHeader record string +func BenchmarkParseIATBatchHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseIATBatchHeader(b) + } +} + +// testIATBHString validates that a known parsed IAT Batch Header +// can be return to a string of the same value +func testIATBHString(t testing.TB) { + var line = "5220 FF3 US123456789 IATTRADEPAYMTCADUSD180621 1231380100000001" + r := NewReader(strings.NewReader(line)) + r.line = line + if err := r.parseIATBatchHeader(); err != nil { + t.Errorf("%T: %s", err, err) + } + record := r.IATCurrentBatch.GetHeader() + + if record.String() != line { + t.Errorf("Strings do not match") + } +} + +// TestIATBHString tests validating that a known parsed IAT BatchHeader +// can be return to a string of the same value +func TestIATBHString(t *testing.T) { + testIATBHString(t) +} + +// BenchmarkIATBHString benchmarks validating that a known parsed IAT BatchHeader +// can be return to a string of the same value +func BenchmarkIATBHString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHString(b) + } +} + +// testIATBHFVString validates that a known parsed IAT Batch Header +// can be return to a string of the same value +func testIATBHFVString(t testing.TB) { + var line = "5220 FV2123456789012345US123456789 IATTRADEPAYMTCADUSD180621 1231380100000001" + r := NewReader(strings.NewReader(line)) + r.line = line + if err := r.parseIATBatchHeader(); err != nil { + t.Errorf("%T: %s", err, err) + } + record := r.IATCurrentBatch.GetHeader() + + if record.String() != line { + t.Errorf("Strings do not match") + } +} + +// TestIATBHFVString tests validating that a known parsed IAT BatchHeader +// can be return to a string of the same value +func TestIATBHFVString(t *testing.T) { + testIATBHFVString(t) +} + +// BenchmarkIATBHFVString benchmarks validating that a known parsed IAT BatchHeader +// can be return to a string of the same value +func BenchmarkIATBHFVString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHFVString(b) + } +} + +// testValidateIATBHRecordType validates error if IATBatchHeader recordType is invalid +func testValidateIATBHRecordType(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.recordType = "2" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHRecordType tests validating error if IATBatchHeader recordType is invalid +func TestValidateIATBHRecordType(t *testing.T) { + testValidateIATBHRecordType(t) +} + +// BenchmarkValidateIATBHRecordType benchmarks validating error if IATBatchHeader recordType is invalid +func BenchmarkValidateIATBHRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHRecordType(b) + } +} + +// testValidateIATBHServiceClassCode validates error if IATBatchHeader +// ServiceClassCode is invalid +func testValidateIATBHServiceClassCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ServiceClassCode = 999 + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHServiceClassCode tests validating error if IATBatchHeader +// ServiceClassCode is invalid +func TestValidateIATBHServiceClassCode(t *testing.T) { + testValidateIATBHServiceClassCode(t) +} + +// BenchmarkValidateIATBHServiceClassCode benchmarks validating error if IATBatchHeader +// ServiceClassCode is invalid +func BenchmarkValidateIATBHServiceClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHServiceClassCode(b) + } +} + +// testValidateIATBHForeignExchangeIndicator validates error if IATBatchHeader +// ForeignExchangeIndicator is invalid +func testValidateIATBHForeignExchangeIndicator(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ForeignExchangeIndicator = "XY" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignExchangeIndicator" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHForeignExchangeIndicator tests validating error if IATBatchHeader +// ForeignExchangeIndicator is invalid +func TestValidateIATBHForeignExchangeIndicator(t *testing.T) { + testValidateIATBHForeignExchangeIndicator(t) +} + +// BenchmarkValidateIATBHForeignExchangeIndicator benchmarks validating error if IATBatchHeader +// ForeignExchangeIndicator is invalid +func BenchmarkValidateIATBHForeignExchangeIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHForeignExchangeIndicator(b) + } +} + +// testValidateIATBHForeignExchangeReferenceIndicator validates error if IATBatchHeader +// ForeignExchangeReferenceIndicator is invalid +func testValidateIATBHForeignExchangeReferenceIndicator(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ForeignExchangeReferenceIndicator = 5 + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignExchangeReferenceIndicator" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHForeignExchangeReferenceIndicator tests validating error if IATBatchHeader +// ForeignExchangeReferenceIndicator is invalid +func TestValidateIATBHForeignExchangeReferenceIndicator(t *testing.T) { + testValidateIATBHForeignExchangeReferenceIndicator(t) +} + +// BenchmarkValidateIATBHForeignExchangeReferenceIndicator benchmarks validating error if IATBatchHeader +// ForeignExchangeReferenceIndicator is invalid +func BenchmarkValidateIATBHForeignExchangeReferenceIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHForeignExchangeReferenceIndicator(b) + } +} + +// testValidateIATBHISODestinationCountryCode validates error if IATBatchHeader +// ISODestinationCountryCode is invalid +func testValidateIATBHISODestinationCountryCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ISODestinationCountryCode = "®" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ISODestinationCountryCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHISODestinationCountryCode tests validating error if IATBatchHeader +// ISODestinationCountryCode is invalid +func TestValidateIATBHISODestinationCountryCode(t *testing.T) { + testValidateIATBHISODestinationCountryCode(t) +} + +// BenchmarkValidateIATBHISODestinationCountryCode benchmarks validating error if IATBatchHeader +// ISODestinationCountryCode is invalid +func BenchmarkValidateIATBHISODestinationCountryCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHISODestinationCountryCode(b) + } +} + +// testValidateIATBHOriginatorIdentification validates error if IATBatchHeader +// OriginatorIdentification is invalid +func testValidateIATBHOriginatorIdentification(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.OriginatorIdentification = "®" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "OriginatorIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHOriginatorIdentification tests validating error if IATBatchHeader +// OriginatorIdentification is invalid +func TestValidateIATBHOriginatorIdentification(t *testing.T) { + testValidateIATBHOriginatorIdentification(t) +} + +// BenchmarkValidateIATBHOriginatorIdentification benchmarks validating error if IATBatchHeader +// OriginatorIdentification is invalid +func BenchmarkValidateIATBHOriginatorIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHOriginatorIdentification(b) + } +} + +// testValidateIATBHStandardEntryClassCode validates error if IATBatchHeader +// StandardEntryClassCode is invalid +func testValidateIATBHStandardEntryClassCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.StandardEntryClassCode = "ABC" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHStandardEntryClassCode tests validating error if IATBatchHeader +// StandardEntryClassCode is invalid +func TestValidateIATBHStandardEntryClassCode(t *testing.T) { + testValidateIATBHStandardEntryClassCode(t) +} + +// BenchmarkValidateIATBHStandardEntryClassCode benchmarks validating error if IATBatchHeader +// StandardEntryClassCode is invalid +func BenchmarkValidateIATBHStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHStandardEntryClassCode(b) + } +} + +// testValidateIATBHCompanyEntryDescription validates error if IATBatchHeader +// CompanyEntryDescription is invalid +func testValidateIATBHCompanyEntryDescription(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.CompanyEntryDescription = "®" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CompanyEntryDescription" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHCompanyEntryDescription tests validating error if IATBatchHeader +// CompanyEntryDescription is invalid +func TestValidateIATBHCompanyEntryDescription(t *testing.T) { + testValidateIATBHCompanyEntryDescription(t) +} + +// BenchmarkValidateIATBHCompanyEntryDescription benchmarks validating error if IATBatchHeader +// CompanyEntryDescription is invalid +func BenchmarkValidateIATBHCompanyEntryDescription(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHCompanyEntryDescription(b) + } +} + +// testValidateIATBHISOOriginatingCurrencyCode validates error if IATBatchHeader +// ISOOriginatingCurrencyCode is invalid +func testValidateIATBHISOOriginatingCurrencyCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ISOOriginatingCurrencyCode = "®" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ISOOriginatingCurrencyCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHISOOriginatingCurrencyCode tests validating error if IATBatchHeader +// ISOOriginatingCurrencyCode is invalid +func TestValidateIATBHISOOriginatingCurrencyCode(t *testing.T) { + testValidateIATBHISOOriginatingCurrencyCode(t) +} + +// BenchmarkValidateIATBHISOOriginatingCurrencyCode benchmarks validating error if IATBatchHeader +// ISOOriginatingCurrencyCode is invalid +func BenchmarkValidateIATBHISOOriginatingCurrencyCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHISOOriginatingCurrencyCode(b) + } +} + +// testValidateIATBHISODestinationCurrencyCode validates error if IATBatchHeader +// ISODestinationCurrencyCode is invalid +func testValidateIATBHISODestinationCurrencyCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ISODestinationCurrencyCode = "®" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ISODestinationCurrencyCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHISODestinationCurrencyCode tests validating error if IATBatchHeader +// ISODestinationCurrencyCode is invalid +func TestValidateIATBHISODestinationCurrencyCode(t *testing.T) { + testValidateIATBHISODestinationCurrencyCode(t) +} + +// BenchmarkValidateIATBHISODestinationCurrencyCode benchmarks validating error if IATBatchHeader +// ISODestinationCurrencyCode is invalid +func BenchmarkValidateIATBHISODestinationCurrencyCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHISODestinationCurrencyCode(b) + } +} + +// testValidateIATBHOriginatorStatusCode validates error if IATBatchHeader +// OriginatorStatusCode is invalid +func testValidateIATBHOriginatorStatusCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.OriginatorStatusCode = 7 + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "OriginatorStatusCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateIATBHOriginatorStatusCode tests validating error if IATBatchHeader +// OriginatorStatusCode is invalid +func TestValidateIATBHOriginatorStatusCode(t *testing.T) { + testValidateIATBHOriginatorStatusCode(t) +} + +// BenchmarkValidateIATBHOriginatorStatusCode benchmarks validating error if IATBatchHeader +// OriginatorStatusCode is invalid +func BenchmarkValidateIATBHOriginatorStatusCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateIATBHOriginatorStatusCode(b) + } +} + +//FieldInclusion + +// testIATBHRecordType validates IATBatchHeader recordType fieldInclusion +func testIATBHRecordType(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.recordType = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHRecordType tests validating IATBatchHeader recordType fieldInclusion +func TestIATBHRecordType(t *testing.T) { + testIATBHRecordType(t) +} + +// BenchmarkIATBHRecordType benchmarks validating IATBatchHeader recordType fieldInclusion +func BenchmarkIATBHRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHRecordType(b) + } +} + +// testIATBHServiceClassCode validates IATBatchHeader ServiceClassCode fieldInclusion +func testIATBHServiceClassCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ServiceClassCode = 0 + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHServiceClassCode tests validating IATBatchHeader ServiceClassCode fieldInclusion +func TestIATBHServiceClassCode(t *testing.T) { + testIATBHServiceClassCode(t) +} + +// BenchmarkIATBHServiceClassCode benchmarks validating IATBatchHeader ServiceClassCode fieldInclusion +func BenchmarkIATBHServiceClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHServiceClassCode(b) + } +} + +// testIATBHForeignExchangeIndicator validates IATBatchHeader ForeignExchangeIndicator fieldInclusion +func testIATBHForeignExchangeIndicator(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ForeignExchangeIndicator = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignExchangeIndicator" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHForeignExchangeIndicator tests validating IATBatchHeader ForeignExchangeIndicator fieldInclusion +func TestIATBHForeignExchangeIndicator(t *testing.T) { + testIATBHForeignExchangeIndicator(t) +} + +// BenchmarkIATBHForeignExchangeIndicator benchmarks validating IATBatchHeader ForeignExchangeIndicator fieldInclusion +func BenchmarkIATBHForeignExchangeIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHForeignExchangeIndicator(b) + } +} + +// testIATBHForeignExchangeReferenceIndicator validates IATBatchHeader ForeignExchangeReferenceIndicator fieldInclusion +func testIATBHForeignExchangeReferenceIndicator(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ForeignExchangeReferenceIndicator = 0 + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignExchangeReferenceIndicator" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHForeignExchangeReferenceIndicator tests validating IATBatchHeader ForeignExchangeReferenceIndicator fieldInclusion +func TestIATBHForeignExchangeReferenceIndicator(t *testing.T) { + testIATBHForeignExchangeReferenceIndicator(t) +} + +// BenchmarkIATBHForeignExchangeReferenceIndicator benchmarks validating IATBatchHeader ForeignExchangeReferenceIndicator fieldInclusion +func BenchmarkIATBHForeignExchangeReferenceIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHForeignExchangeReferenceIndicator(b) + } +} + +// testIATBHISODestinationCountryCode validates IATBatchHeader ISODestinationCountryCode fieldInclusion +func testIATBHISODestinationCountryCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ISODestinationCountryCode = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ISODestinationCountryCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHISODestinationCountryCode tests validating IATBatchHeader ISODestinationCountryCode fieldInclusion +func TestIATBHISODestinationCountryCode(t *testing.T) { + testIATBHISODestinationCountryCode(t) +} + +// BenchmarkIATBHISODestinationCountryCode benchmarks validating IATBatchHeader ISODestinationCountryCode fieldInclusion +func BenchmarkIATBHISODestinationCountryCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHISODestinationCountryCode(b) + } +} + +// testIATBHOriginatorIdentification validates IATBatchHeader OriginatorIdentification fieldInclusion +func testIATBHOriginatorIdentification(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.OriginatorIdentification = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "OriginatorIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHOriginatorIdentification tests validating IATBatchHeader OriginatorIdentification fieldInclusion +func TestIATBHOriginatorIdentification(t *testing.T) { + testIATBHOriginatorIdentification(t) +} + +// BenchmarkIATBHOriginatorIdentification benchmarks validating IATBatchHeader OriginatorIdentification fieldInclusion +func BenchmarkIATBHOriginatorIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHOriginatorIdentification(b) + } +} + +// testIATBHStandardEntryClassCode validates IATBatchHeader StandardEntryClassCode fieldInclusion +func testIATBHStandardEntryClassCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.StandardEntryClassCode = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHStandardEntryClassCode tests validating IATBatchHeader StandardEntryClassCode fieldInclusion +func TestIATBHStandardEntryClassCode(t *testing.T) { + testIATBHStandardEntryClassCode(t) +} + +// BenchmarkIATBHStandardEntryClassCode benchmarks validating IATBatchHeader StandardEntryClassCode fieldInclusion +func BenchmarkIATBHStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHStandardEntryClassCode(b) + } +} + +// testIATBHCompanyEntryDescription validates IATBatchHeader CompanyEntryDescription fieldInclusion +func testIATBHCompanyEntryDescription(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.CompanyEntryDescription = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CompanyEntryDescription" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHCompanyEntryDescription tests validating IATBatchHeader CompanyEntryDescription fieldInclusion +func TestIATBHCompanyEntryDescription(t *testing.T) { + testIATBHCompanyEntryDescription(t) +} + +// BenchmarkIATBHCompanyEntryDescription benchmarks validating IATBatchHeader CompanyEntryDescription fieldInclusion +func BenchmarkIATBHCompanyEntryDescription(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHCompanyEntryDescription(b) + } +} + +// testIATBHISOOriginatingCurrencyCode validates IATBatchHeader ISOOriginatingCurrencyCode fieldInclusion +func testIATBHISOOriginatingCurrencyCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ISOOriginatingCurrencyCode = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ISOOriginatingCurrencyCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHISOOriginatingCurrencyCode tests validating IATBatchHeader ISOOriginatingCurrencyCode fieldInclusion +func TestIATBHISOOriginatingCurrencyCode(t *testing.T) { + testIATBHISOOriginatingCurrencyCode(t) +} + +// BenchmarkIATBHISOOriginatingCurrencyCode benchmarks validating IATBatchHeader ISOOriginatingCurrencyCode fieldInclusion +func BenchmarkIATBHISOOriginatingCurrencyCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHISOOriginatingCurrencyCode(b) + } +} + +// testIATBHISODestinationCurrencyCode validates IATBatchHeader ISODestinationCurrencyCode fieldInclusion +func testIATBHISODestinationCurrencyCode(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ISODestinationCurrencyCode = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ISODestinationCurrencyCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHISODestinationCurrencyCode tests validating IATBatchHeader ISODestinationCurrencyCode fieldInclusion +func TestIATBHISODestinationCurrencyCode(t *testing.T) { + testIATBHISODestinationCurrencyCode(t) +} + +// BenchmarkIATBHISODestinationCurrencyCode benchmarks validating IATBatchHeader ISODestinationCurrencyCode fieldInclusion +func BenchmarkIATBHISODestinationCurrencyCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHISODestinationCurrencyCode(b) + } +} + +// testIATBHODFIIdentification validates IATBatchHeader ODFIIdentification fieldInclusion +func testIATBHODFIIdentification(t testing.TB) { + bh := mockIATBatchHeaderFF() + bh.ODFIIdentification = "" + if err := bh.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATBHODFIIdentification tests validating IATBatchHeader ODFIIdentification fieldInclusion +func TestIATBHODFIIdentification(t *testing.T) { + testIATBHODFIIdentification(t) +} + +// BenchmarkIATBHODFIIdentification benchmarks validating IATBatchHeader ODFIIdentification fieldInclusion +func BenchmarkIATBHODFIIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBHODFIIdentification(b) + } +} diff --git a/iatBatcher.go b/iatBatcher.go new file mode 100644 index 000000000..9a60bc43e --- /dev/null +++ b/iatBatcher.go @@ -0,0 +1,55 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +// IATBatcher abstract an IAT ACH batch type +type IATBatcher interface { + GetHeader() *IATBatchHeader + SetHeader(*IATBatchHeader) + GetControl() *BatchControl + SetControl(*BatchControl) + GetEntries() []*IATEntryDetail + AddEntry(*IATEntryDetail) + Create() error + Validate() error + // Category defines if a Forward or Return + Category() string +} + +/*// BatchError is an Error that describes batch validation issues +type IATBatchError struct { + BatchNumber int + FieldName string + Msg string +} + +func (e *BatchError) IATError() string { + return fmt.Sprintf("BatchNumber %d %s %s", e.BatchNumber, e.FieldName, e.Msg) +}*/ + +// Errors specific to parsing a Batch container +var ( +// generic messages +/* msgBatchHeaderControlEquality = "header %v is not equal to control %v" + msgBatchCalculatedControlEquality = "calculated %v is out-of-balance with control %v" + msgBatchAscending = "%v is less than last %v. Must be in ascending order" + // specific messages for error + msgBatchCompanyEntryDescription = "Company entry description %v is not valid for batch type %v" + msgBatchOriginatorDNE = "%v is not “2” for DNE with entry transaction code of 23 or 33" + msgBatchTraceNumberNotODFI = "%v in header does not match entry trace number %v" + msgBatchAddendaIndicator = "is 0 but found addenda record(s)" + msgBatchAddendaTraceNumber = "%v does not match proceeding entry detail trace number %v" + msgBatchEntries = "must have Entry Record(s) to be built" + msgBatchAddendaCount = "%v addendum found where %v is allowed for batch type %v" + msgBatchTransactionCodeCredit = "%v a credit is not allowed" + msgBatchSECType = "header SEC type code %v for batch type %v" + msgBatchTypeCode = "%v found in addenda and expecting %v for batch type %v" + msgBatchServiceClassCode = "Service Class Code %v is not valid for batch type %v" + msgBatchForwardReturn = "Forward and Return entries found in the same batch" + msgBatchAmount = "Amount must be less than %v for SEC code %v" + msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" + msgBatchTransactionCode = "Transaction code %v is not allowed for batch type %v" + msgBatchCardTransactionType = "Card Transaction Type %v is invalid"*/ +) diff --git a/iatEntryDetail.go b/iatEntryDetail.go new file mode 100644 index 000000000..83eabe2d8 --- /dev/null +++ b/iatEntryDetail.go @@ -0,0 +1,269 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" + "strconv" +) + +// IATEntryDetail contains the actual transaction data for an individual entry. +// Fields include those designating the entry as a deposit (credit) or +// withdrawal (debit), the transit routing number for the entry recipient’s financial +// institution, the account number (left justify,no zero fill), name, and dollar amount. +type IATEntryDetail struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + + // RecordType defines the type of record in the block. 6 + recordType string + + // TransactionCode if the receivers account is: + // Credit (deposit) to checking account ‘22’ + // Prenote for credit to checking account ‘23’ + // Debit (withdrawal) to checking account ‘27’ + // Prenote for debit to checking account ‘28’ + // Credit to savings account ‘32’ + // Prenote for credit to savings account ‘33’ + // Debit to savings account ‘37’ + // Prenote for debit to savings account ‘38’ + TransactionCode int `json:"transactionCode"` + + // RDFIIdentification is the RDFI's routing number without the last digit. + // Receiving Depository Financial Institution + RDFIIdentification string `json:"RDFIIdentification"` + + // CheckDigit the last digit of the RDFI's routing number + CheckDigit string `json:"checkDigit"` + + // AddendaRecords is the number of Addenda Records + AddendaRecords int `json:"AddendaRecords"` + + // reserved - Leave blank + reserved string + + // Amount Number of cents you are debiting/crediting this account + Amount int `json:"amount"` + + // DFIAccountNumber is the receiver's bank account number you are crediting/debiting. + // It important to note that this is an alphanumeric field, so its space padded, no zero padded + DFIAccountNumber string `json:"DFIAccountNumber"` + + // reserved2 - Leave blank + reservedTwo string + + // OFACSreeningIndicator - Leave blank + OFACSreeningIndicator string `json:"OFACSreeningIndicator"` + + // SecondaryOFACSreeningIndicator - Leave blank + SecondaryOFACSreeningIndicator string `json:"SecondaryOFACSreeningIndicator"` + + // AddendaRecordIndicator indicates the existence of an Addenda Record. + // A value of "1" indicates that one ore more addenda records follow, + // and "0" means no such record is present. + AddendaRecordIndicator int `json:"addendaRecordIndicator,omitempty"` + + // TraceNumber assigned by the ODFI in ascending sequence, is included in each + // Entry Detail Record, Corporate Entry Detail Record, and addenda Record. + // Trace Numbers uniquely identify each entry within a batch in an ACH input file. + // In association with the Batch Number, transmission (File Creation) Date, + // and File ID Modifier, the Trace Number uniquely identifies an entry within a given file. + // For addenda Records, the Trace Number will be identical to the Trace Number + // in the associated Entry Detail Record, since the Trace Number is associated + // with an entry or item rather than a physical record. + TraceNumber int `json:"traceNumber,omitempty"` + + // Addendum a list of Addenda for the Entry Detail + Addendum []Addendumer `json:"addendum,omitempty"` + // Category defines if the entry is a Forward, Return, or NOC + Category string `json:"category,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to golang Converters + converters +} + +// NewIATEntryDetail returns a new IATEntryDetail with default values for non exported fields +func NewIATEntryDetail() *IATEntryDetail { + iatEd := &IATEntryDetail{ + recordType: "6", + Category: CategoryForward, + AddendaRecordIndicator: 1, + } + return iatEd +} + +// Parse takes the input record string and parses the EntryDetail values +func (ed *IATEntryDetail) Parse(record string) { + // 1-1 Always "6" + ed.recordType = "6" + // 2-3 is checking credit 22 debit 27 savings credit 32 debit 37 + ed.TransactionCode = ed.parseNumField(record[1:3]) + // 4-11 the RDFI's routing number without the last digit. + ed.RDFIIdentification = ed.parseStringField(record[3:11]) + // 12-12 The last digit of the RDFI's routing number + ed.CheckDigit = ed.parseStringField(record[11:12]) + // 13-16 Number of addenda records + ed.AddendaRecords = ed.parseNumField(record[12:16]) + // 17-29 reserved - Leave blank + ed.reserved = " " + // 30-39 Number of cents you are debiting/crediting this account + ed.Amount = ed.parseNumField(record[29:39]) + // 40-74 The foreign receiver's account number you are crediting/debiting + ed.DFIAccountNumber = record[39:74] + // 75-76 reserved2 Leave blank + ed.reservedTwo = " " + // 77 OFACScreeningIndicator + ed.OFACSreeningIndicator = " " + // 78-78 Secondary SecondaryOFACSreeningIndicator + ed.SecondaryOFACSreeningIndicator = " " + // 79-79 1 if addenda exists 0 if it does not + //ed.AddendaRecordIndicator = 1 + ed.AddendaRecordIndicator = ed.parseNumField(record[78:79]) + // 80-94 An internal identification (alphanumeric) that you use to uniquely identify + // this Entry Detail Record This number should be unique to the transaction and will help identify the transaction in case of an inquiry + ed.TraceNumber = ed.parseNumField(record[79:94]) +} + +// String writes the EntryDetail struct to a 94 character string. +func (ed *IATEntryDetail) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v%v", + ed.recordType, + ed.TransactionCode, + ed.RDFIIdentificationField(), + ed.CheckDigit, + ed.AddendaRecordsField(), + ed.reservedField(), + ed.AmountField(), + ed.DFIAccountNumberField(), + ed.reservedTwoField(), + ed.OFACSreeningIndicatorField(), + ed.SecondaryOFACSreeningIndicatorField(), + ed.AddendaRecordIndicator, + ed.TraceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (ed *IATEntryDetail) Validate() error { + if err := ed.fieldInclusion(); err != nil { + return err + } + if ed.recordType != "6" { + msg := fmt.Sprintf(msgRecordType, 6) + return &FieldError{FieldName: "recordType", Value: ed.recordType, Msg: msg} + } + if err := ed.isTransactionCode(ed.TransactionCode); err != nil { + return &FieldError{FieldName: "TransactionCode", Value: strconv.Itoa(ed.TransactionCode), Msg: err.Error()} + } + if err := ed.isAlphanumeric(ed.DFIAccountNumber); err != nil { + return &FieldError{FieldName: "DFIAccountNumber", Value: ed.DFIAccountNumber, Msg: err.Error()} + } + // CheckDigit calculations + calculated := ed.CalculateCheckDigit(ed.RDFIIdentificationField()) + + edCheckDigit, err := strconv.Atoi(ed.CheckDigit) + if err != nil { + return &FieldError{FieldName: "CheckDigit", Value: ed.CheckDigit, Msg: err.Error()} + } + + if calculated != edCheckDigit { + msg := fmt.Sprintf(msgValidCheckDigit, calculated) + return &FieldError{FieldName: "RDFIIdentification", Value: ed.CheckDigit, Msg: msg} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (ed *IATEntryDetail) fieldInclusion() error { + if ed.recordType == "" { + return &FieldError{FieldName: "recordType", + Value: ed.recordType, Msg: msgFieldInclusion} + } + if ed.TransactionCode == 0 { + return &FieldError{FieldName: "TransactionCode", + Value: strconv.Itoa(ed.TransactionCode), Msg: msgFieldInclusion} + } + if ed.RDFIIdentification == "" { + return &FieldError{FieldName: "RDFIIdentification", + Value: ed.RDFIIdentificationField(), Msg: msgFieldInclusion} + } + if ed.AddendaRecords == 0 { + return &FieldError{FieldName: "AddendaRecords", + Value: strconv.Itoa(ed.AddendaRecords), Msg: msgFieldInclusion} + } + if ed.DFIAccountNumber == "" { + return &FieldError{FieldName: "DFIAccountNumber", + Value: ed.DFIAccountNumber, Msg: msgFieldInclusion} + } + if ed.AddendaRecordIndicator == 0 { + return &FieldError{FieldName: "AddendaRecordIndicator", + Value: strconv.Itoa(ed.AddendaRecordIndicator), Msg: msgFieldInclusion} + } + if ed.TraceNumber == 0 { + return &FieldError{FieldName: "TraceNumber", + Value: ed.TraceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// SetRDFI takes the 9 digit RDFI account number and separates it for RDFIIdentification and CheckDigit +func (ed *IATEntryDetail) SetRDFI(rdfi string) *IATEntryDetail { + s := ed.stringField(rdfi, 9) + ed.RDFIIdentification = ed.parseStringField(s[:8]) + ed.CheckDigit = ed.parseStringField(s[8:9]) + return ed +} + +// SetTraceNumber takes first 8 digits of ODFI and concatenates a sequence number onto the TraceNumber +func (ed *IATEntryDetail) SetTraceNumber(ODFIIdentification string, seq int) { + trace := ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7) + ed.TraceNumber = ed.parseNumField(trace) +} + +// RDFIIdentificationField get the rdfiIdentification with zero padding +func (ed *IATEntryDetail) RDFIIdentificationField() string { + return ed.stringField(ed.RDFIIdentification, 8) +} + +// AddendaRecordsField returns a zero padded TraceNumber string +func (ed *IATEntryDetail) AddendaRecordsField() string { + return ed.numericField(ed.AddendaRecords, 4) +} + +func (ed *IATEntryDetail) reservedField() string { + return ed.alphaField(ed.reserved, 13) +} + +// AmountField returns a zero padded string of amount +func (ed *IATEntryDetail) AmountField() string { + return ed.numericField(ed.Amount, 10) +} + +// DFIAccountNumberField gets the DFIAccountNumber with space padding +func (ed *IATEntryDetail) DFIAccountNumberField() string { + return ed.alphaField(ed.DFIAccountNumber, 35) +} + +// reservedTwoField gets the reservedTwo +func (ed *IATEntryDetail) reservedTwoField() string { + return ed.alphaField(ed.reservedTwo, 2) +} + +// OFACSreeningIndicatorField gets the OFACSreeningIndicator +func (ed *IATEntryDetail) OFACSreeningIndicatorField() string { + return ed.alphaField(ed.OFACSreeningIndicator, 1) +} + +// SecondaryOFACSreeningIndicatorField gets the SecondaryOFACSreeningIndicator +func (ed *IATEntryDetail) SecondaryOFACSreeningIndicatorField() string { + return ed.alphaField(ed.SecondaryOFACSreeningIndicator, 1) +} + +// TraceNumberField returns a zero padded TraceNumber string +func (ed *IATEntryDetail) TraceNumberField() string { + return ed.numericField(ed.TraceNumber, 15) +} diff --git a/iatEntryDetail_test.go b/iatEntryDetail_test.go new file mode 100644 index 000000000..ba1f293e0 --- /dev/null +++ b/iatEntryDetail_test.go @@ -0,0 +1,490 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "strings" + "testing" +) + +// mockIATEntryDetail creates an IAT EntryDetail +func mockIATEntryDetail() *IATEntryDetail { + entry := NewIATEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("121042882") + entry.AddendaRecords = 7 + entry.DFIAccountNumber = "123456789" + entry.Amount = 100000 + entry.SetTraceNumber(mockIATBatchHeaderFF().ODFIIdentification, 1) + entry.Category = CategoryForward + return entry +} + +// testMockIATEntryDetail validates an IATEntryDetail record +func testMockIATEntryDetail(t testing.TB) { + entry := mockIATEntryDetail() + if err := entry.Validate(); err != nil { + t.Error("mockEntryDetail does not validate and will break other tests") + } + if entry.TransactionCode != 22 { + t.Error("TransactionCode dependent default value has changed") + } + if entry.RDFIIdentification != "12104288" { + t.Error("RDFIIdentification dependent default value has changed") + } + // ToDo: Add checkDigit test + if entry.AddendaRecords != 7 { + t.Error("AddendaRecords default dependent value has changed") + } + if entry.DFIAccountNumber != "123456789" { + t.Error("DFIAccountNumber dependent default value has changed") + } + if entry.Amount != 100000 { + t.Error("Amount dependent default value has changed") + } + if entry.TraceNumber != 231380100000001 { + t.Errorf("TraceNumber dependent default value has changed %v", entry.TraceNumber) + } +} + +// TestMockIATEntryDetail tests validating an IATEntryDetail record +func TestMockIATEntryDetail(t *testing.T) { + testMockIATEntryDetail(t) +} + +// BenchmarkMockIATEntryDetail benchmarks validating an IATEntryDetail record +func BenchmarkIATMockEntryDetail(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testMockIATEntryDetail(b) + } +} + +// testParseIATEntryDetail parses a known IATEntryDetail record string. +func testParseIATEntryDetail(t testing.TB) { + var line = "6221210428820007 000010000012345678901234567890123456789012345 1231380100000001" + r := NewReader(strings.NewReader(line)) + r.addIATCurrentBatch(NewBatchIAT(mockIATBatchHeaderFF())) + r.IATCurrentBatch.SetHeader(mockIATBatchHeaderFF()) + r.line = line + if err := r.parseIATEntryDetail(); err != nil { + t.Errorf("%T: %s", err, err) + } + record := r.IATCurrentBatch.GetEntries()[0] + + if record.recordType != "6" { + t.Errorf("RecordType Expected '6' got: %v", record.recordType) + } + if record.TransactionCode != 22 { + t.Errorf("TransactionCode Expected '22' got: %v", record.TransactionCode) + } + if record.RDFIIdentificationField() != "12104288" { + t.Errorf("RDFIIdentification Expected '12104288' got: '%v'", record.RDFIIdentificationField()) + } + + if record.AddendaRecordsField() != "0007" { + t.Errorf("addendaRecords Expected '0007' got: %v", record.AddendaRecords) + } + if record.CheckDigit != "2" { + t.Errorf("CheckDigit Expected '2' got: %v", record.CheckDigit) + } + if record.AmountField() != "0000100000" { + t.Errorf("Amount Expected '0000100000' got: %v", record.AmountField()) + } + if record.DFIAccountNumberField() != "12345678901234567890123456789012345" { + t.Errorf("DfiAccountNumber Expected '12345678901234567890123456789012345' got: %v", record.DFIAccountNumberField()) + } + if record.AddendaRecordIndicator != 1 { + t.Errorf("AddendaRecordIndicator Expected '0' got: %v", record.AddendaRecordIndicator) + } + if record.TraceNumberField() != "231380100000001" { + t.Errorf("TraceNumber Expected '231380100000001' got: %v", record.TraceNumberField()) + } +} + +// TestParseIATEntryDetail tests parsing a known IATEntryDetail record string. +func TestParseIATEntryDetail(t *testing.T) { + testParseIATEntryDetail(t) +} + +// BenchmarkParseIATEntryDetail benchmarks parsing a known IATEntryDetail record string. +func BenchmarkParseIATEntryDetail(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testParseIATEntryDetail(b) + } +} + +// testIATEDString validates that a known parsed entry +// detail can be returned to a string of the same value +func testIATEDString(t testing.TB) { + var line = "6221210428820007 000010000012345678901234567890123456789012345 1231380100000001" + r := NewReader(strings.NewReader(line)) + r.addIATCurrentBatch(NewBatchIAT(mockIATBatchHeaderFF())) + r.IATCurrentBatch.SetHeader(mockIATBatchHeaderFF()) + r.line = line + if err := r.parseIATEntryDetail(); err != nil { + t.Errorf("%T: %s", err, err) + } + record := r.IATCurrentBatch.GetEntries()[0] + + if record.String() != line { + t.Errorf("Strings do not match") + } +} + +// TestIATEDString tests validating that a known parsed entry +// detail can be returned to a string of the same value +func TestIATEDString(t *testing.T) { + testIATEDString(t) +} + +// BenchmarkIATEDString benchmarks validating that a known parsed entry +// detail can be returned to a string of the same value +func BenchmarkIATEDString(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDString(b) + } +} + +// testIATEDInvalidRecordType validates error for IATEntryDetail invalid recordType +func testIATEDInvalidRecordType(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.recordType = "2" + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDInvalidRecordType tests validating error for IATEntryDetail invalid recordType +func TestIATEDInvalidRecordType(t *testing.T) { + testIATEDInvalidRecordType(t) +} + +// BenchmarkIATEDRecordType benchmarks validating error for IATEntryDetail invalid recordType +func BenchmarkIATEDInvalidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDInvalidRecordType(b) + } +} + +// testIATEDInvalidTransactionCode validates error for IATEntryDetail invalid TransactionCode +func testIATEDInvalidTransactionCode(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.TransactionCode = 77 + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDInvalidTransactionCode tests validating error for IATEntryDetail invalid TransactionCode +func TestIATEDInvalidTransactionCode(t *testing.T) { + testIATEDInvalidTransactionCode(t) +} + +// BenchmarkIATEDTransactionCode benchmarks validating error for IATEntryDetail invalid TransactionCode +func BenchmarkIATEDInvalidTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDInvalidTransactionCode(b) + } +} + +// testEDIATDFIAccountNumberAlphaNumeric validates company identification is alphanumeric +func testEDIATDFIAccountNumberAlphaNumeric(t testing.TB) { + ed := mockIATEntryDetail() + ed.DFIAccountNumber = "®" + if err := ed.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "DFIAccountNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestEDIATDFIAccountNumberAlphaNumeric tests validating company identification is alphanumeric +func TestEDIATDFIAccountNumberAlphaNumeric(t *testing.T) { + testEDIATDFIAccountNumberAlphaNumeric(t) +} + +// BenchmarkEDIATDFIAccountNumberAlphaNumeric benchmarks validating company identification is alphanumeric +func BenchmarkEDIATDFIAccountNumberAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDIATDFIAccountNumberAlphaNumeric(b) + } +} + +// testEDIATisCheckDigit validates check digit +func testEDIATisCheckDigit(t testing.TB) { + ed := mockIATEntryDetail() + ed.CheckDigit = "1" + if err := ed.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "RDFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestEDIATisCheckDigit tests validating check digit +func TestEDIATisCheckDigit(t *testing.T) { + testEDIATisCheckDigit(t) +} + +// BenchmarkEDIATisCheckDigit benchmarks validating check digit +func BenchmarkEDIATisCheckDigit(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDIATisCheckDigit(b) + } +} + +// testEDIATSetRDFII validates setting RDFI +func testEDIATSetRDFI(t testing.TB) { + ed := NewIATEntryDetail() + ed.SetRDFI("810866774") + if ed.RDFIIdentification != "81086677" { + t.Error("RDFI identification") + } + if ed.CheckDigit != "4" { + t.Error("Unexpected check digit") + } +} + +// TestEDIATSetRDFI tests validating setting RDFI +func TestEDIATSetRDFI(t *testing.T) { + testEDIATSetRDFI(t) +} + +// BenchmarkEDIATSetRDFI benchmarks validating setting RDFI +func BenchmarkEDIATSetRDFI(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testEDIATSetRDFI(b) + } +} + +// testValidateEDIATCheckDigit validates CheckDigit error +func testValidateEDIATCheckDigit(t testing.TB) { + ed := mockIATEntryDetail() + ed.CheckDigit = "XYZ" + if err := ed.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "CheckDigit" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestValidateEDIATCheckDigit tests validating validates CheckDigit error +func TestValidateEDIATCheckDigit(t *testing.T) { + testValidateEDIATCheckDigit(t) +} + +// BenchmarkValidateEDIATCheckDigit benchmarks validating CheckDigit error +func BenchmarkValidateEDIATCheckDigit(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testValidateEDIATCheckDigit(b) + } +} + +//FieldInclusion + +// testIATEDRecordType validates IATEntryDetail recordType fieldInclusion +func testIATEDRecordType(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.recordType = "" + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDRecordType tests validating IATEntryDetail recordType fieldInclusion +func TestIATEDRecordType(t *testing.T) { + testIATEDRecordType(t) +} + +// BenchmarkIATEDRecordType benchmarks validating IATEntryDetail recordType fieldInclusion +func BenchmarkIATEDRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDRecordType(b) + } +} + +// testIATEDTransactionCode validates IATEntryDetail TransactionCode fieldInclusion +func testIATEDTransactionCode(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.TransactionCode = 0 + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDTransactionCode tests validating IATEntryDetail TransactionCode fieldInclusion +func TestIATEDTransactionCode(t *testing.T) { + testIATEDTransactionCode(t) +} + +// BenchmarkIATEDTransactionCode benchmarks validating IATEntryDetail TransactionCode fieldInclusion +func BenchmarkIATEDTransactionCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDTransactionCode(b) + } +} + +// testIATEDRDFIIdentification validates IATEntryDetail RDFIIdentification fieldInclusion +func testIATEDRDFIIdentification(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.RDFIIdentification = "" + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "RDFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDRDFIIdentification tests validating IATEntryDetail RDFIIdentification fieldInclusion +func TestIATEDRDFIIdentification(t *testing.T) { + testIATEDRDFIIdentification(t) +} + +// BenchmarkIATEDRDFIIdentification benchmarks validating IATEntryDetail RDFIIdentification fieldInclusion +func BenchmarkIATEDRDFIIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDRDFIIdentification(b) + } +} + +// testIATEDAddendaRecords validates IATEntryDetail AddendaRecords fieldInclusion +func testIATEDAddendaRecords(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.AddendaRecords = 0 + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "AddendaRecords" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDAddendaRecords tests validating IATEntryDetail AddendaRecords fieldInclusion +func TestIATEDAddendaRecords(t *testing.T) { + testIATEDAddendaRecords(t) +} + +// BenchmarkIATEDAddendaRecords benchmarks validating IATEntryDetail AddendaRecords fieldInclusion +func BenchmarkIATEDAddendaRecords(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDAddendaRecords(b) + } +} + +// testIATEDDFIAccountNumber validates IATEntryDetail DFIAccountNumber fieldInclusion +func testIATEDDFIAccountNumber(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.DFIAccountNumber = "" + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "DFIAccountNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDDFIAccountNumber tests validating IATEntryDetail DFIAccountNumber fieldInclusion +func TestIATEDDFIAccountNumber(t *testing.T) { + testIATEDDFIAccountNumber(t) +} + +// BenchmarkIATEDDFIAccountNumber benchmarks validating IATEntryDetail DFIAccountNumber fieldInclusion +func BenchmarkIATEDDFIAccountNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDDFIAccountNumber(b) + } +} + +// testIATEDTraceNumber validates IATEntryDetail TraceNumber fieldInclusion +func testIATEDTraceNumber(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.TraceNumber = 0 + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDTraceNumber tests validating IATEntryDetail TraceNumber fieldInclusion +func TestIATEDTraceNumber(t *testing.T) { + testIATEDTraceNumber(t) +} + +// BenchmarkIATEDTraceNumber benchmarks validating IATEntryDetail TraceNumber fieldInclusion +func BenchmarkIATEDTraceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDTraceNumber(b) + } +} + +// testIATEDAddendaRecordIndicator validates IATEntryDetail AddendaIndicator fieldInclusion +func testIATEDAddendaRecordIndicator(t testing.TB) { + iatEd := mockIATEntryDetail() + iatEd.AddendaRecordIndicator = 0 + if err := iatEd.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "AddendaRecordIndicator" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestIATEDAddendaRecordIndicator tests validating IATEntryDetail AddendaRecordIndicator fieldInclusion +func TestIATEDAddendaRecordIndicator(t *testing.T) { + testIATEDAddendaRecordIndicator(t) +} + +// BenchmarkIATEDAddendaRecordIndicator benchmarks validating IATEntryDetail AddendaRecordIndicator fieldInclusion +func BenchmarkIATEDAddendaRecordIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATEDAddendaRecordIndicator(b) + } +} diff --git a/reader.go b/reader.go index e3f7c2202..c7a9dfe56 100644 --- a/reader.go +++ b/reader.go @@ -36,6 +36,8 @@ type Reader struct { line string // currentBatch is the current Batch entries being parsed currentBatch Batcher + // IATCurrentBatch is the current IATBatch entries being parsed + IATCurrentBatch IATBatcher // line number of the file being parsed lineNum int // recordName holds the current record name being parsed. @@ -57,6 +59,12 @@ func (r *Reader) addCurrentBatch(batch Batcher) { r.currentBatch = batch } +// addCurrentBatch creates the current batch type for the file being read. A successful +// current batch will be added to r.File once parsed. +func (r *Reader) addIATCurrentBatch(iatBatch IATBatcher) { + r.IATCurrentBatch = iatBatch +} + // NewReader returns a new ACH Reader that reads from r. func NewReader(r io.Reader) *Reader { return &Reader{ @@ -204,9 +212,12 @@ func (r *Reader) parseBatchHeader() error { return nil } +// ToDo: come up with a switch - entryDetailer back to that? + // parseEntryDetail takes the input record string and parses the EntryDetailRecord values func (r *Reader) parseEntryDetail() error { r.recordName = "EntryDetail" + if r.currentBatch == nil { return r.error(&FileError{Msg: msgFileBatchOutside}) } @@ -299,3 +310,45 @@ func (r *Reader) parseFileControl() error { } return nil } + +// parseIATBatchHeader takes the input record string and parses the FileHeaderRecord values +func (r *Reader) parseIATBatchHeader() error { + r.recordName = "IATBatchHeader" + if r.IATCurrentBatch != nil { + // batch header inside of current batch + return r.error(&FileError{Msg: msgFileBatchInside}) + } + + // Ensure we have a valid IAT BatchHeader before building a batch. + bh := NewIATBatchHeader() + bh.Parse(r.line) + if err := bh.Validate(); err != nil { + return r.error(err) + } + + // Passing BatchHeader into NewBatchIAT creates a Batcher of IAT SEC code type. + iatBatch, err := IATNewBatch(bh) + if err != nil { + return r.error(err) + } + + r.addIATCurrentBatch(iatBatch) + + return nil +} + +// parseIATEntryDetail takes the input record string and parses the EntryDetailRecord values +func (r *Reader) parseIATEntryDetail() error { + r.recordName = "IATEntryDetail" + + if r.IATCurrentBatch == nil { + return r.error(&FileError{Msg: msgFileBatchOutside}) + } + ed := new(IATEntryDetail) + ed.Parse(r.line) + if err := ed.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.AddEntry(ed) + return nil +} diff --git a/validators.go b/validators.go index a88a9f7ea..47f359897 100644 --- a/validators.go +++ b/validators.go @@ -31,6 +31,9 @@ var ( msgValidMonth = "is an invalid month" msgValidDay = "is an invalid day" msgValidYear = "is an invalid year" + // IAT + msgForeignExchangeIndicator = "is an invalid Foreign Exchange Indicator" + msgForeignExchangeReferenceIndicator = "is an invalid Foreign Exchange Reference Indicator" ) // validator is common validation and formatting of golang types to ach type strings @@ -144,6 +147,28 @@ func (v *validator) isDay(m string, d string) error { return errors.New(msgValidDay) } +// isForeignExchangeIndicator ensures foreign exchange indicators of an +// IATBatchHeader is valid +func (v *validator) isForeignExchangeIndicator(code string) error { + switch code { + case + "FV", "VF", "FF": + return nil + } + return errors.New(msgForeignExchangeIndicator) +} + +// isForeignExchangeReferenceIndicator ensures foreign exchange reference +// indicator of am IATBatchHeader is valid +func (v *validator) isForeignExchangeReferenceIndicator(code int) error { + switch code { + case + 1, 2, 3: + return nil + } + return errors.New(msgForeignExchangeReferenceIndicator) +} + // isOriginatorStatusCode ensures status code of a batch is valid func (v *validator) isOriginatorStatusCode(code int) error { switch code { From dcaef5005c5b6c3b4ae3acc19f290b8e03fb99f2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 13:16:50 -0400 Subject: [PATCH 0262/1694] #211 megacheck #211 megacheck --- iatBatch.go | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/iatBatch.go b/iatBatch.go index 9a3b7ae09..663044891 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -338,33 +338,6 @@ func (batch *iatBatch) isAddendaSequence() error { return nil } -// isAddendaCount iterates through each entry detail and checks the number of addendum is greater than the count parameter otherwise it returns an error. -// Following SEC codes allow for none or one Addendum -// "PPD", "WEB", "CCD", "CIE", "DNE", "MTE", "POS", "SHR" -func (batch *iatBatch) isAddendaCount(count int) error { - for _, entry := range batch.Entries { - if len(entry.Addendum) > count { - msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addendum), count, batch.Header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaCount", Msg: msg} - } - - } - return nil -} - -// isTypeCode takes a TypeCode string and verifies Addenda records match -func (batch *iatBatch) isTypeCode(typeCode string) error { - for _, entry := range batch.Entries { - for _, addenda := range entry.Addendum { - if addenda.TypeCode() != typeCode { - msg := fmt.Sprintf(msgBatchTypeCode, addenda.TypeCode(), typeCode, batch.Header.StandardEntryClassCode) - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TypeCode", Msg: msg} - } - } - } - return nil -} - // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *iatBatch) isCategory() error { category := batch.GetEntries()[0].Category From 287e7a6c779beb7384325a9d7aaae8f7ecf610cd Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 15:07:49 -0400 Subject: [PATCH 0263/1694] #211 Formatting #211 Formatting --- entryDetail.go | 10 ---------- entryDetail_test.go | 2 +- iatEntryDetail.go | 16 +--------------- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/entryDetail.go b/entryDetail.go index 3a3c3bca5..0594650b2 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -29,28 +29,21 @@ type EntryDetail struct { // Debit to savings account ‘37’ // Prenote for debit to savings account ‘38’ TransactionCode int `json:"transactionCode"` - // RDFIIdentification is the RDFI's routing number without the last digit. // Receiving Depository Financial Institution RDFIIdentification string `json:"RDFIIdentification"` - // CheckDigit the last digit of the RDFI's routing number CheckDigit string `json:"checkDigit"` - // DFIAccountNumber is the receiver's bank account number you are crediting/debiting. // It important to note that this is an alphanumeric field, so its space padded, no zero padded DFIAccountNumber string `json:"DFIAccountNumber"` - // Amount Number of cents you are debiting/crediting this account Amount int `json:"amount"` - // IdentificationNumber an internal identification (alphanumeric) that // you use to uniquely identify this Entry Detail Record IdentificationNumber string `json:"identificationNumber,omitempty"` - // IndividualName The name of the receiver, usually the name on the bank account IndividualName string `json:"individualName"` - // DiscretionaryData allows ODFIs to include codes, of significance only to them, // to enable specialized handling of the entry. There will be no // standardized interpretation for the value of this field. It can either @@ -60,12 +53,10 @@ type EntryDetail struct { // // WEB uses the Discretionary Data Field as the Payment Type Code DiscretionaryData string `json:"discretionaryData,omitempty"` - // AddendaRecordIndicator indicates the existence of an Addenda Record. // A value of "1" indicates that one ore more addenda records follow, // and "0" means no such record is present. AddendaRecordIndicator int `json:"addendaRecordIndicator,omitempty"` - // TraceNumber assigned by the ODFI in ascending sequence, is included in each // Entry Detail Record, Corporate Entry Detail Record, and addenda Record. // Trace Numbers uniquely identify each entry within a batch in an ACH input file. @@ -75,7 +66,6 @@ type EntryDetail struct { // in the associated Entry Detail Record, since the Trace Number is associated // with an entry or item rather than a physical record. TraceNumber int `json:"traceNumber,omitempty"` - // Addendum a list of Addenda for the Entry Detail Addendum []Addendumer `json:"addendum,omitempty"` // Category defines if the entry is a Forward, Return, or NOC diff --git a/entryDetail_test.go b/entryDetail_test.go index 40228666b..74b6632d6 100644 --- a/entryDetail_test.go +++ b/entryDetail_test.go @@ -378,7 +378,7 @@ func TestEDSetRDFI(t *testing.T) { testEDSetRDFI(t) } -// Benchmark benchmarks validating setting RDFI +// BenchmarkEDSetRDFI benchmarks validating setting RDFI func BenchmarkEDSetRDFI(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 83eabe2d8..886e28e0b 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -16,10 +16,8 @@ import ( type IATEntryDetail struct { // ID is a client defined string used as a reference to this record. ID string `json:"id"` - // RecordType defines the type of record in the block. 6 recordType string - // TransactionCode if the receivers account is: // Credit (deposit) to checking account ‘22’ // Prenote for credit to checking account ‘23’ @@ -30,41 +28,30 @@ type IATEntryDetail struct { // Debit to savings account ‘37’ // Prenote for debit to savings account ‘38’ TransactionCode int `json:"transactionCode"` - // RDFIIdentification is the RDFI's routing number without the last digit. // Receiving Depository Financial Institution RDFIIdentification string `json:"RDFIIdentification"` - // CheckDigit the last digit of the RDFI's routing number CheckDigit string `json:"checkDigit"` - // AddendaRecords is the number of Addenda Records AddendaRecords int `json:"AddendaRecords"` - // reserved - Leave blank reserved string - // Amount Number of cents you are debiting/crediting this account Amount int `json:"amount"` - // DFIAccountNumber is the receiver's bank account number you are crediting/debiting. // It important to note that this is an alphanumeric field, so its space padded, no zero padded DFIAccountNumber string `json:"DFIAccountNumber"` - - // reserved2 - Leave blank + // reservedTwo - Leave blank reservedTwo string - // OFACSreeningIndicator - Leave blank OFACSreeningIndicator string `json:"OFACSreeningIndicator"` - // SecondaryOFACSreeningIndicator - Leave blank SecondaryOFACSreeningIndicator string `json:"SecondaryOFACSreeningIndicator"` - // AddendaRecordIndicator indicates the existence of an Addenda Record. // A value of "1" indicates that one ore more addenda records follow, // and "0" means no such record is present. AddendaRecordIndicator int `json:"addendaRecordIndicator,omitempty"` - // TraceNumber assigned by the ODFI in ascending sequence, is included in each // Entry Detail Record, Corporate Entry Detail Record, and addenda Record. // Trace Numbers uniquely identify each entry within a batch in an ACH input file. @@ -74,7 +61,6 @@ type IATEntryDetail struct { // in the associated Entry Detail Record, since the Trace Number is associated // with an entry or item rather than a physical record. TraceNumber int `json:"traceNumber,omitempty"` - // Addendum a list of Addenda for the Entry Detail Addendum []Addendumer `json:"addendum,omitempty"` // Category defines if the entry is a Forward, Return, or NOC From c676384b95b398f31d19b69ac93fdf63dfaaca46 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 18:49:20 -0400 Subject: [PATCH 0264/1694] #211 SEC Code IAT #211 SEC Code IAT --- file.go | 32 +++++++++++++++++++++++++++----- reader.go | 25 +++++++++++++++++++------ 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/file.go b/file.go index 74123019d..9fa9f6f7a 100644 --- a/file.go +++ b/file.go @@ -53,10 +53,11 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { - ID string `json:"id"` - Header FileHeader `json:"fileHeader"` - Batches []Batcher `json:"batches"` - Control FileControl `json:"fileControl"` + ID string `json:"id"` + Header FileHeader `json:"fileHeader"` + Batches []Batcher `json:"batches"` + IATBatches []IATBatcher `json:"IATBatches"` + Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches NotificationOfChange []*BatchCOR @@ -81,7 +82,7 @@ func (f *File) Create() error { return err } // Requires at least one Batch in the new file. - if len(f.Batches) <= 0 { + if len(f.Batches) <= 0 && len(f.Batches) <= 0 { return &FileError{FieldName: "Batches", Value: strconv.Itoa(len(f.Batches)), Msg: "must have []*Batches to be built"} } // add 2 for FileHeader/control and reset if build was called twice do to error @@ -105,6 +106,21 @@ func (f *File) Create() error { totalDebitAmount = totalDebitAmount + batch.GetControl().TotalDebitEntryDollarAmount totalCreditAmount = totalCreditAmount + batch.GetControl().TotalCreditEntryDollarAmount + } + for i, iatBatch := range f.IATBatches { + // create ascending batch numbers + f.IATBatches[i].GetHeader().BatchNumber = batchSeq + f.IATBatches[i].GetControl().BatchNumber = batchSeq + batchSeq++ + // sum file entry and addenda records. Assume batch.Create() batch properly calculated control + fileEntryAddendaCount = fileEntryAddendaCount + iatBatch.GetControl().EntryAddendaCount + // add 2 for Batch header/control + entry added count + totalRecordsInFile = totalRecordsInFile + 2 + iatBatch.GetControl().EntryAddendaCount + // sum hash from batch control. Assume Batch.Build properly calculated field. + fileEntryHashSum = fileEntryHashSum + iatBatch.GetControl().EntryHash + totalDebitAmount = totalDebitAmount + iatBatch.GetControl().TotalDebitEntryDollarAmount + totalCreditAmount = totalCreditAmount + iatBatch.GetControl().TotalCreditEntryDollarAmount + } // create FileControl from calculated values fc := NewFileControl() @@ -137,6 +153,12 @@ func (f *File) AddBatch(batch Batcher) []Batcher { return f.Batches } +// AddIATBatch appends a IATBatch to the ach.File +func (f *File) AddIATBatch(iatBatch IATBatcher) []IATBatcher { + f.IATBatches = append(f.IATBatches, iatBatch) + return f.IATBatches +} + // SetHeader allows for header to be built. func (f *File) SetHeader(h FileHeader) *File { f.Header = h diff --git a/reader.go b/reader.go index c7a9dfe56..bb85def9d 100644 --- a/reader.go +++ b/reader.go @@ -136,12 +136,27 @@ func (r *Reader) parseLine() error { return err } case batchHeaderPos: - if err := r.parseBatchHeader(); err != nil { - return err + switch r.line[49:53] { + case "IAT": + if err := r.parseIATBatchHeader(); err != nil { + return err + } + default: + if err := r.parseBatchHeader(); err != nil { + return err + } } case entryDetailPos: - if err := r.parseEntryDetail(); err != nil { - return err + switch r.line[16:29] { + + case " ": + if err := r.parseIATEntryDetail(); err != nil { + return err + } + default: + if err := r.parseEntryDetail(); err != nil { + return err + } } case entryAddendaPos: if err := r.parseAddenda(); err != nil { @@ -212,8 +227,6 @@ func (r *Reader) parseBatchHeader() error { return nil } -// ToDo: come up with a switch - entryDetailer back to that? - // parseEntryDetail takes the input record string and parses the EntryDetailRecord values func (r *Reader) parseEntryDetail() error { r.recordName = "EntryDetail" From b11a0037c419ac2e4bbcca9b385a4410443a1590 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 18:51:33 -0400 Subject: [PATCH 0265/1694] #211 IAT support #211 IAT Support --- file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file.go b/file.go index 9fa9f6f7a..fa983e0de 100644 --- a/file.go +++ b/file.go @@ -82,7 +82,7 @@ func (f *File) Create() error { return err } // Requires at least one Batch in the new file. - if len(f.Batches) <= 0 && len(f.Batches) <= 0 { + if len(f.Batches) <= 0 && len(f.IATBatches) <= 0 { return &FileError{FieldName: "Batches", Value: strconv.Itoa(len(f.Batches)), Msg: "must have []*Batches to be built"} } // add 2 for FileHeader/control and reset if build was called twice do to error From d63c62a1d171729ce2b9ffaa9527c52861236ae2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 19:01:03 -0400 Subject: [PATCH 0266/1694] #211 Fix Reader.go megacheck issue #211 Fix Reader.go megacheck issue --- reader.go | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/reader.go b/reader.go index bb85def9d..bd7a026b4 100644 --- a/reader.go +++ b/reader.go @@ -136,27 +136,12 @@ func (r *Reader) parseLine() error { return err } case batchHeaderPos: - switch r.line[49:53] { - case "IAT": - if err := r.parseIATBatchHeader(); err != nil { - return err - } - default: - if err := r.parseBatchHeader(); err != nil { - return err - } + if err := r.parseBatchHeader(); err != nil { + return err } case entryDetailPos: - switch r.line[16:29] { - - case " ": - if err := r.parseIATEntryDetail(); err != nil { - return err - } - default: - if err := r.parseEntryDetail(); err != nil { - return err - } + if err := r.parseEntryDetail(); err != nil { + return err } case entryAddendaPos: if err := r.parseAddenda(); err != nil { @@ -227,6 +212,8 @@ func (r *Reader) parseBatchHeader() error { return nil } + + // parseEntryDetail takes the input record string and parses the EntryDetailRecord values func (r *Reader) parseEntryDetail() error { r.recordName = "EntryDetail" @@ -364,4 +351,4 @@ func (r *Reader) parseIATEntryDetail() error { } r.IATCurrentBatch.AddEntry(ed) return nil -} +} \ No newline at end of file From 3d98db2fb638fb3d99023702ecee74abce1ee10d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 19:01:42 -0400 Subject: [PATCH 0267/1694] #211 reader.go govet #211 reader.go govet --- reader.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/reader.go b/reader.go index bd7a026b4..06672f9f7 100644 --- a/reader.go +++ b/reader.go @@ -212,8 +212,6 @@ func (r *Reader) parseBatchHeader() error { return nil } - - // parseEntryDetail takes the input record string and parses the EntryDetailRecord values func (r *Reader) parseEntryDetail() error { r.recordName = "EntryDetail" @@ -351,4 +349,4 @@ func (r *Reader) parseIATEntryDetail() error { } r.IATCurrentBatch.AddEntry(ed) return nil -} \ No newline at end of file +} From 908b742fad4bf127c92f0a472dade0f83aa7d2f7 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 19:16:44 -0400 Subject: [PATCH 0268/1694] #211 IAT reader modifications #211 IAT reader modifications --- reader.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/reader.go b/reader.go index 06672f9f7..5fb7f2c12 100644 --- a/reader.go +++ b/reader.go @@ -136,12 +136,26 @@ func (r *Reader) parseLine() error { return err } case batchHeaderPos: - if err := r.parseBatchHeader(); err != nil { - return err + switch r.line[50:53] { + case "IAT": + if err := r.parseIATBatchHeader(); err != nil { + return err + } + default: + if err := r.parseBatchHeader(); err != nil { + return err + } } case entryDetailPos: - if err := r.parseEntryDetail(); err != nil { - return err + switch r.line[16:29] { + case " ": + if err := r.parseIATEntryDetail(); err != nil { + return err + } + default: + if err := r.parseEntryDetail(); err != nil { + return err + } } case entryAddendaPos: if err := r.parseAddenda(); err != nil { From ecb470712a5afc0d44a617d15e56862655e61fec Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 19:22:40 -0400 Subject: [PATCH 0269/1694] revert due to gocyclo error revert due to gocyclo error --- reader.go | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/reader.go b/reader.go index 5fb7f2c12..06672f9f7 100644 --- a/reader.go +++ b/reader.go @@ -136,26 +136,12 @@ func (r *Reader) parseLine() error { return err } case batchHeaderPos: - switch r.line[50:53] { - case "IAT": - if err := r.parseIATBatchHeader(); err != nil { - return err - } - default: - if err := r.parseBatchHeader(); err != nil { - return err - } + if err := r.parseBatchHeader(); err != nil { + return err } case entryDetailPos: - switch r.line[16:29] { - case " ": - if err := r.parseIATEntryDetail(); err != nil { - return err - } - default: - if err := r.parseEntryDetail(); err != nil { - return err - } + if err := r.parseEntryDetail(); err != nil { + return err } case entryAddendaPos: if err := r.parseAddenda(); err != nil { From 32640ba1c5de20f1633323e653660a7aeab847e6 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 20:45:26 -0400 Subject: [PATCH 0270/1694] #211 reader.go Add parseBH and parseED --- reader.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/reader.go b/reader.go index 06672f9f7..ed5cf9c03 100644 --- a/reader.go +++ b/reader.go @@ -136,11 +136,11 @@ func (r *Reader) parseLine() error { return err } case batchHeaderPos: - if err := r.parseBatchHeader(); err != nil { + if err := r.parseBH(); err != nil { return err } case entryDetailPos: - if err := r.parseEntryDetail(); err != nil { + if err := r.parseED(); err != nil { return err } case entryAddendaPos: @@ -350,3 +350,31 @@ func (r *Reader) parseIATEntryDetail() error { r.IATCurrentBatch.AddEntry(ed) return nil } + +// parseBH parses determines whether to parse an IATBatchHeader or BatchHeader +func (r *Reader) parseBH() error { + if r.line[50:53] == "IAT" { + if err := r.parseIATBatchHeader(); err != nil { + return err + } + } else { + if err := r.parseBatchHeader(); err != nil { + return err + } + } + return nil +} + +// parseEd parses determines whether to parse an IATEntryDetail or EntryDetail +func (r *Reader) parseED() error { + if r.line[16:29] == " " { + if err := r.parseIATEntryDetail(); err != nil { + return err + } + } else { + if err := r.parseEntryDetail(); err != nil { + return err + } + } + return nil +} From 8e5fd7e229b99ec67ba7ea1d76bed15913baeade Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 22:26:52 -0400 Subject: [PATCH 0271/1694] #211 Writer #211 Writer --- writer.go | 68 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/writer.go b/writer.go index 688966608..1822d46ad 100644 --- a/writer.go +++ b/writer.go @@ -40,6 +40,36 @@ func (w *Writer) Write(file *File) error { } w.lineNum++ + if err := w.writeBatch(file); err != nil { + return err + } + + if err := w.writeIATBatch(file); err != nil { + return err + } + + if _, err := w.w.WriteString(file.Control.String() + "\n"); err != nil { + return err + } + w.lineNum++ + + // pad the final block + for i := 0; i < (10-(w.lineNum%10)) && w.lineNum%10 != 0; i++ { + if _, err := w.w.WriteString(strings.Repeat("9", 94) + "\n"); err != nil { + return err + } + } + + return w.w.Flush() +} + +// Flush writes any buffered data to the underlying io.Writer. +// To check if an error occurred during the Flush, call Error. +func (w *Writer) Flush() { + w.w.Flush() +} + +func (w *Writer) writeBatch(file *File) error { for _, batch := range file.Batches { if _, err := w.w.WriteString(batch.GetHeader().String() + "\n"); err != nil { return err @@ -62,23 +92,31 @@ func (w *Writer) Write(file *File) error { } w.lineNum++ } - if _, err := w.w.WriteString(file.Control.String() + "\n"); err != nil { - return err - } - w.lineNum++ + return nil +} - // pad the final block - for i := 0; i < (10-(w.lineNum%10)) && w.lineNum%10 != 0; i++ { - if _, err := w.w.WriteString(strings.Repeat("9", 94) + "\n"); err != nil { +func (w *Writer) writeIATBatch(file *File) error { + for _, iatBatch := range file.IATBatches { + if _, err := w.w.WriteString(iatBatch.GetHeader().String() + "\n"); err != nil { return err } + w.lineNum++ + for _, entry := range iatBatch.GetEntries() { + if _, err := w.w.WriteString(entry.String() + "\n"); err != nil { + return err + } + w.lineNum++ + for _, addenda := range entry.Addendum { + if _, err := w.w.WriteString(addenda.String() + "\n"); err != nil { + return err + } + w.lineNum++ + } + } + if _, err := w.w.WriteString(iatBatch.GetControl().String() + "\n"); err != nil { + return err + } + w.lineNum++ } - - return w.w.Flush() -} - -// Flush writes any buffered data to the underlying io.Writer. -// To check if an error occurred during the Flush, call Error. -func (w *Writer) Flush() { - w.w.Flush() + return nil } From 682bc006249a972e5742439b9cef25125cae9922 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 26 Jun 2018 22:31:38 -0400 Subject: [PATCH 0272/1694] #211 iatBatch #211 iatBatch --- iatBatch.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iatBatch.go b/iatBatch.go index 663044891..6a3357b6d 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -307,6 +307,8 @@ func (batch *iatBatch) isTraceNumberODFI() error { return nil } +// ToDo: Adjustments for IAT Addenda + // isAddendaSequence check multiple errors on addenda records in the batch entries func (batch *iatBatch) isAddendaSequence() error { for _, entry := range batch.Entries { From 9bfd27703611d98eea935c5657bcd50833b707c4 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 27 Jun 2018 11:03:47 -0400 Subject: [PATCH 0273/1694] #211 reader.go #211 reader.go --- reader.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reader.go b/reader.go index ed5cf9c03..c39791610 100644 --- a/reader.go +++ b/reader.go @@ -367,6 +367,8 @@ func (r *Reader) parseBH() error { // parseEd parses determines whether to parse an IATEntryDetail or EntryDetail func (r *Reader) parseED() error { + // ToDo: Review if this can be true for domestic files. + // IATIndicator field if r.line[16:29] == " " { if err := r.parseIATEntryDetail(); err != nil { return err From 5693d61c19161b8eea1d489a6a65885d8651a26f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 28 Jun 2018 12:39:44 -0400 Subject: [PATCH 0274/1694] #211 Update unrelated to IAT Addenda02_test #211 Updates to Addenda02_test --- addenda02_test.go | 67 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/addenda02_test.go b/addenda02_test.go index dcc0b26b5..c174f126a 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -23,6 +23,7 @@ func mockAddenda02() *Addenda02 { return addenda02 } +// TestMockAddenda02 validates mockAddenda02 func TestMockAddenda02(t *testing.T) { addenda02 := mockAddenda02() if err := addenda02.Validate(); err != nil { @@ -30,6 +31,7 @@ func TestMockAddenda02(t *testing.T) { } } +// testAddenda02ValidRecordType validates Addenda02 recordType func testAddenda02ValidRecordType(t testing.TB) { addenda02 := mockAddenda02() addenda02.recordType = "63" @@ -43,10 +45,13 @@ func testAddenda02ValidRecordType(t testing.TB) { } } } + +// TestAddenda02ValidRecordType tests validating Addenda02 recordType func TestAddenda02ValidRecordType(t *testing.T) { testAddenda02ValidRecordType(t) } +// BenchmarkAddenda02ValidRecordType benchmarks validating Addenda02 recordType func BenchmarkAddenda02ValidRecordType(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -54,6 +59,7 @@ func BenchmarkAddenda02ValidRecordType(b *testing.B) { } } +// testAddenda02ValidTypeCode validates Addenda02 TypeCode func testAddenda02ValidTypeCode(t testing.TB) { addenda02 := mockAddenda02() addenda02.typeCode = "65" @@ -67,10 +73,13 @@ func testAddenda02ValidTypeCode(t testing.TB) { } } } + +// TestAddenda02ValidTypeCode tests validating Addenda02 TypeCode func TestAddenda02ValidTypeCode(t *testing.T) { testAddenda02ValidTypeCode(t) } +// BenchmarkAddenda02ValidTypeCode benchmarks validating Addenda02 TypeCode func BenchmarkAddenda02ValidTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -78,6 +87,7 @@ func BenchmarkAddenda02ValidTypeCode(b *testing.B) { } } +// testAddenda02TypeCode02 TypeCode is 02 if typeCode is a valid TypeCode func testAddenda02TypeCode02(t testing.TB) { addenda02 := mockAddenda02() addenda02.typeCode = "05" @@ -91,10 +101,13 @@ func testAddenda02TypeCode02(t testing.TB) { } } } + +// TestAddenda02TypeCode02 tests TypeCode is 02 if typeCode is a valid TypeCode func TestAddenda02TypeCode02(t *testing.T) { testAddenda02TypeCode02(t) } +// BenchmarkAddenda02TypeCode02 benchmarks TypeCode is 02 if typeCode is a valid TypeCode func BenchmarkAddenda02TypeCode02(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -102,7 +115,8 @@ func BenchmarkAddenda02TypeCode02(b *testing.B) { } } -func testAddenda02RecordType(t testing.TB) { +// testAddenda02FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda02FieldInclusionRecordType(t testing.TB) { addenda02 := mockAddenda02() addenda02.recordType = "" if err := addenda02.Validate(); err != nil { @@ -114,18 +128,21 @@ func testAddenda02RecordType(t testing.TB) { } } -func TestAddenda02RecordType(t *testing.T) { - testAddenda02RecordType(t) +// TestAddenda02FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda02FieldInclusionRecordType(t *testing.T) { + testAddenda02FieldInclusionRecordType(t) } +// BenchmarkAddenda02FieldInclusionRecordType benchmarks validating recordType fieldInclusion func BenchmarkAddenda02FieldInclusionRecordType(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testAddenda02RecordType(b) + testAddenda02FieldInclusionRecordType(b) } } -func testAddenda02TypeCode(t testing.TB) { +// testAddenda02FieldInclusionRecordType validates TypeCode fieldInclusion +func testAddenda02FieldInclusionTypeCode(t testing.TB) { addenda02 := mockAddenda02() addenda02.typeCode = "" if err := addenda02.Validate(); err != nil { @@ -137,17 +154,20 @@ func testAddenda02TypeCode(t testing.TB) { } } -func TestAddenda02TypeCode(t *testing.T) { - testAddenda02TypeCode(t) +// TestAddenda02FieldInclusionRecordType tests validating TypeCode fieldInclusion +func TestAddenda02FieldInclusionTypeCode(t *testing.T) { + testAddenda02FieldInclusionTypeCode(t) } -func BenchmarkAddenda02TypeCode(b *testing.B) { +// BenchmarkAddenda02FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda02FieldInclusionTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testAddenda02TypeCode(b) + testAddenda02FieldInclusionTypeCode(b) } } +// testAddenda02TerminalIdentificationCode validates TerminalIdentificationCode is required func testAddenda02TerminalIdentificationCode(t testing.TB) { addenda02 := mockAddenda02() addenda02.TerminalIdentificationCode = "" @@ -160,10 +180,12 @@ func testAddenda02TerminalIdentificationCode(t testing.TB) { } } +// TestAddenda02TerminalIdentificationCode tests validating TerminalIdentificationCode is required func TestAddenda02TerminalIdentificationCode(t *testing.T) { testAddenda02TerminalIdentificationCode(t) } +// BenchmarkAddenda02TerminalIdentificationCode benchmarks validating TerminalIdentificationCode is required func BenchmarkAddenda02TerminalIdentificationCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -171,6 +193,7 @@ func BenchmarkAddenda02TerminalIdentificationCode(b *testing.B) { } } +// testAddenda02TransactionSerialNumber validates TransactionSerialNumber is required func testAddenda02TransactionSerialNumber(t testing.TB) { addenda02 := mockAddenda02() addenda02.TransactionSerialNumber = "" @@ -183,10 +206,12 @@ func testAddenda02TransactionSerialNumber(t testing.TB) { } } +// TestAddenda02TransactionSerialNumber tests validating TransactionSerialNumber is required func TestAddenda02TransactionSerialNumber(t *testing.T) { testAddenda02TransactionSerialNumber(t) } +// BenchmarkAddenda02TransactionSerialNumber benchmarks validating TransactionSerialNumber is required func BenchmarkAddenda02TransactionSerialNumber(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -194,6 +219,7 @@ func BenchmarkAddenda02TransactionSerialNumber(b *testing.B) { } } +// testAddenda02TransactionDate validates TransactionDate is required func testAddenda02TransactionDate(t testing.TB) { addenda02 := mockAddenda02() addenda02.TransactionDate = "" @@ -206,10 +232,12 @@ func testAddenda02TransactionDate(t testing.TB) { } } +// TestAddenda02TransactionDate tests validating TransactionDate is required func TestAddenda02TransactionDate(t *testing.T) { testAddenda02TransactionDate(t) } +// BenchmarkAddenda02TransactionDate benchmarks validating TransactionDate is required func BenchmarkAddenda02TransactionDate(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -217,6 +245,7 @@ func BenchmarkAddenda02TransactionDate(b *testing.B) { } } +// testAddenda02TerminalLocation validates TerminalLocation is required func testAddenda02TerminalLocation(t testing.TB) { addenda02 := mockAddenda02() addenda02.TerminalLocation = "" @@ -229,10 +258,12 @@ func testAddenda02TerminalLocation(t testing.TB) { } } +// TestAddenda02TerminalLocation tests validating TerminalLocation is required func TestAddenda02TerminalLocation(t *testing.T) { testAddenda02TerminalLocation(t) } +// BenchmarkAddenda02TerminalLocation benchmarks validating TerminalLocation is required func BenchmarkAddenda02TerminalLocation(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -240,6 +271,7 @@ func BenchmarkAddenda02TerminalLocation(b *testing.B) { } } +// testAddenda02TerminalCity validates TerminalCity is required func testAddenda02TerminalCity(t testing.TB) { addenda02 := mockAddenda02() addenda02.TerminalCity = "" @@ -252,10 +284,12 @@ func testAddenda02TerminalCity(t testing.TB) { } } +// TestAddenda02TerminalCity tests validating TerminalCity is required func TestAddenda02TerminalCity(t *testing.T) { testAddenda02TerminalCity(t) } +// BenchmarkAddenda02TerminalCity benchmarks validating TerminalCity is required func BenchmarkAddenda02TerminalCity(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -263,6 +297,7 @@ func BenchmarkAddenda02TerminalCity(b *testing.B) { } } +// testAddenda02TerminalState validates TerminalState is required func testAddenda02TerminalState(t testing.TB) { addenda02 := mockAddenda02() addenda02.TerminalState = "" @@ -275,10 +310,12 @@ func testAddenda02TerminalState(t testing.TB) { } } +// TestAddenda02TerminalState tests validating TerminalState is required func TestAddenda02TerminalState(t *testing.T) { testAddenda02TerminalState(t) } +// BenchmarkAddenda02TerminalState benchmarks validating TerminalState is required func BenchmarkAddenda02TerminalState(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -332,7 +369,7 @@ func TestAddenda02TransactionDateMonth(t *testing.T) { testAddenda02TransactionDateMonth(t) } -// BenchmarkAddenda02TransactionDateMonth test validating the month is valid for transactionDate +// BenchmarkAddenda02TransactionDateMonth benchmarks validating the month is valid for transactionDate func BenchmarkAddenda02TransactionDateMonth(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -360,7 +397,7 @@ func TestAddenda02TransactionDateDay(t *testing.T) { testAddenda02TransactionDateDay(t) } -// BenchmarkAddenda02TransactionDateDay test validating the day is valid for transactionDate +// BenchmarkAddenda02TransactionDateDay benchmarks validating the day is valid for transactionDate func BenchmarkAddenda02TransactionDateDay(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -388,7 +425,7 @@ func TestAddenda02TransactionDateFeb(t *testing.T) { testAddenda02TransactionDateFeb(t) } -// BenchmarkAddenda02TransactionDateFeb test validating the day is valid for transactionDate +// BenchmarkAddenda02TransactionDateFeb benchmarks validating the day is valid for transactionDate func BenchmarkAddenda02TransactionDateFeb(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -416,7 +453,7 @@ func TestAddenda02TransactionDate30Day(t *testing.T) { testAddenda02TransactionDate30Day(t) } -// BenchmarkAddenda02TransactionDate30Day test validating the day is valid for transactionDate +// BenchmarkAddenda02TransactionDate30Day benchmarks validating the day is valid for transactionDate func BenchmarkAddenda02TransactionDate30Day(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -444,7 +481,7 @@ func TestAddenda02TransactionDate31Day(t *testing.T) { testAddenda02TransactionDate31Day(t) } -// BenchmarkAddenda02TransactionDate31Day test validating the day is valid for transactionDate +// BenchmarkAddenda02TransactionDate31Day benchmarks validating the day is valid for transactionDate func BenchmarkAddenda02TransactionDate31Day(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -472,7 +509,7 @@ func TestAddenda02TransactionDateInvalidDay(t *testing.T) { testAddenda02TransactionDateInvalidDay(t) } -// BenchmarkAddenda02TransactionDateInvalidDay test validating the day is invalid for transactionDate +// BenchmarkAddenda02TransactionDateInvalidDay benchmarks validating the day is invalid for transactionDate func BenchmarkAddenda02TransactionDateInvalidDay(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From 04da5114b6a65adacb2a1b43c5ea2b06475bb633 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 28 Jun 2018 12:44:39 -0400 Subject: [PATCH 0275/1694] #211 Addenda10 TransactionTypeCode #211 Addenda10 TransactionTypeCode --- validators.go | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/validators.go b/validators.go index 47f359897..5efe892b2 100644 --- a/validators.go +++ b/validators.go @@ -34,6 +34,7 @@ var ( // IAT msgForeignExchangeIndicator = "is an invalid Foreign Exchange Indicator" msgForeignExchangeReferenceIndicator = "is an invalid Foreign Exchange Reference Indicator" + msgAddenda10TransactionTypeCode = "is an invalid Addenda10 Transaction Type Code" ) // validator is common validation and formatting of golang types to ach type strings @@ -239,20 +240,20 @@ func (v *validator) isTypeCode(code string) error { // // The Tran Code is a two-digit code in positions 2 - 3 of the Entry Detail Record (6 Record) within an ACH File. // The first digit of the Tran Code indicates the account type to which the entry will post, where the number: -// "2"designates a Checking Account. -// "3"designates a Savings Account. -// "4"designates a General Ledger Account. -// "5"designates Loan Account. +// "2" designates a Checking Account. +// "3" designates a Savings Account. +// "4" designates a General Ledger Account. +// "5" designates Loan Account. //The second digit of the Tran Code identifies the entry as: // an original forward entry, where the number: -// "2"designates a credit. or -// "7"designates a debit. +// "2" designates a credit. or +// "7" designates a debit. // a return or NOC, where the number: -// "1"designates the return/NOC of a credit, or -// "6"designates a return/NOC of a debit. +// "1" designates the return/NOC of a credit, or +// "6" designates a return/NOC of a debit. // a pre-note or non-monetary informational transaction, where the number: -// "3"designates a credit, or -// "8"designates a debit. +// "3" designates a credit, or +// "8" designates a debit. func (v *validator) isTransactionCode(code int) error { switch code { // TransactionCode if the receivers account is: @@ -364,6 +365,21 @@ func (v *validator) isTransactionCode(code int) error { return errors.New(msgTransactionCode) } +// isTransactionTypeCode verifies Addenda10 TransactionTypeCode is a valid value +// ANN = Annuity, BUS = Business/Commercial, DEP = Deposit, LOA = Loan, MIS = Miscellaneous, MOR = Mortgage +// PEN = Pension, RLS = Rent/Lease, REM = Remittance2, SAL = Salary/Payroll, TAX = Tax, TEL = Telephone-Initiated Transaction +// WEB = Internet-Initiated Transaction, ARC = Accounts Receivable Entry, BOC = Back Office Conversion Entry, +// POP = Point of Purchase Entry, RCK = Re-presented Check Entry +func (v *validator) isTransactionTypeCode(s string) error { + switch s { + case "ANN", "BUS", "DEP", "LOA", "MIS", "MOR", + "PEN", "RLS", "REM", "SAL", "TAX", "TEL", "WEB", + "ARC", "BOC", "POP", "RCK": + return nil + } + return errors.New(msgAddenda10TransactionTypeCode) +} + // isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters func (v *validator) isUpperAlphanumeric(s string) error { if upperAlphanumericRegex.MatchString(s) { From ff4db1f4562c56ea4f45643f1132965205e9d03c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 28 Jun 2018 12:52:27 -0400 Subject: [PATCH 0276/1694] # 211 Addenda10 Addenda10 code Addenda10_test --- addenda10.go | 173 ++++++++++++++++++++++++++++++++++++++++++++++ addenda10_test.go | 161 ++++++++++++++++++++++++++++++++++++++++++ validators.go | 4 +- 3 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 addenda10.go create mode 100644 addenda10_test.go diff --git a/addenda10.go b/addenda10.go new file mode 100644 index 000000000..e37697451 --- /dev/null +++ b/addenda10.go @@ -0,0 +1,173 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" + "strconv" +) + +// Addenda10 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 10 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// It is mandatory for IAT entries +type Addenda10 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. + recordType string + // TypeCode Addenda10 types code '10' + typeCode string + // Transaction Type Code Describes the type of payment: + // ANN = Annuity, BUS = Business/Commercial, DEP = Deposit, LOA = Loan, MIS = Miscellaneous, MOR = Mortgage + // PEN = Pension, RLS = Rent/Lease, REM = Remittance2, SAL = Salary/Payroll, TAX = Tax, TEL = Telephone-Initiated Transaction + // WEB = Internet-Initiated Transaction, ARC = Accounts Receivable Entry, BOC = Back Office Conversion Entry, + // POP = Point of Purchase Entry, RCK = Re-presented Check Entry + TransactionTypeCode string `json:"transactionTypeCode"` + // Foreign Payment Amount $$$$$$$$$$$$$$$$¢¢ + // For inbound IAT payments this field should contain the USD amount or may be blank. + ForeignPaymentAmount int `json:"foreignPaymentAmount"` + // Foreign Trace Number + ForeignTraceNumber string `json:"foreignTraceNumber,omitempty"` + // Receiving Company Name/Individual Name + Name string `json:"name"` + // reserved - Leave blank + reserved string + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda10 returns a new Addenda10 with default values for none exported fields +func NewAddenda10() *Addenda10 { + addenda10 := new(Addenda10) + addenda10.recordType = "7" + addenda10.typeCode = "10" + return addenda10 +} + +// Parse takes the input record string and parses the Addenda10 values +func (addenda10 *Addenda10) Parse(record string) { + // 1-1 Always "7" + addenda10.recordType = "7" + // 2-3 Always 10 + addenda10.typeCode = record[1:3] + // 04-06 Describes the type of payment + addenda10.TransactionTypeCode = record[3:6] + // 07-24 Payment Amount For inbound IAT payments this field should contain the USD amount or may be blank. + addenda10.ForeignPaymentAmount = addenda10.parseNumField(record[06:24]) + // 25-46 Insert blanks or zeros + addenda10.ForeignTraceNumber = addenda10.parseStringField(record[24:46]) + // 47-81 Receiving Company Name/Individual Name + addenda10.Name = record[47:81] + // 82-87 reserved - Leave blank + addenda10.reserved = " " + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda10.EntryDetailSequenceNumber = addenda10.parseNumField(record[87:94]) +} + +// String writes the Addenda10 struct to a 94 character string. +func (addenda10 *Addenda10) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v", + addenda10.recordType, + addenda10.typeCode, + // TransactionTypeCode Validator + addenda10.TransactionTypeCode, + addenda10.ForeignPaymentAmountField(), + addenda10.ForeignTraceNumberField(), + addenda10.NameField(), + addenda10.reservedField(), + addenda10.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda10 *Addenda10) Validate() error { + if err := addenda10.fieldInclusion(); err != nil { + return err + } + if addenda10.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda10.recordType, Msg: msg} + } + if err := addenda10.isTypeCode(addenda10.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda10.typeCode, Msg: err.Error()} + } + if err := addenda10.isTransactionTypeCode(addenda10.TransactionTypeCode); err != nil { + return &FieldError{FieldName: "TransactionTypeCode", Value: addenda10.TransactionTypeCode, Msg: err.Error()} + } + // ToDo: Foreign Exchange Amount + if err := addenda10.isAlphanumeric(addenda10.ForeignTraceNumber); err != nil { + return &FieldError{FieldName: "ForeignTraceNumber", Value: addenda10.ForeignTraceNumber, Msg: err.Error()} + } + if err := addenda10.isAlphanumeric(addenda10.Name); err != nil { + return &FieldError{FieldName: "Name", Value: addenda10.Name, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda10 *Addenda10) fieldInclusion() error { + if addenda10.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda10.recordType, Msg: msgFieldInclusion} + } + if addenda10.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda10.typeCode, Msg: msgFieldInclusion} + } + if addenda10.TransactionTypeCode == "" { + return &FieldError{FieldName: "TransactionTypeCode", + Value: addenda10.TransactionTypeCode, Msg: msgFieldRequired} + } + if addenda10.ForeignPaymentAmount == 0 { + return &FieldError{FieldName: "ForeignPaymentAmount", + Value: strconv.Itoa(addenda10.ForeignPaymentAmount), Msg: msgFieldRequired} + } + if addenda10.Name == "" { + return &FieldError{FieldName: "Name", Value: addenda10.Name, Msg: msgFieldRequired} + } + if addenda10.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", + Value: addenda10.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// ForeignPaymentAmountField returns ForeignPaymentAmount zero padded +// Payment Amount For inbound IAT payments this field should contain the USD amount or may be blank. +// ToDo: Review/Add logic for blank +func (addenda10 *Addenda10) ForeignPaymentAmountField() string { + return addenda10.numericField(addenda10.ForeignPaymentAmount, 18) +} + +// ForeignTraceNumberField gets the Foreign TraceNumber left padded +func (addenda10 *Addenda10) ForeignTraceNumberField() string { + return addenda10.alphaField(addenda10.ForeignTraceNumber, 35) +} + +// NameField gets th name field - Receiving Company Name/Individual Name left padded +func (addenda10 *Addenda10) NameField() string { + return addenda10.alphaField(addenda10.Name, 22) +} + +// reservedField gets reserved - blank space +func (addenda10 *Addenda10) reservedField() string { + return addenda10.alphaField(addenda10.reserved, 6) +} + +// TypeCode Defines the specific explanation and format for the addenda10 information left padded +func (addenda10 *Addenda10) TypeCode() string { + return addenda10.typeCode +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda10 *Addenda10) EntryDetailSequenceNumberField() string { + return addenda10.numericField(addenda10.EntryDetailSequenceNumber, 7) +} diff --git a/addenda10_test.go b/addenda10_test.go new file mode 100644 index 000000000..fa177aef3 --- /dev/null +++ b/addenda10_test.go @@ -0,0 +1,161 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import "testing" + +func mockAddenda10() *Addenda10 { + addenda10 := NewAddenda10() + addenda10.TransactionTypeCode = "ANN" + addenda10.ForeignPaymentAmount = 100000 + addenda10.ForeignTraceNumber = "928383-23938" + addenda10.Name = "BEK Enterprises" + addenda10.EntryDetailSequenceNumber = 00000001 + return addenda10 +} + +// TestMockAddenda10 validates mockAddenda10 +func TestMockAddenda10(t *testing.T) { + addenda10 := mockAddenda10() + if err := addenda10.Validate(); err != nil { + t.Error("mockAddenda10 does not validate and will break other tests") + } +} + +// testAddenda10ValidRecordType validates Addenda10 recordType +func testAddenda10ValidRecordType(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.recordType = "63" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda10ValidRecordType tests validating Addenda10 recordType +func TestAddenda10ValidRecordType(t *testing.T) { + testAddenda10ValidRecordType(t) +} + +// BenchmarkAddenda10ValidRecordType benchmarks validating Addenda10 recordType +func BenchmarkAddenda10ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10ValidRecordType(b) + } +} + +// testAddenda10ValidTypeCode validates Addenda10 TypeCode +func testAddenda10ValidTypeCode(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.typeCode = "65" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda10ValidTypeCode tests validating Addenda10 TypeCode +func TestAddenda10ValidTypeCode(t *testing.T) { + testAddenda10ValidTypeCode(t) +} + +// BenchmarkAddenda10ValidTypeCode benchmarks validating Addenda10 TypeCode +func BenchmarkAddenda10ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10ValidTypeCode(b) + } +} + +// testAddenda10TypeCode10 TypeCode is 10 if typeCode is a valid TypeCode +func testAddenda10TypeCode10(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.typeCode = "05" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda10TypeCode10 tests TypeCode is 10 if typeCode is a valid TypeCode +func TestAddenda10TypeCode10(t *testing.T) { + testAddenda10TypeCode10(t) +} + +// BenchmarkAddenda10TypeCode10 benchmarks TypeCode is 10 if typeCode is a valid TypeCode +func BenchmarkAddenda10TypeCode10(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10TypeCode10(b) + } +} + +// testAddenda10FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda10FieldInclusionRecordType(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.recordType = "" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda10FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda10FieldInclusionRecordType(t *testing.T) { + testAddenda10FieldInclusionRecordType(t) +} + +// BenchmarkAddenda10FieldInclusionRecordType benchmarks validating recordType fieldInclusion +func BenchmarkAddenda10FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10FieldInclusionRecordType(b) + } +} + +// testAddenda10FieldInclusionRecordType validates TypeCode fieldInclusion +func testAddenda10FieldInclusionTypeCode(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.typeCode = "" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda10FieldInclusionRecordType tests validating TypeCode fieldInclusion +func TestAddenda10FieldInclusionTypeCode(t *testing.T) { + testAddenda10FieldInclusionTypeCode(t) +} + +// BenchmarkAddenda10FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda10FieldInclusionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10FieldInclusionTypeCode(b) + } +} diff --git a/validators.go b/validators.go index 5efe892b2..382be9931 100644 --- a/validators.go +++ b/validators.go @@ -34,7 +34,7 @@ var ( // IAT msgForeignExchangeIndicator = "is an invalid Foreign Exchange Indicator" msgForeignExchangeReferenceIndicator = "is an invalid Foreign Exchange Reference Indicator" - msgAddenda10TransactionTypeCode = "is an invalid Addenda10 Transaction Type Code" + msgTransactionTypeCode = "is an invalid Addenda10 Transaction Type Code" ) // validator is common validation and formatting of golang types to ach type strings @@ -377,7 +377,7 @@ func (v *validator) isTransactionTypeCode(s string) error { "ARC", "BOC", "POP", "RCK": return nil } - return errors.New(msgAddenda10TransactionTypeCode) + return errors.New(msgTransactionTypeCode) } // isUpperAlphanumeric checks if string only contains ASCII alphanumeric upper case characters From d60b70eb1f0290a7282fc0d85f0c02c697119278 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 28 Jun 2018 13:16:50 -0400 Subject: [PATCH 0277/1694] #211 Addenda10 code coverage tests #211 Addenda10 code coverage tests --- addenda02_test.go | 6 +++--- addenda10.go | 6 +++--- addenda10_test.go | 31 ++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/addenda02_test.go b/addenda02_test.go index c174f126a..476418131 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -323,7 +323,7 @@ func BenchmarkAddenda02TerminalState(b *testing.B) { } } -// TestAddenda02 String validates that a known parsed file can be return to a string of the same value +// TestAddenda02String validates that a known parsed Addenda02 record can be return to a string of the same value func testAddenda02String(t testing.TB) { addenda02 := NewAddenda02() var line = "702REFONEAREFTERM021000490612123456Target Store 0049 PHILADELPHIA PA121042880000123" @@ -336,12 +336,12 @@ func testAddenda02String(t testing.TB) { } } -// TestAddenda02String tests validating that a known parsed file can be return to a string of the same value +// TestAddenda02String tests validating that a known parsed Addenda02 record can be return to a string of the same value func TestAddenda02String(t *testing.T) { testAddenda02String(t) } -// BenchmarkAddenda02String benchmarks validating that a known parsed file can be return to a string of the same value +// BenchmarkAddenda02String benchmarks validating that a known parsed Addenda02 record can be return to a string of the same value func BenchmarkAddenda02String(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/addenda10.go b/addenda10.go index e37697451..e34d1cef8 100644 --- a/addenda10.go +++ b/addenda10.go @@ -66,7 +66,7 @@ func (addenda10 *Addenda10) Parse(record string) { // 25-46 Insert blanks or zeros addenda10.ForeignTraceNumber = addenda10.parseStringField(record[24:46]) // 47-81 Receiving Company Name/Individual Name - addenda10.Name = record[47:81] + addenda10.Name = record[46:81] // 82-87 reserved - Leave blank addenda10.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record @@ -149,12 +149,12 @@ func (addenda10 *Addenda10) ForeignPaymentAmountField() string { // ForeignTraceNumberField gets the Foreign TraceNumber left padded func (addenda10 *Addenda10) ForeignTraceNumberField() string { - return addenda10.alphaField(addenda10.ForeignTraceNumber, 35) + return addenda10.alphaField(addenda10.ForeignTraceNumber, 22) } // NameField gets th name field - Receiving Company Name/Individual Name left padded func (addenda10 *Addenda10) NameField() string { - return addenda10.alphaField(addenda10.Name, 22) + return addenda10.alphaField(addenda10.Name, 35) } // reservedField gets reserved - blank space diff --git a/addenda10_test.go b/addenda10_test.go index fa177aef3..7905be81f 100644 --- a/addenda10_test.go +++ b/addenda10_test.go @@ -4,7 +4,9 @@ package ach -import "testing" +import ( + "testing" +) func mockAddenda10() *Addenda10 { addenda10 := NewAddenda10() @@ -159,3 +161,30 @@ func BenchmarkAddenda10FieldInclusionTypeCode(b *testing.B) { testAddenda10FieldInclusionTypeCode(b) } } + +// TestAddenda10String validates that a known parsed Addenda10 record can be return to a string of the same value +func testAddenda10String(t testing.TB) { + addenda10 := NewAddenda10() + var line = "710ANN000000000000100000928383-23938 BEK Enterprises 0000001" + addenda10.Parse(line) + + if addenda10.String() != line { + t.Errorf("Strings do not match") + } + if addenda10.TypeCode() != "10" { + t.Errorf("TypeCode Expected 10 got: %v", addenda10.TypeCode()) + } +} + +// TestAddenda10String tests validating that a known parsed Addenda10 record can be return to a string of the same value +func TestAddenda10String(t *testing.T) { + testAddenda10String(t) +} + +// BenchmarkAddenda10String benchmarks validating that a known parsed Addenda10 record can be return to a string of the same value +func BenchmarkAddenda10String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10String(b) + } +} From 4a3affba6aab7b42acdc6e4548106dae4903d71a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 28 Jun 2018 13:52:09 -0400 Subject: [PATCH 0278/1694] #211 Addenda10 code coverage #211 Addenda10 code coverage --- addenda02_test.go | 1 + addenda10.go | 4 + addenda10_test.go | 189 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) diff --git a/addenda02_test.go b/addenda02_test.go index 476418131..26bcb2373 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -8,6 +8,7 @@ import ( "testing" ) +// mockAddenda02 creates a mock Addenda02 record func mockAddenda02() *Addenda02 { addenda02 := NewAddenda02() addenda02.ReferenceInformationOne = "REFONEA" diff --git a/addenda10.go b/addenda10.go index e34d1cef8..9f5f57109 100644 --- a/addenda10.go +++ b/addenda10.go @@ -100,6 +100,10 @@ func (addenda10 *Addenda10) Validate() error { if err := addenda10.isTypeCode(addenda10.typeCode); err != nil { return &FieldError{FieldName: "TypeCode", Value: addenda10.typeCode, Msg: err.Error()} } + // Type Code must be 10 + if addenda10.typeCode != "10" { + return &FieldError{FieldName: "TypeCode", Value: addenda10.typeCode, Msg: msgAddendaTypeCode} + } if err := addenda10.isTransactionTypeCode(addenda10.TransactionTypeCode); err != nil { return &FieldError{FieldName: "TransactionTypeCode", Value: addenda10.TransactionTypeCode, Msg: err.Error()} } diff --git a/addenda10_test.go b/addenda10_test.go index 7905be81f..1d16e179b 100644 --- a/addenda10_test.go +++ b/addenda10_test.go @@ -8,6 +8,7 @@ import ( "testing" ) +// mockAddenda10() creates a mock Addenda10 record func mockAddenda10() *Addenda10 { addenda10 := NewAddenda10() addenda10.TransactionTypeCode = "ANN" @@ -110,6 +111,86 @@ func BenchmarkAddenda10TypeCode10(b *testing.B) { } } +// testAddenda10TransactionTypeCode validates TransactionTypeCode +func testAddenda10TransactionTypeCode(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.TransactionTypeCode = "ABC" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TransactionTypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda10TransactionTypeCode tests validating TransactionTypeCode +func TestAddenda10TransactionTypeCode(t *testing.T) { + testAddenda10TransactionTypeCode(t) +} + +// BenchmarkAddenda10TransactionTypeCode benchmarks validating TransactionTypeCode +func BenchmarkAddenda10TransactionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10TransactionTypeCode(b) + } +} + +// testForeignTraceNumberAlphaNumeric validates ForeignTraceNumber is alphanumeric +func testForeignTraceNumberAlphaNumeric(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.ForeignTraceNumber = "®" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignTraceNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestForeignTraceNumberAlphaNumeric tests validating ForeignTraceNumber is alphanumeric +func TestForeignTraceNumberAlphaNumeric(t *testing.T) { + testForeignTraceNumberAlphaNumeric(t) +} + +// BenchmarkForeignTraceNumberAlphaNumeric benchmarks validating ForeignTraceNumber is alphanumeric +func BenchmarkForeignTraceNumberAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testForeignTraceNumberAlphaNumeric(b) + } +} + +// testNameAlphaNumeric validates Name is alphanumeric +func testNameAlphaNumeric(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.Name = "Jas®n" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "Name" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestNameAlphaNumeric tests validating Name is alphanumeric +func TestNameAlphaNumeric(t *testing.T) { + testNameAlphaNumeric(t) +} + +// BenchmarkNameAlphaNumeric benchmarks validating Name is alphanumeric +func BenchmarkNameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testNameAlphaNumeric(b) + } +} + // testAddenda10FieldInclusionRecordType validates recordType fieldInclusion func testAddenda10FieldInclusionRecordType(t testing.TB) { addenda10 := mockAddenda10() @@ -162,6 +243,114 @@ func BenchmarkAddenda10FieldInclusionTypeCode(b *testing.B) { } } +// testAddenda10FieldInclusionTransactionTypeCode validates TransactionTypeCode fieldInclusion +func testAddenda10FieldInclusionTransactionTypeCode(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.TransactionTypeCode = "" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldRequired { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda10FieldInclusionTransactionTypeCode tests validating +// TransactionTypeCode fieldInclusion +func TestAddenda10FieldInclusionTransactionTypeCode(t *testing.T) { + testAddenda10FieldInclusionTransactionTypeCode(t) +} + +// BenchmarkAddenda10FieldInclusionTransactionTypeCode benchmarks validating +// TransactionTypeCode fieldInclusion +func BenchmarkAddenda10FieldInclusionTransactionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10FieldInclusionTransactionTypeCode(b) + } +} + +// testAddenda10FieldInclusionForeignPaymentAmount validates ForeignPaymentAmount fieldInclusion +func testAddenda10FieldInclusionForeignPaymentAmount(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.ForeignPaymentAmount = 0 + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldRequired { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda10FieldInclusionForeignPaymentAmount tests validating ForeignPaymentAmount fieldInclusion +func TestAddenda10FieldInclusionForeignPaymentAmount(t *testing.T) { + testAddenda10FieldInclusionForeignPaymentAmount(t) +} + +// BenchmarkAddenda10FieldInclusionForeignPaymentAmount benchmarks validating ForeignPaymentAmount fieldInclusion +func BenchmarkAddenda10FieldInclusionForeignPaymentAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10FieldInclusionForeignPaymentAmount(b) + } +} + +// testAddenda10FieldInclusionName validates Name fieldInclusion +func testAddenda10FieldInclusionName(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.Name = "" + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldRequired { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda10FieldInclusionName tests validating Name fieldInclusion +func TestAddenda10FieldInclusionName(t *testing.T) { + testAddenda10FieldInclusionName(t) +} + +// BenchmarkAddenda10FieldInclusionName benchmarks validating Name fieldInclusion +func BenchmarkAddenda10FieldInclusionName(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10FieldInclusionName(b) + } +} + +// testAddenda10FieldInclusionEntryDetailSequenceNumber validates EntryDetailSequenceNumber fieldInclusion +func testAddenda10FieldInclusionEntryDetailSequenceNumber(t testing.TB) { + addenda10 := mockAddenda10() + addenda10.EntryDetailSequenceNumber = 0 + if err := addenda10.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda10FieldInclusionEntryDetailSequenceNumber tests validating +// EntryDetailSequenceNumber fieldInclusion +func TestAddenda10FieldInclusionEntryDetailSequenceNumber(t *testing.T) { + testAddenda10FieldInclusionEntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda10FieldInclusionEntryDetailSequenceNumber benchmarks validating +// EntryDetailSequenceNumber fieldInclusion +func BenchmarkAddenda10FieldInclusionEntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10FieldInclusionEntryDetailSequenceNumber(b) + } +} + // TestAddenda10String validates that a known parsed Addenda10 record can be return to a string of the same value func testAddenda10String(t testing.TB) { addenda10 := NewAddenda10() From 6c7b5683e133471779d77c4f827539d116238c0d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 28 Jun 2018 14:07:09 -0400 Subject: [PATCH 0279/1694] #211 Test review modifications #211 Test review modifications --- addenda10.go | 9 ++++----- addenda10_test.go | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/addenda10.go b/addenda10.go index 9f5f57109..055f388ef 100644 --- a/addenda10.go +++ b/addenda10.go @@ -107,7 +107,7 @@ func (addenda10 *Addenda10) Validate() error { if err := addenda10.isTransactionTypeCode(addenda10.TransactionTypeCode); err != nil { return &FieldError{FieldName: "TransactionTypeCode", Value: addenda10.TransactionTypeCode, Msg: err.Error()} } - // ToDo: Foreign Exchange Amount + // ToDo: Foreign Exchange Amount blank ? if err := addenda10.isAlphanumeric(addenda10.ForeignTraceNumber); err != nil { return &FieldError{FieldName: "ForeignTraceNumber", Value: addenda10.ForeignTraceNumber, Msg: err.Error()} } @@ -135,7 +135,7 @@ func (addenda10 *Addenda10) fieldInclusion() error { Value: strconv.Itoa(addenda10.ForeignPaymentAmount), Msg: msgFieldRequired} } if addenda10.Name == "" { - return &FieldError{FieldName: "Name", Value: addenda10.Name, Msg: msgFieldRequired} + return &FieldError{FieldName: "Name", Value: addenda10.Name, Msg: msgFieldInclusion} } if addenda10.EntryDetailSequenceNumber == 0 { return &FieldError{FieldName: "EntryDetailSequenceNumber", @@ -145,8 +145,7 @@ func (addenda10 *Addenda10) fieldInclusion() error { } // ForeignPaymentAmountField returns ForeignPaymentAmount zero padded -// Payment Amount For inbound IAT payments this field should contain the USD amount or may be blank. -// ToDo: Review/Add logic for blank +// ToDo: Review/Add logic for blank ? func (addenda10 *Addenda10) ForeignPaymentAmountField() string { return addenda10.numericField(addenda10.ForeignPaymentAmount, 18) } @@ -156,7 +155,7 @@ func (addenda10 *Addenda10) ForeignTraceNumberField() string { return addenda10.alphaField(addenda10.ForeignTraceNumber, 22) } -// NameField gets th name field - Receiving Company Name/Individual Name left padded +// NameField gets the name field - Receiving Company Name/Individual Name left padded func (addenda10 *Addenda10) NameField() string { return addenda10.alphaField(addenda10.Name, 35) } diff --git a/addenda10_test.go b/addenda10_test.go index 1d16e179b..20e719e49 100644 --- a/addenda10_test.go +++ b/addenda10_test.go @@ -303,7 +303,7 @@ func testAddenda10FieldInclusionName(t testing.TB) { addenda10.Name = "" if err := addenda10.Validate(); err != nil { if e, ok := err.(*FieldError); ok { - if e.Msg != msgFieldRequired { + if e.Msg != msgFieldInclusion { t.Errorf("%T: %s", err, err) } } From ee974c3581a632f4b87773fc40cdeb031042fbe8 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 28 Jun 2018 16:59:23 -0400 Subject: [PATCH 0280/1694] #211 addenda11 and addenda12 #211 addenda11 and addenda12 --- README.md | 10 +- addenda10.go | 6 +- addenda11.go | 150 +++++++++++++++++++++ addenda11_test.go | 321 +++++++++++++++++++++++++++++++++++++++++++++ addenda12.go | 158 ++++++++++++++++++++++ addenda12_test.go | 327 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 967 insertions(+), 5 deletions(-) create mode 100644 addenda11.go create mode 100644 addenda11_test.go create mode 100644 addenda12.go create mode 100644 addenda12_test.go diff --git a/README.md b/README.md index 4c14fcdc6..32bbcbcaf 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,12 @@ ACH is under active development but already in production for multiple companies * Return Entries * Addenda Type Code 02 * Addenda Type Code 05 - * Addenda Type Code 98 - * Addenda Type Code 99 - - + * Addenda Type Code 10 (IAT) + * Addenda Type Code 11 (IAT) + * Addenda Type Code 12 (IAT) + * Addenda Type Code 98 (NOC) + * Addenda Type Code 99 (Return) + ## Project Roadmap * Additional SEC codes will be added based on library users needs. Please open an issue with a valid test file. * Review the project issues for more detailed information diff --git a/addenda10.go b/addenda10.go index 055f388ef..c220caa21 100644 --- a/addenda10.go +++ b/addenda10.go @@ -11,7 +11,11 @@ import ( // Addenda10 is a Addendumer addenda which provides business transaction information for Addenda Type // Code 10 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. -// It is mandatory for IAT entries +// +// Addenda10 is mandatory for IAT entries +// +// The First Addenda Record identifies the Receiver of the transaction and the dollar amount of +// the payment. type Addenda10 struct { // ID is a client defined string used as a reference to this record. ID string `json:"id"` diff --git a/addenda11.go b/addenda11.go new file mode 100644 index 000000000..81daa9277 --- /dev/null +++ b/addenda11.go @@ -0,0 +1,150 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// Addenda11 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 11 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// +// Addenda11 is mandatory for IAT entries +// +// The Addenda11 record identifies key information related to the Originator of +// the entry. +type Addenda11 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. + recordType string + // TypeCode Addenda11 types code '11' + typeCode string + // Originator Name contains the originators name (your company name / name) + OriginatorName string `json:"originatorName"` + // Originator Street Address Contains the originators street address (your company's address / your address) + OriginatorStreetAddress string `json:"originatorStreetAddress"` + // reserved - Leave blank + reserved string + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda11 returns a new Addenda11 with default values for none exported fields +func NewAddenda11() *Addenda11 { + addenda11 := new(Addenda11) + addenda11.recordType = "7" + addenda11.typeCode = "11" + return addenda11 +} + +// Parse takes the input record string and parses the Addenda11 values +func (addenda11 *Addenda11) Parse(record string) { + // 1-1 Always "7" + addenda11.recordType = "7" + // 2-3 Always 11 + addenda11.typeCode = record[1:3] + // 4-38 + addenda11.OriginatorName = record[3:38] + // 38-73 + addenda11.OriginatorStreetAddress = record[38:73] + // 74-87 reserved - Leave blank + addenda11.reserved = " " + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda11.EntryDetailSequenceNumber = addenda11.parseNumField(record[87:94]) +} + +// String writes the Addenda11 struct to a 94 character string. +func (addenda11 *Addenda11) String() string { + return fmt.Sprintf("%v%v%v%v%v%v", + addenda11.recordType, + addenda11.typeCode, + addenda11.OriginatorNameField(), + addenda11.OriginatorStreetAddressField(), + addenda11.reservedField(), + addenda11.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda11 *Addenda11) Validate() error { + if err := addenda11.fieldInclusion(); err != nil { + return err + } + if addenda11.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda11.recordType, Msg: msg} + } + if err := addenda11.isTypeCode(addenda11.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda11.typeCode, Msg: err.Error()} + } + // Type Code must be 11 + if addenda11.typeCode != "11" { + return &FieldError{FieldName: "TypeCode", Value: addenda11.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda11.isAlphanumeric(addenda11.OriginatorName); err != nil { + return &FieldError{FieldName: "OriginatorName", Value: addenda11.OriginatorName, Msg: err.Error()} + } + if err := addenda11.isAlphanumeric(addenda11.OriginatorStreetAddress); err != nil { + return &FieldError{FieldName: "OriginatorStreetAddress", Value: addenda11.OriginatorStreetAddress, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda11 *Addenda11) fieldInclusion() error { + if addenda11.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda11.recordType, Msg: msgFieldInclusion} + } + if addenda11.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda11.typeCode, Msg: msgFieldInclusion} + } + if addenda11.OriginatorName == "" { + return &FieldError{FieldName: "OriginatorName", + Value: addenda11.OriginatorName, Msg: msgFieldInclusion} + } + if addenda11.OriginatorStreetAddress == "" { + return &FieldError{FieldName: "OriginatorStreetAddress", + Value: addenda11.OriginatorStreetAddress, Msg: msgFieldInclusion} + } + if addenda11.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", + Value: addenda11.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// OriginatorNameField gets the OriginatorName field - Originator Company Name/Individual Name left padded +func (addenda11 *Addenda11) OriginatorNameField() string { + return addenda11.alphaField(addenda11.OriginatorName, 35) +} + +// OriginatorStreetAddressField gets the OriginatorStreetAddress field - Originator Street Address left padded +func (addenda11 *Addenda11) OriginatorStreetAddressField() string { + return addenda11.alphaField(addenda11.OriginatorStreetAddress, 35) +} + +// reservedField gets reserved - blank space +func (addenda11 *Addenda11) reservedField() string { + return addenda11.alphaField(addenda11.reserved, 14) +} + +// TypeCode Defines the specific explanation and format for the addenda11 information left padded +func (addenda11 *Addenda11) TypeCode() string { + return addenda11.typeCode +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda11 *Addenda11) EntryDetailSequenceNumberField() string { + return addenda11.numericField(addenda11.EntryDetailSequenceNumber, 7) +} diff --git a/addenda11_test.go b/addenda11_test.go new file mode 100644 index 000000000..cb2b26cf5 --- /dev/null +++ b/addenda11_test.go @@ -0,0 +1,321 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda11() creates a mock Addenda11 record +func mockAddenda11() *Addenda11 { + addenda11 := NewAddenda11() + addenda11.OriginatorName = "BEK Solutions" + addenda11.OriginatorStreetAddress = "15 West Place Street" + addenda11.EntryDetailSequenceNumber = 00000001 + return addenda11 +} + +// TestMockAddenda11 validates mockAddenda11 +func TestMockAddenda11(t *testing.T) { + addenda11 := mockAddenda11() + if err := addenda11.Validate(); err != nil { + t.Error("mockAddenda11 does not validate and will break other tests") + } +} + +// testAddenda11ValidRecordType validates Addenda11 recordType +func testAddenda11ValidRecordType(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.recordType = "63" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda11ValidRecordType tests validating Addenda11 recordType +func TestAddenda11ValidRecordType(t *testing.T) { + testAddenda11ValidRecordType(t) +} + +// BenchmarkAddenda11ValidRecordType benchmarks validating Addenda11 recordType +func BenchmarkAddenda11ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11ValidRecordType(b) + } +} + +// testAddenda11ValidTypeCode validates Addenda11 TypeCode +func testAddenda11ValidTypeCode(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.typeCode = "65" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda11ValidTypeCode tests validating Addenda11 TypeCode +func TestAddenda11ValidTypeCode(t *testing.T) { + testAddenda11ValidTypeCode(t) +} + +// BenchmarkAddenda11ValidTypeCode benchmarks validating Addenda11 TypeCode +func BenchmarkAddenda11ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11ValidTypeCode(b) + } +} + +// testAddenda11TypeCode11 TypeCode is 11 if typeCode is a valid TypeCode +func testAddenda11TypeCode11(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.typeCode = "05" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda11TypeCode11 tests TypeCode is 11 if typeCode is a valid TypeCode +func TestAddenda11TypeCode11(t *testing.T) { + testAddenda11TypeCode11(t) +} + +// BenchmarkAddenda11TypeCode11 benchmarks TypeCode is 11 if typeCode is a valid TypeCode +func BenchmarkAddenda11TypeCode11(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11TypeCode11(b) + } +} + +// testOriginatorNameAlphaNumeric validates OriginatorName is alphanumeric +func testOriginatorNameAlphaNumeric(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.OriginatorName = "BEK S®lutions" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "OriginatorName" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestNameOriginatorAlphaNumeric tests validating OriginatorName is alphanumeric +func TestOriginatorNameAlphaNumeric(t *testing.T) { + testOriginatorNameAlphaNumeric(t) +} + +// BenchmarkOriginatorNameAlphaNumeric benchmarks validating OriginatorName is alphanumeric +func BenchmarkOriginatorNameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testOriginatorNameAlphaNumeric(b) + } +} + +// testOriginatorStreetAddressAlphaNumeric validates OriginatorStreetAddress is alphanumeric +func testOriginatorStreetAddressAlphaNumeric(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.OriginatorStreetAddress = "15 W®st" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "OriginatorStreetAddress" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestNameOriginatorAlphaNumeric tests validating OriginatorStreetAddress is alphanumeric +func TestOriginatorStreetAddressAlphaNumeric(t *testing.T) { + testOriginatorStreetAddressAlphaNumeric(t) +} + +// BenchmarkOriginatorStreetAddressAlphaNumeric benchmarks validating OriginatorStreetAddress is alphanumeric +func BenchmarkOriginatorStreetAddressAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testOriginatorStreetAddressAlphaNumeric(b) + } +} + +// testAddenda11FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda11FieldInclusionRecordType(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.recordType = "" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda11FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda11FieldInclusionRecordType(t *testing.T) { + testAddenda11FieldInclusionRecordType(t) +} + +// BenchmarkAddenda11FieldInclusionRecordType benchmarks validating recordType fieldInclusion +func BenchmarkAddenda11FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11FieldInclusionRecordType(b) + } +} + +// testAddenda11FieldInclusionRecordType validates TypeCode fieldInclusion +func testAddenda11FieldInclusionTypeCode(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.typeCode = "" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda11FieldInclusionRecordType tests validating TypeCode fieldInclusion +func TestAddenda11FieldInclusionTypeCode(t *testing.T) { + testAddenda11FieldInclusionTypeCode(t) +} + +// BenchmarkAddenda11FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda11FieldInclusionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11FieldInclusionTypeCode(b) + } +} + +// testAddenda11FieldInclusionOriginatorName validates OriginatorName fieldInclusion +func testAddenda11FieldInclusionOriginatorName(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.OriginatorName = "" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda11FieldInclusionOriginatorName tests validating OriginatorName fieldInclusion +func TestAddenda11FieldInclusionOriginatorName(t *testing.T) { + testAddenda11FieldInclusionOriginatorName(t) +} + +// BenchmarkAddenda11FieldInclusionOriginatorName benchmarks validating OriginatorName fieldInclusion +func BenchmarkAddenda11FieldInclusionOriginatorName(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11FieldInclusionOriginatorName(b) + } +} + +// testAddenda11FieldInclusionOriginatorStreetAddress validates OriginatorStreetAddress fieldInclusion +func testAddenda11FieldInclusionOriginatorStreetAddress(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.OriginatorStreetAddress = "" + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda11FieldInclusionOriginatorStreetAddress tests validating OriginatorStreetAddress fieldInclusion +func TestAddenda11FieldInclusionOriginatorStreetAddress(t *testing.T) { + testAddenda11FieldInclusionOriginatorStreetAddress(t) +} + +// BenchmarkAddenda11FieldInclusionOriginatorStreetAddress benchmarks validating OriginatorStreetAddress fieldInclusion +func BenchmarkAddenda11FieldInclusionOriginatorStreetAddress(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11FieldInclusionOriginatorStreetAddress(b) + } +} + +// testAddenda11FieldInclusionEntryDetailSequenceNumber validates EntryDetailSequenceNumber fieldInclusion +func testAddenda11FieldInclusionEntryDetailSequenceNumber(t testing.TB) { + addenda11 := mockAddenda11() + addenda11.EntryDetailSequenceNumber = 0 + if err := addenda11.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda11FieldInclusionEntryDetailSequenceNumber tests validating +// EntryDetailSequenceNumber fieldInclusion +func TestAddenda11FieldInclusionEntryDetailSequenceNumber(t *testing.T) { + testAddenda11FieldInclusionEntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda11FieldInclusionEntryDetailSequenceNumber benchmarks validating +// EntryDetailSequenceNumber fieldInclusion +func BenchmarkAddenda11FieldInclusionEntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11FieldInclusionEntryDetailSequenceNumber(b) + } +} + +// TestAddenda11String validates that a known parsed Addenda11 record can be return to a string of the same value +func testAddenda11String(t testing.TB) { + addenda11 := NewAddenda11() + var line = "711BEK Solutions 15 West Place Street 0000001" + addenda11.Parse(line) + + if addenda11.String() != line { + t.Errorf("Strings do not match") + } + if addenda11.TypeCode() != "11" { + t.Errorf("TypeCode Expected 11 got: %v", addenda11.TypeCode()) + } +} + +// TestAddenda11String tests validating that a known parsed Addenda11 record can be return to a string of the same value +func TestAddenda11String(t *testing.T) { + testAddenda11String(t) +} + +// BenchmarkAddenda11String benchmarks validating that a known parsed Addenda11 record can be return to a string of the same value +func BenchmarkAddenda11String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11String(b) + } +} diff --git a/addenda12.go b/addenda12.go new file mode 100644 index 000000000..c591faf20 --- /dev/null +++ b/addenda12.go @@ -0,0 +1,158 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// Addenda12 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 12 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// +// Addenda12 is mandatory for IAT entries +// +// The Addenda12 record identifies key information related to the Originator of +// the entry. +type Addenda12 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. + recordType string + // TypeCode Addenda12 types code '12' + typeCode string + // Originator City & State / Province + // Data elements City and State / Province should be separated with an asterisk (*) as a delimiter + // and the field should end with a backslash (\). + // For example: San FranciscoCA. + OriginatorCityStateProvince string `json:"originatorCityStateProvince"` + // Originator Country & Postal Code + // Data elements must be separated by an asterisk (*) and must end with a backslash (\) + // For example: US10036\ + OriginatorCountryPostalCode string `json:"originatorCountryPostalCode"` + // reserved - Leave blank + reserved string + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda12 returns a new Addenda12 with default values for none exported fields +func NewAddenda12() *Addenda12 { + addenda12 := new(Addenda12) + addenda12.recordType = "7" + addenda12.typeCode = "12" + return addenda12 +} + +// Parse takes the input record string and parses the Addenda12 values +func (addenda12 *Addenda12) Parse(record string) { + // 1-1 Always "7" + addenda12.recordType = "7" + // 2-3 Always 12 + addenda12.typeCode = record[1:3] + // 4-38 + addenda12.OriginatorCityStateProvince = record[3:38] + // 38-73 + addenda12.OriginatorCountryPostalCode = record[38:73] + // 74-87 reserved - Leave blank + addenda12.reserved = " " + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda12.EntryDetailSequenceNumber = addenda12.parseNumField(record[87:94]) +} + +// String writes the Addenda12 struct to a 94 character string. +func (addenda12 *Addenda12) String() string { + return fmt.Sprintf("%v%v%v%v%v%v", + addenda12.recordType, + addenda12.typeCode, + addenda12.OriginatorCityStateProvinceField(), + // ToDo Validator for backslash + addenda12.OriginatorCountryPostalCodeField(), + addenda12.reservedField(), + addenda12.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda12 *Addenda12) Validate() error { + if err := addenda12.fieldInclusion(); err != nil { + return err + } + if addenda12.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda12.recordType, Msg: msg} + } + if err := addenda12.isTypeCode(addenda12.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda12.typeCode, Msg: err.Error()} + } + // Type Code must be 12 + if addenda12.typeCode != "12" { + return &FieldError{FieldName: "TypeCode", Value: addenda12.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda12.isAlphanumeric(addenda12.OriginatorCityStateProvince); err != nil { + return &FieldError{FieldName: "OriginatorCityStateProvince", + Value: addenda12.OriginatorCityStateProvince, Msg: err.Error()} + } + if err := addenda12.isAlphanumeric(addenda12.OriginatorCountryPostalCode); err != nil { + return &FieldError{FieldName: "OriginatorCountryPostalCode", + Value: addenda12.OriginatorCountryPostalCode, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda12 *Addenda12) fieldInclusion() error { + if addenda12.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda12.recordType, Msg: msgFieldInclusion} + } + if addenda12.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda12.typeCode, Msg: msgFieldInclusion} + } + if addenda12.OriginatorCityStateProvince == "" { + return &FieldError{FieldName: "OriginatorCityStateProvince", + Value: addenda12.OriginatorCityStateProvince, Msg: msgFieldInclusion} + } + if addenda12.OriginatorCountryPostalCode == "" { + return &FieldError{FieldName: "OriginatorCountryPostalCode", + Value: addenda12.OriginatorCountryPostalCode, Msg: msgFieldInclusion} + } + if addenda12.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", + Value: addenda12.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// OriginatorCityStateProvinceField gets the OriginatorCityStateProvinceField left padded +func (addenda12 *Addenda12) OriginatorCityStateProvinceField() string { + return addenda12.alphaField(addenda12.OriginatorCityStateProvince, 35) +} + +// OriginatorCountryPostalCodeField gets the OriginatorCountryPostalCode field left padded +func (addenda12 *Addenda12) OriginatorCountryPostalCodeField() string { + return addenda12.alphaField(addenda12.OriginatorCountryPostalCode, 35) +} + +// reservedField gets reserved - blank space +func (addenda12 *Addenda12) reservedField() string { + return addenda12.alphaField(addenda12.reserved, 14) +} + +// TypeCode Defines the specific explanation and format for the addenda12 information left padded +func (addenda12 *Addenda12) TypeCode() string { + return addenda12.typeCode +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda12 *Addenda12) EntryDetailSequenceNumberField() string { + return addenda12.numericField(addenda12.EntryDetailSequenceNumber, 7) +} diff --git a/addenda12_test.go b/addenda12_test.go new file mode 100644 index 000000000..207c60e20 --- /dev/null +++ b/addenda12_test.go @@ -0,0 +1,327 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda12() creates a mock Addenda12 record +func mockAddenda12() *Addenda12 { + addenda12 := NewAddenda12() + addenda12.OriginatorCityStateProvince = "JacobsTown*PA\\" + addenda12.OriginatorCountryPostalCode = "US19305\\" + addenda12.EntryDetailSequenceNumber = 00000001 + return addenda12 +} + +// TestMockAddenda12 validates mockAddenda12 +func TestMockAddenda12(t *testing.T) { + addenda12 := mockAddenda12() + if err := addenda12.Validate(); err != nil { + t.Error("mockAddenda12 does not validate and will break other tests") + } +} + +// testAddenda12ValidRecordType validates Addenda12 recordType +func testAddenda12ValidRecordType(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.recordType = "63" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda12ValidRecordType tests validating Addenda12 recordType +func TestAddenda12ValidRecordType(t *testing.T) { + testAddenda12ValidRecordType(t) +} + +// BenchmarkAddenda12ValidRecordType benchmarks validating Addenda12 recordType +func BenchmarkAddenda12ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12ValidRecordType(b) + } +} + +// testAddenda12ValidTypeCode validates Addenda12 TypeCode +func testAddenda12ValidTypeCode(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.typeCode = "65" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda12ValidTypeCode tests validating Addenda12 TypeCode +func TestAddenda12ValidTypeCode(t *testing.T) { + testAddenda12ValidTypeCode(t) +} + +// BenchmarkAddenda12ValidTypeCode benchmarks validating Addenda12 TypeCode +func BenchmarkAddenda12ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12ValidTypeCode(b) + } +} + +// testAddenda12TypeCode12 TypeCode is 12 if typeCode is a valid TypeCode +func testAddenda12TypeCode12(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.typeCode = "05" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda12TypeCode12 tests TypeCode is 12 if typeCode is a valid TypeCode +func TestAddenda12TypeCode12(t *testing.T) { + testAddenda12TypeCode12(t) +} + +// BenchmarkAddenda12TypeCode12 benchmarks TypeCode is 12 if typeCode is a valid TypeCode +func BenchmarkAddenda12TypeCode12(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12TypeCode12(b) + } +} + +// testOriginatorCityStateProvinceAlphaNumeric validates OriginatorCityStateProvince is alphanumeric +func testOriginatorCityStateProvinceAlphaNumeric(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.OriginatorCityStateProvince = "Jacobs®Town*PA" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "OriginatorCityStateProvince" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestNameOriginatorAlphaNumeric tests validating OriginatorCityStateProvince is alphanumeric +func TestOriginatorCityStateProvinceAlphaNumeric(t *testing.T) { + testOriginatorCityStateProvinceAlphaNumeric(t) +} + +// BenchmarkOriginatorCityStateProvinceAlphaNumeric benchmarks validating OriginatorCityStateProvince is alphanumeric +func BenchmarkOriginatorCityStateProvinceAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testOriginatorCityStateProvinceAlphaNumeric(b) + } +} + +// testOriginatorCountryPostalCodeAlphaNumeric validates OriginatorCountryPostalCode is alphanumeric +func testOriginatorCountryPostalCodeAlphaNumeric(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.OriginatorCountryPostalCode = "US19®305" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "OriginatorCountryPostalCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestNameOriginatorAlphaNumeric tests validating OriginatorCountryPostalCode is alphanumeric +func TestOriginatorCountryPostalCodeAlphaNumeric(t *testing.T) { + testOriginatorCountryPostalCodeAlphaNumeric(t) +} + +// BenchmarkOriginatorCountryPostalCodeAlphaNumeric benchmarks validating OriginatorCountryPostalCode is alphanumeric +func BenchmarkOriginatorCountryPostalCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testOriginatorCountryPostalCodeAlphaNumeric(b) + } +} + +// testAddenda12FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda12FieldInclusionRecordType(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.recordType = "" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda12FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda12FieldInclusionRecordType(t *testing.T) { + testAddenda12FieldInclusionRecordType(t) +} + +// BenchmarkAddenda12FieldInclusionRecordType benchmarks validating recordType fieldInclusion +func BenchmarkAddenda12FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12FieldInclusionRecordType(b) + } +} + +// testAddenda12FieldInclusionRecordType validates TypeCode fieldInclusion +func testAddenda12FieldInclusionTypeCode(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.typeCode = "" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda12FieldInclusionRecordType tests validating TypeCode fieldInclusion +func TestAddenda12FieldInclusionTypeCode(t *testing.T) { + testAddenda12FieldInclusionTypeCode(t) +} + +// BenchmarkAddenda12FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda12FieldInclusionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12FieldInclusionTypeCode(b) + } +} + +// testAddenda12FieldInclusionOriginatorCityStateProvince validates OriginatorCityStateProvince fieldInclusion +func testAddenda12FieldInclusionOriginatorCityStateProvince(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.OriginatorCityStateProvince = "" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda12FieldInclusionOriginatorCityStateProvince tests validating OriginatorCityStateProvince fieldInclusion +func TestAddenda12FieldInclusionOriginatorCityStateProvince(t *testing.T) { + testAddenda12FieldInclusionOriginatorCityStateProvince(t) +} + +// BenchmarkAddenda12FieldInclusionOriginatorCityStateProvince benchmarks validating OriginatorCityStateProvince fieldInclusion +func BenchmarkAddenda12FieldInclusionOriginatorCityStateProvince(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12FieldInclusionOriginatorCityStateProvince(b) + } +} + +// testAddenda12FieldInclusionOriginatorCountryPostalCode validates OriginatorCountryPostalCode fieldInclusion +func testAddenda12FieldInclusionOriginatorCountryPostalCode(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.OriginatorCountryPostalCode = "" + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda12FieldInclusionOriginatorCountryPostalCode tests validating OriginatorCountryPostalCode fieldInclusion +func TestAddenda12FieldInclusionOriginatorCountryPostalCode(t *testing.T) { + testAddenda12FieldInclusionOriginatorCountryPostalCode(t) +} + +// BenchmarkAddenda12FieldInclusionOriginatorCountryPostalCode benchmarks validating OriginatorCountryPostalCode fieldInclusion +func BenchmarkAddenda12FieldInclusionOriginatorCountryPostalCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12FieldInclusionOriginatorCountryPostalCode(b) + } +} + +// testAddenda12FieldInclusionEntryDetailSequenceNumber validates EntryDetailSequenceNumber fieldInclusion +func testAddenda12FieldInclusionEntryDetailSequenceNumber(t testing.TB) { + addenda12 := mockAddenda12() + addenda12.EntryDetailSequenceNumber = 0 + if err := addenda12.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda12FieldInclusionEntryDetailSequenceNumber tests validating +// EntryDetailSequenceNumber fieldInclusion +func TestAddenda12FieldInclusionEntryDetailSequenceNumber(t *testing.T) { + testAddenda12FieldInclusionEntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda12FieldInclusionEntryDetailSequenceNumber benchmarks validating +// EntryDetailSequenceNumber fieldInclusion +func BenchmarkAddenda12FieldInclusionEntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12FieldInclusionEntryDetailSequenceNumber(b) + } +} + +// TestAddenda12String validates that a known parsed Addenda12 record can be return to a string of the same value +func testAddenda12String(t testing.TB) { + addenda12 := NewAddenda12() + // Backslash logic + var line = "712" + + "JacobsTown*PA\\ " + + "US19305\\ " + + " " + + "0000001" + + addenda12.Parse(line) + + if addenda12.String() != line { + t.Errorf("Strings do not match") + } + if addenda12.TypeCode() != "12" { + t.Errorf("TypeCode Expected 12 got: %v", addenda12.TypeCode()) + } +} + +// TestAddenda12String tests validating that a known parsed Addenda12 record can be return to a string of the same value +func TestAddenda12String(t *testing.T) { + testAddenda12String(t) +} + +// BenchmarkAddenda12String benchmarks validating that a known parsed Addenda12 record can be return to a string of the same value +func BenchmarkAddenda12String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12String(b) + } +} From 736d89a9d2f3e7ea55bf8c3472bafa98e0c6094a Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 00:28:40 -0400 Subject: [PATCH 0281/1694] #211 Comment Changes #211 Comment Changes --- addenda11_test.go | 4 ++-- addenda12_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addenda11_test.go b/addenda11_test.go index cb2b26cf5..8251ad68e 100644 --- a/addenda11_test.go +++ b/addenda11_test.go @@ -122,7 +122,7 @@ func testOriginatorNameAlphaNumeric(t testing.TB) { } } -// TestNameOriginatorAlphaNumeric tests validating OriginatorName is alphanumeric +// TestOriginatorNameAlphaNumeric tests validating OriginatorName is alphanumeric func TestOriginatorNameAlphaNumeric(t *testing.T) { testOriginatorNameAlphaNumeric(t) } @@ -148,7 +148,7 @@ func testOriginatorStreetAddressAlphaNumeric(t testing.TB) { } } -// TestNameOriginatorAlphaNumeric tests validating OriginatorStreetAddress is alphanumeric +// TestOriginatorStreetAddressAlphaNumeric tests validating OriginatorStreetAddress is alphanumeric func TestOriginatorStreetAddressAlphaNumeric(t *testing.T) { testOriginatorStreetAddressAlphaNumeric(t) } diff --git a/addenda12_test.go b/addenda12_test.go index 207c60e20..10a67cec6 100644 --- a/addenda12_test.go +++ b/addenda12_test.go @@ -122,7 +122,7 @@ func testOriginatorCityStateProvinceAlphaNumeric(t testing.TB) { } } -// TestNameOriginatorAlphaNumeric tests validating OriginatorCityStateProvince is alphanumeric +// TestOriginatorCityStateProvinceAlphaNumeric tests validating OriginatorCityStateProvince is alphanumeric func TestOriginatorCityStateProvinceAlphaNumeric(t *testing.T) { testOriginatorCityStateProvinceAlphaNumeric(t) } @@ -148,7 +148,7 @@ func testOriginatorCountryPostalCodeAlphaNumeric(t testing.TB) { } } -// TestNameOriginatorAlphaNumeric tests validating OriginatorCountryPostalCode is alphanumeric +// TestOriginatorCountryPostalCodeAlphaNumeric tests validating OriginatorCountryPostalCode is alphanumeric func TestOriginatorCountryPostalCodeAlphaNumeric(t *testing.T) { testOriginatorCountryPostalCodeAlphaNumeric(t) } From 0fedc457299ad5b0f66c37c5c4e33fa264e2dfbf Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 13:50:44 -0400 Subject: [PATCH 0282/1694] #211 Addenda 13 and Addenda14 Addenda 13 and Addenda14 --- addenda11.go | 2 +- addenda13.go | 206 ++++++++++++++++++++++ addenda13_test.go | 428 ++++++++++++++++++++++++++++++++++++++++++++++ addenda14.go | 202 ++++++++++++++++++++++ addenda14_test.go | 428 ++++++++++++++++++++++++++++++++++++++++++++++ validators.go | 17 ++ 6 files changed, 1282 insertions(+), 1 deletion(-) create mode 100644 addenda13.go create mode 100644 addenda13_test.go create mode 100644 addenda14.go create mode 100644 addenda14_test.go diff --git a/addenda11.go b/addenda11.go index 81daa9277..1b3335269 100644 --- a/addenda11.go +++ b/addenda11.go @@ -55,7 +55,7 @@ func (addenda11 *Addenda11) Parse(record string) { addenda11.typeCode = record[1:3] // 4-38 addenda11.OriginatorName = record[3:38] - // 38-73 + // 39-73 addenda11.OriginatorStreetAddress = record[38:73] // 74-87 reserved - Leave blank addenda11.reserved = " " diff --git a/addenda13.go b/addenda13.go new file mode 100644 index 000000000..874221db0 --- /dev/null +++ b/addenda13.go @@ -0,0 +1,206 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// Addenda13 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 13 in a machine readable format. It is usually formatted according to ANSI, ASC, X13 Standard. +// +// Addenda13 is mandatory for IAT entries +// +// The Addenda13 contains information related to the financial institution originating the entry. +// For inbound IAT entries, the Fourth Addenda Record must contain information to identify the +// foreign financial institution that is providing the funding and payment instruction for the IAT entry. +type Addenda13 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. + recordType string + // TypeCode Addenda13 types code '13' + typeCode string + // Originating DFI Name + // For Outbound IAT Entries, this field must contain the name of the U.S. ODFI. + // For Inbound IATs: Name of the foreign bank providing funding for the payment transaction + ODFIName string `json:"ODFIName"` + // Originating DFI Identification Number Qualifier + // For Inbound IATs: The 2-digit code that identifies the numbering scheme used in the + // Foreign DFI Identification Number field: + // 01 = National Clearing System + // 02 = BIC Code + // 03 = IBAN Code + ODFIIDNumberQualifier string `json:"ODFIIDNumberQualifier"` + // Originating DFI Identification + // This field contains the routing number that identifies the U.S. ODFI initiating the entry. + // For Inbound IATs: This field contains the bank ID number of the Foreign Bank providing funding + // for the payment transaction. + ODFIIdentification string `json:"ODFIIdentification"` + // Originating DFI Branch Country Code + // USb” = United States + //(“b” indicates a blank space) + // For Inbound IATs: This 3 position field contains a 2-character code as approved by the + // International Organization for Standardization (ISO) used to identify the country in which + // the branch of the bank that originated the entry is located. Values for other countries can + // be found on the International Organization for Standardization website: www.iso.org. + ODFIBranchCountryCode string `json:"ODFIBranchCountryCode"` + // reserved - Leave blank + reserved string + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda13 returns a new Addenda13 with default values for none exported fields +func NewAddenda13() *Addenda13 { + addenda13 := new(Addenda13) + addenda13.recordType = "7" + addenda13.typeCode = "13" + return addenda13 +} + +// Parse takes the input record string and parses the Addenda13 values +func (addenda13 *Addenda13) Parse(record string) { + // 1-1 Always "7" + addenda13.recordType = "7" + // 2-3 Always 13 + addenda13.typeCode = record[1:3] + // 4-38 ODFIName + addenda13.ODFIName = record[3:38] + // 39-40 ODFIIDNumberQualifier + addenda13.ODFIIDNumberQualifier = record[38:40] + // 41-74 ODFIIdentification + addenda13.ODFIIdentification = record[40:74] + // 75-77 + addenda13.ODFIBranchCountryCode = record[74:77] + // 78-87 reserved - Leave blank + addenda13.reserved = " " + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda13.EntryDetailSequenceNumber = addenda13.parseNumField(record[87:94]) +} + +// String writes the Addenda13 struct to a 94 character string. +func (addenda13 *Addenda13) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v", + addenda13.recordType, + addenda13.typeCode, + addenda13.ODFINameField(), + addenda13.ODFIIDNumberQualifierField(), + addenda13.ODFIIdentificationField(), + addenda13.ODFIBranchCountryCodeField(), + addenda13.reservedField(), + addenda13.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda13 *Addenda13) Validate() error { + if err := addenda13.fieldInclusion(); err != nil { + return err + } + if addenda13.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda13.recordType, Msg: msg} + } + if err := addenda13.isTypeCode(addenda13.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda13.typeCode, Msg: err.Error()} + } + // Type Code must be 13 + if addenda13.typeCode != "13" { + return &FieldError{FieldName: "TypeCode", Value: addenda13.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda13.isAlphanumeric(addenda13.ODFIName); err != nil { + return &FieldError{FieldName: "ODFIName", + Value: addenda13.ODFIName, Msg: err.Error()} + } + // Valid ODFI Identification Number Qualifier + if err := addenda13.isIDNumberQualifier(addenda13.ODFIIDNumberQualifier); err != nil { + return &FieldError{FieldName: "ODFIIDNumberQualifier", + Value: addenda13.ODFIIDNumberQualifier, Msg: err.Error()} + } + if err := addenda13.isAlphanumeric(addenda13.ODFIIdentification); err != nil { + return &FieldError{FieldName: "ODFIIdentification", + Value: addenda13.ODFIIdentification, Msg: err.Error()} + } + if err := addenda13.isAlphanumeric(addenda13.ODFIBranchCountryCode); err != nil { + return &FieldError{FieldName: "ODFIBranchCountryCode", + Value: addenda13.ODFIBranchCountryCode, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda13 *Addenda13) fieldInclusion() error { + if addenda13.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda13.recordType, Msg: msgFieldInclusion} + } + if addenda13.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda13.typeCode, Msg: msgFieldInclusion} + } + if addenda13.ODFIName == "" { + return &FieldError{FieldName: "ODFIName", + Value: addenda13.ODFIName, Msg: msgFieldInclusion} + } + if addenda13.ODFIIDNumberQualifier == "" { + return &FieldError{FieldName: "ODFIIDNumberQualifier", + Value: addenda13.ODFIIDNumberQualifier, Msg: msgFieldInclusion} + } + if addenda13.ODFIIdentification == "" { + return &FieldError{FieldName: "ODFIIdentification", + Value: addenda13.ODFIIdentification, Msg: msgFieldInclusion} + } + if addenda13.ODFIBranchCountryCode == "" { + return &FieldError{FieldName: "ODFIBranchCountryCode", + Value: addenda13.ODFIBranchCountryCode, Msg: msgFieldInclusion} + } + if addenda13.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", + Value: addenda13.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// ODFINameField gets the ODFIName field left padded +func (addenda13 *Addenda13) ODFINameField() string { + return addenda13.alphaField(addenda13.ODFIName, 35) +} + +// ODFIIDNumberQualifierField gets the ODFIIDNumberQualifier field left padded +func (addenda13 *Addenda13) ODFIIDNumberQualifierField() string { + return addenda13.alphaField(addenda13.ODFIIDNumberQualifier, 2) +} + +// ODFIIdentificationField gets the ODFIIdentificationCode field left padded +func (addenda13 *Addenda13) ODFIIdentificationField() string { + return addenda13.alphaField(addenda13.ODFIIdentification, 34) +} + +// ODFIBranchCountryCodeField gets the ODFIBranchCountryCode field left padded +func (addenda13 *Addenda13) ODFIBranchCountryCodeField() string { + return addenda13.alphaField(addenda13.ODFIBranchCountryCode, 2) + " " +} + +// reservedField gets reserved - blank space +func (addenda13 *Addenda13) reservedField() string { + return addenda13.alphaField(addenda13.reserved, 10) +} + +// TypeCode Defines the specific explanation and format for the addenda13 information left padded +func (addenda13 *Addenda13) TypeCode() string { + return addenda13.typeCode +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda13 *Addenda13) EntryDetailSequenceNumberField() string { + return addenda13.numericField(addenda13.EntryDetailSequenceNumber, 7) +} diff --git a/addenda13_test.go b/addenda13_test.go new file mode 100644 index 000000000..e6d930c9e --- /dev/null +++ b/addenda13_test.go @@ -0,0 +1,428 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda13() creates a mock Addenda13 record +func mockAddenda13() *Addenda13 { + addenda13 := NewAddenda13() + addenda13.ODFIName = "Wells Fargo" + addenda13.ODFIIDNumberQualifier = "01" + addenda13.ODFIIdentification = "121042882" + addenda13.ODFIBranchCountryCode = "US" + addenda13.EntryDetailSequenceNumber = 00000001 + return addenda13 +} + +// TestMockAddenda13 validates mockAddenda13 +func TestMockAddenda13(t *testing.T) { + addenda13 := mockAddenda13() + if err := addenda13.Validate(); err != nil { + t.Error("mockAddenda13 does not validate and will break other tests") + } +} + +// testAddenda13ValidRecordType validates Addenda13 recordType +func testAddenda13ValidRecordType(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.recordType = "63" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda13ValidRecordType tests validating Addenda13 recordType +func TestAddenda13ValidRecordType(t *testing.T) { + testAddenda13ValidRecordType(t) +} + +// BenchmarkAddenda13ValidRecordType benchmarks validating Addenda13 recordType +func BenchmarkAddenda13ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13ValidRecordType(b) + } +} + +// testAddenda13ValidTypeCode validates Addenda13 TypeCode +func testAddenda13ValidTypeCode(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.typeCode = "65" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda13ValidTypeCode tests validating Addenda13 TypeCode +func TestAddenda13ValidTypeCode(t *testing.T) { + testAddenda13ValidTypeCode(t) +} + +// BenchmarkAddenda13ValidTypeCode benchmarks validating Addenda13 TypeCode +func BenchmarkAddenda13ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13ValidTypeCode(b) + } +} + +// testAddenda13TypeCode13 TypeCode is 13 if typeCode is a valid TypeCode +func testAddenda13TypeCode13(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.typeCode = "05" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda13TypeCode13 tests TypeCode is 13 if typeCode is a valid TypeCode +func TestAddenda13TypeCode13(t *testing.T) { + testAddenda13TypeCode13(t) +} + +// BenchmarkAddenda13TypeCode13 benchmarks TypeCode is 13 if typeCode is a valid TypeCode +func BenchmarkAddenda13TypeCode13(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13TypeCode13(b) + } +} + +// testODFINameAlphaNumeric validates ODFIName is alphanumeric +func testODFINameAlphaNumeric(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIName = "Wells®Fargo" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ODFIName" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestODFINameAlphaNumeric tests validating ODFIName is alphanumeric +func TestODFINameAlphaNumeric(t *testing.T) { + testODFINameAlphaNumeric(t) +} + +// BenchmarkODFINameAlphaNumeric benchmarks validating ODFIName is alphanumeric +func BenchmarkODFINameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testODFINameAlphaNumeric(b) + } +} + +// testODFIIDNumberQualifierValid validates ODFIIDNumberQualifier is valid +func testODFIIDNumberQualifierValid(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIIDNumberQualifier = "®1" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ODFIIDNumberQualifier" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestODFIIDNumberQualifierValid tests validating ODFIIDNumberQualifier is valid +func TestODFIIDNumberQualifierValid(t *testing.T) { + testODFIIDNumberQualifierValid(t) +} + +// BenchmarkODFIIDNumberQualifierValid benchmarks validating ODFIIDNumberQualifier is valid +func BenchmarkODFIIDNumberQualifierValid(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testODFIIDNumberQualifierValid(b) + } +} + +// testODFIIdentificationAlphaNumeric validates ODFIIdentification is alphanumeric +func testODFIIdentificationAlphaNumeric(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIIdentification = "®121042882" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestODFIIdentificationAlphaNumeric tests validating ODFIIdentification is alphanumeric +func TestODFIIdentificationAlphaNumeric(t *testing.T) { + testODFIIdentificationAlphaNumeric(t) +} + +// BenchmarkODFIIdentificationAlphaNumeric benchmarks validating ODFIIdentification is alphanumeric +func BenchmarkODFIIdentificationAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testODFIIdentificationAlphaNumeric(b) + } +} + +// testODFIBranchCountryCodeAlphaNumeric validates ODFIBranchCountryCode is alphanumeric +func testODFIBranchCountryCodeAlphaNumeric(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIBranchCountryCode = "U®" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ODFIBranchCountryCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestODFIBranchCountryCodeAlphaNumeric tests validating ODFIBranchCountryCode is alphanumeric +func TestODFIBranchCountryCodeAlphaNumeric(t *testing.T) { + testODFIBranchCountryCodeAlphaNumeric(t) +} + +// BenchmarkODFIBranchCountryCodeAlphaNumeric benchmarks validating ODFIBranchCountryCode is alphanumeric +func BenchmarkODFIBranchCountryCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testODFIBranchCountryCodeAlphaNumeric(b) + } +} + +// testAddenda13FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda13FieldInclusionRecordType(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.recordType = "" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda13FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda13FieldInclusionRecordType(t *testing.T) { + testAddenda13FieldInclusionRecordType(t) +} + +// BenchmarkAddenda13FieldInclusionRecordType benchmarks validating recordType fieldInclusion +func BenchmarkAddenda13FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13FieldInclusionRecordType(b) + } +} + +// testAddenda13FieldInclusionRecordType validates TypeCode fieldInclusion +func testAddenda13FieldInclusionTypeCode(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.typeCode = "" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda13FieldInclusionRecordType tests validating TypeCode fieldInclusion +func TestAddenda13FieldInclusionTypeCode(t *testing.T) { + testAddenda13FieldInclusionTypeCode(t) +} + +// BenchmarkAddenda13FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda13FieldInclusionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13FieldInclusionTypeCode(b) + } +} + +// testAddenda13FieldInclusionODFIName validates ODFIName fieldInclusion +func testAddenda13FieldInclusionODFIName(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIName = "" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda13FieldInclusionODFIName tests validating ODFIName fieldInclusion +func TestAddenda13FieldInclusionODFIName(t *testing.T) { + testAddenda13FieldInclusionODFIName(t) +} + +// BenchmarkAddenda13FieldInclusionODFIName benchmarks validating ODFIName fieldInclusion +func BenchmarkAddenda13FieldInclusionODFIName(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13FieldInclusionODFIName(b) + } +} + +// testAddenda13FieldInclusionODFIIDNumberQualifier validates ODFIIDNumberQualifier fieldInclusion +func testAddenda13FieldInclusionODFIIDNumberQualifier(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIIDNumberQualifier = "" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda13FieldInclusionODFIIDNumberQualifier tests validating ODFIIDNumberQualifier fieldInclusion +func TestAddenda13FieldInclusionODFIIDNumberQualifier(t *testing.T) { + testAddenda13FieldInclusionODFIIDNumberQualifier(t) +} + +// BenchmarkAddenda13FieldInclusionODFIIDNumberQualifier benchmarks validating ODFIIDNumberQualifier fieldInclusion +func BenchmarkAddenda13FieldInclusionODFIIDNumberQualifier(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13FieldInclusionODFIIDNumberQualifier(b) + } +} + +// testAddenda13FieldInclusionODFIIdentification validates ODFIIdentification fieldInclusion +func testAddenda13FieldInclusionODFIIdentification(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIIdentification = "" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda13FieldInclusionODFIIdentification tests validating ODFIIdentification fieldInclusion +func TestAddenda13FieldInclusionODFIIdentification(t *testing.T) { + testAddenda13FieldInclusionODFIIdentification(t) +} + +// BenchmarkAddenda13FieldInclusionODFIIdentification benchmarks validating ODFIIdentification fieldInclusion +func BenchmarkAddenda13FieldInclusionODFIIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13FieldInclusionODFIIdentification(b) + } +} + +// testAddenda13FieldInclusionODFIBranchCountryCode validates ODFIBranchCountryCode fieldInclusion +func testAddenda13FieldInclusionODFIBranchCountryCode(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.ODFIBranchCountryCode = "" + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda13FieldInclusionODFIBranchCountryCode tests validating ODFIBranchCountryCode fieldInclusion +func TestAddenda13FieldInclusionODFIBranchCountryCode(t *testing.T) { + testAddenda13FieldInclusionODFIBranchCountryCode(t) +} + +// BenchmarkAddenda13FieldInclusionODFIBranchCountryCode benchmarks validating ODFIBranchCountryCode fieldInclusion +func BenchmarkAddenda13FieldInclusionODFIBranchCountryCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13FieldInclusionODFIBranchCountryCode(b) + } +} + +// testAddenda13FieldInclusionEntryDetailSequenceNumber validates EntryDetailSequenceNumber fieldInclusion +func testAddenda13FieldInclusionEntryDetailSequenceNumber(t testing.TB) { + addenda13 := mockAddenda13() + addenda13.EntryDetailSequenceNumber = 0 + if err := addenda13.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda13FieldInclusionEntryDetailSequenceNumber tests validating +// EntryDetailSequenceNumber fieldInclusion +func TestAddenda13FieldInclusionEntryDetailSequenceNumber(t *testing.T) { + testAddenda13FieldInclusionEntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda13FieldInclusionEntryDetailSequenceNumber benchmarks validating +// EntryDetailSequenceNumber fieldInclusion +func BenchmarkAddenda13FieldInclusionEntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13FieldInclusionEntryDetailSequenceNumber(b) + } +} + +// TestAddenda13String validates that a known parsed Addenda13 record can be return to a string of the same value +func testAddenda13String(t testing.TB) { + addenda13 := NewAddenda13() + var line = "713Wells Fargo 121042882 US 0000001" + + addenda13.Parse(line) + + if addenda13.String() != line { + t.Errorf("Strings do not match") + } + if addenda13.TypeCode() != "13" { + t.Errorf("TypeCode Expected 13 got: %v", addenda13.TypeCode()) + } +} + +// TestAddenda13String tests validating that a known parsed Addenda13 record can be return to a string of the same value +func TestAddenda13String(t *testing.T) { + testAddenda13String(t) +} + +// BenchmarkAddenda13String benchmarks validating that a known parsed Addenda13 record can be return to a string of the same value +func BenchmarkAddenda13String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13String(b) + } +} diff --git a/addenda14.go b/addenda14.go new file mode 100644 index 000000000..38cbc7299 --- /dev/null +++ b/addenda14.go @@ -0,0 +1,202 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// Addenda14 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 14 in a machine readable format. It is usually formatted according to ANSI, ASC, X14 Standard. +// +// Addenda14 is mandatory for IAT entries +// +// The Addenda14 identifies the Receiving financial institution holding the Receiver's account. +type Addenda14 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. + recordType string + // TypeCode Addenda14 types code '14' + typeCode string + // Receiving DFI Name + // Name of the Receiver's bank + RDFIName string `json:"RDFIName"` + // Receiving DFI Identification Number Qualifier + // The 2-digit code that identifies the numbering scheme used in the + // Receiving DFI Identification Number field:: + // 01 = National Clearing System + // 02 = BIC Code + // 03 = IBAN Code + RDFIIDNumberQualifier string `json:"RDFIIDNumberQualifier"` + // Receiving DFI Identification + // This field contains the bank identification number of the DFI at which the + // Receiver maintains his account. + RDFIIdentification string `json:"RDFIIdentification"` + // Receiving DFI Branch Country Code + // USb” = United States + //(“b” indicates a blank space) + // This 3 position field contains a 2-character code as approved by the International + // Organization for Standardization (ISO) used to identify the country in which the + // branch of the bank that receives the entry is located. Values for other countries can + // be found on the International Organization for Standardization website: www.iso.org + RDFIBranchCountryCode string `json:"RDFIBranchCountryCode"` + // reserved - Leave blank + reserved string + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda14 returns a new Addenda14 with default values for none exported fields +func NewAddenda14() *Addenda14 { + addenda14 := new(Addenda14) + addenda14.recordType = "7" + addenda14.typeCode = "14" + return addenda14 +} + +// Parse takes the input record string and parses the Addenda14 values +func (addenda14 *Addenda14) Parse(record string) { + // 1-1 Always "7" + addenda14.recordType = "7" + // 2-3 Always 14 + addenda14.typeCode = record[1:3] + // 4-38 RDFIName + addenda14.RDFIName = record[3:38] + // 39-40 RDFIIDNumberQualifier + addenda14.RDFIIDNumberQualifier = record[38:40] + // 41-74 RDFIIdentification + addenda14.RDFIIdentification = record[40:74] + // 75-77 + addenda14.RDFIBranchCountryCode = record[74:77] + // 78-87 reserved - Leave blank + addenda14.reserved = " " + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda14.EntryDetailSequenceNumber = addenda14.parseNumField(record[87:94]) +} + +// String writes the Addenda14 struct to a 94 character string. +func (addenda14 *Addenda14) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v", + addenda14.recordType, + addenda14.typeCode, + addenda14.RDFINameField(), + addenda14.RDFIIDNumberQualifierField(), + addenda14.RDFIIdentificationField(), + addenda14.RDFIBranchCountryCodeField(), + addenda14.reservedField(), + addenda14.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda14 *Addenda14) Validate() error { + if err := addenda14.fieldInclusion(); err != nil { + return err + } + if addenda14.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda14.recordType, Msg: msg} + } + if err := addenda14.isTypeCode(addenda14.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda14.typeCode, Msg: err.Error()} + } + // Type Code must be 14 + if addenda14.typeCode != "14" { + return &FieldError{FieldName: "TypeCode", Value: addenda14.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda14.isAlphanumeric(addenda14.RDFIName); err != nil { + return &FieldError{FieldName: "RDFIName", + Value: addenda14.RDFIName, Msg: err.Error()} + } + // Valid RDFI Identification Number Qualifier + if err := addenda14.isIDNumberQualifier(addenda14.RDFIIDNumberQualifier); err != nil { + return &FieldError{FieldName: "RDFIIDNumberQualifier", + Value: addenda14.RDFIIDNumberQualifier, Msg: msgIDNumberQualifier} + } + if err := addenda14.isAlphanumeric(addenda14.RDFIIdentification); err != nil { + return &FieldError{FieldName: "RDFIIdentification", + Value: addenda14.RDFIIdentification, Msg: err.Error()} + } + if err := addenda14.isAlphanumeric(addenda14.RDFIBranchCountryCode); err != nil { + return &FieldError{FieldName: "RDFIBranchCountryCode", + Value: addenda14.RDFIBranchCountryCode, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda14 *Addenda14) fieldInclusion() error { + if addenda14.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda14.recordType, Msg: msgFieldInclusion} + } + if addenda14.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda14.typeCode, Msg: msgFieldInclusion} + } + if addenda14.RDFIName == "" { + return &FieldError{FieldName: "RDFIName", + Value: addenda14.RDFIName, Msg: msgFieldInclusion} + } + if addenda14.RDFIIDNumberQualifier == "" { + return &FieldError{FieldName: "RDFIIDNumberQualifier", + Value: addenda14.RDFIIDNumberQualifier, Msg: msgFieldInclusion} + } + if addenda14.RDFIIdentification == "" { + return &FieldError{FieldName: "RDFIIdentification", + Value: addenda14.RDFIIdentification, Msg: msgFieldInclusion} + } + if addenda14.RDFIBranchCountryCode == "" { + return &FieldError{FieldName: "RDFIBranchCountryCode", + Value: addenda14.RDFIBranchCountryCode, Msg: msgFieldInclusion} + } + if addenda14.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", + Value: addenda14.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// RDFINameField gets the RDFIName field left padded +func (addenda14 *Addenda14) RDFINameField() string { + return addenda14.alphaField(addenda14.RDFIName, 35) +} + +// RDFIIDNumberQualifierField gets the RDFIIDNumberQualifier field left padded +func (addenda14 *Addenda14) RDFIIDNumberQualifierField() string { + return addenda14.alphaField(addenda14.RDFIIDNumberQualifier, 2) +} + +// RDFIIdentificationField gets the RDFIIdentificationCode field left padded +func (addenda14 *Addenda14) RDFIIdentificationField() string { + return addenda14.alphaField(addenda14.RDFIIdentification, 34) +} + +// RDFIBranchCountryCodeField gets the RDFIBranchCountryCode field left padded +func (addenda14 *Addenda14) RDFIBranchCountryCodeField() string { + return addenda14.alphaField(addenda14.RDFIBranchCountryCode, 2) + " " +} + +// reservedField gets reserved - blank space +func (addenda14 *Addenda14) reservedField() string { + return addenda14.alphaField(addenda14.reserved, 10) +} + +// TypeCode Defines the specific explanation and format for the addenda14 information left padded +func (addenda14 *Addenda14) TypeCode() string { + return addenda14.typeCode +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda14 *Addenda14) EntryDetailSequenceNumberField() string { + return addenda14.numericField(addenda14.EntryDetailSequenceNumber, 7) +} diff --git a/addenda14_test.go b/addenda14_test.go new file mode 100644 index 000000000..3106abb48 --- /dev/null +++ b/addenda14_test.go @@ -0,0 +1,428 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda14() creates a mock Addenda14 record +func mockAddenda14() *Addenda14 { + addenda14 := NewAddenda14() + addenda14.RDFIName = "Citadel Bank" + addenda14.RDFIIDNumberQualifier = "01" + addenda14.RDFIIdentification = "231380104" + addenda14.RDFIBranchCountryCode = "US" + addenda14.EntryDetailSequenceNumber = 00000001 + return addenda14 +} + +// TestMockAddenda14 validates mockAddenda14 +func TestMockAddenda14(t *testing.T) { + addenda14 := mockAddenda14() + if err := addenda14.Validate(); err != nil { + t.Error("mockAddenda14 does not validate and will break other tests") + } +} + +// testAddenda14ValidRecordType validates Addenda14 recordType +func testAddenda14ValidRecordType(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.recordType = "63" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda14ValidRecordType tests validating Addenda14 recordType +func TestAddenda14ValidRecordType(t *testing.T) { + testAddenda14ValidRecordType(t) +} + +// BenchmarkAddenda14ValidRecordType benchmarks validating Addenda14 recordType +func BenchmarkAddenda14ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14ValidRecordType(b) + } +} + +// testAddenda14ValidTypeCode validates Addenda14 TypeCode +func testAddenda14ValidTypeCode(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.typeCode = "65" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda14ValidTypeCode tests validating Addenda14 TypeCode +func TestAddenda14ValidTypeCode(t *testing.T) { + testAddenda14ValidTypeCode(t) +} + +// BenchmarkAddenda14ValidTypeCode benchmarks validating Addenda14 TypeCode +func BenchmarkAddenda14ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14ValidTypeCode(b) + } +} + +// testAddenda14TypeCode14 TypeCode is 14 if typeCode is a valid TypeCode +func testAddenda14TypeCode14(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.typeCode = "05" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda14TypeCode14 tests TypeCode is 14 if typeCode is a valid TypeCode +func TestAddenda14TypeCode14(t *testing.T) { + testAddenda14TypeCode14(t) +} + +// BenchmarkAddenda14TypeCode14 benchmarks TypeCode is 14 if typeCode is a valid TypeCode +func BenchmarkAddenda14TypeCode14(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14TypeCode14(b) + } +} + +// testRDFINameAlphaNumeric validates RDFIName is alphanumeric +func testRDFINameAlphaNumeric(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIName = "Wells®Fargo" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "RDFIName" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestRDFINameAlphaNumeric tests validating RDFIName is alphanumeric +func TestRDFINameAlphaNumeric(t *testing.T) { + testRDFINameAlphaNumeric(t) +} + +// BenchmarkRDFINameAlphaNumeric benchmarks validating RDFIName is alphanumeric +func BenchmarkRDFINameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRDFINameAlphaNumeric(b) + } +} + +// testRDFIIDNumberQualifierValid validates RDFIIDNumberQualifier is valid +func testRDFIIDNumberQualifierValid(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIIDNumberQualifier = "®1" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "RDFIIDNumberQualifier" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestRDFIIDNumberQualifierValid tests validating RDFIIDNumberQualifier is valid +func TestRDFIIDNumberQualifierValid(t *testing.T) { + testRDFIIDNumberQualifierValid(t) +} + +// BenchmarkRDFIIDNumberQualifierValid benchmarks validating RDFIIDNumberQualifier is valid +func BenchmarkRDFIIDNumberQualifierValid(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRDFIIDNumberQualifierValid(b) + } +} + +// testRDFIIdentificationAlphaNumeric validates RDFIIdentification is alphanumeric +func testRDFIIdentificationAlphaNumeric(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIIdentification = "®121042882" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "RDFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestRDFIIdentificationAlphaNumeric tests validating RDFIIdentification is alphanumeric +func TestRDFIIdentificationAlphaNumeric(t *testing.T) { + testRDFIIdentificationAlphaNumeric(t) +} + +// BenchmarkRDFIIdentificationAlphaNumeric benchmarks validating RDFIIdentification is alphanumeric +func BenchmarkRDFIIdentificationAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRDFIIdentificationAlphaNumeric(b) + } +} + +// testRDFIBranchCountryCodeAlphaNumeric validates RDFIBranchCountryCode is alphanumeric +func testRDFIBranchCountryCodeAlphaNumeric(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIBranchCountryCode = "U®" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "RDFIBranchCountryCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestRDFIBranchCountryCodeAlphaNumeric tests validating RDFIBranchCountryCode is alphanumeric +func TestRDFIBranchCountryCodeAlphaNumeric(t *testing.T) { + testRDFIBranchCountryCodeAlphaNumeric(t) +} + +// BenchmarkRDFIBranchCountryCodeAlphaNumeric benchmarks validating RDFIBranchCountryCode is alphanumeric +func BenchmarkRDFIBranchCountryCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testRDFIBranchCountryCodeAlphaNumeric(b) + } +} + +// testAddenda14FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda14FieldInclusionRecordType(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.recordType = "" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda14FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda14FieldInclusionRecordType(t *testing.T) { + testAddenda14FieldInclusionRecordType(t) +} + +// BenchmarkAddenda14FieldInclusionRecordType benchmarks validating recordType fieldInclusion +func BenchmarkAddenda14FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14FieldInclusionRecordType(b) + } +} + +// testAddenda14FieldInclusionRecordType validates TypeCode fieldInclusion +func testAddenda14FieldInclusionTypeCode(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.typeCode = "" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda14FieldInclusionRecordType tests validating TypeCode fieldInclusion +func TestAddenda14FieldInclusionTypeCode(t *testing.T) { + testAddenda14FieldInclusionTypeCode(t) +} + +// BenchmarkAddenda14FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda14FieldInclusionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14FieldInclusionTypeCode(b) + } +} + +// testAddenda14FieldInclusionRDFIName validates RDFIName fieldInclusion +func testAddenda14FieldInclusionRDFIName(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIName = "" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda14FieldInclusionRDFIName tests validating RDFIName fieldInclusion +func TestAddenda14FieldInclusionRDFIName(t *testing.T) { + testAddenda14FieldInclusionRDFIName(t) +} + +// BenchmarkAddenda14FieldInclusionRDFIName benchmarks validating RDFIName fieldInclusion +func BenchmarkAddenda14FieldInclusionRDFIName(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14FieldInclusionRDFIName(b) + } +} + +// testAddenda14FieldInclusionRDFIIDNumberQualifier validates RDFIIDNumberQualifier fieldInclusion +func testAddenda14FieldInclusionRDFIIDNumberQualifier(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIIDNumberQualifier = "" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda14FieldInclusionRDFIIdNumberQualifier tests validating RDFIIdNumberQualifier fieldInclusion +func TestAddenda14FieldInclusionRDFIIdNumberQualifier(t *testing.T) { + testAddenda14FieldInclusionRDFIIDNumberQualifier(t) +} + +// BenchmarkAddenda14FieldInclusionRDFIIdNumberQualifier benchmarks validating RDFIIdNumberQualifier fieldInclusion +func BenchmarkAddenda14FieldInclusionRDFIIdNumberQualifier(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14FieldInclusionRDFIIDNumberQualifier(b) + } +} + +// testAddenda14FieldInclusionRDFIIdentification validates RDFIIdentification fieldInclusion +func testAddenda14FieldInclusionRDFIIdentification(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIIdentification = "" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda14FieldInclusionRDFIIdentification tests validating RDFIIdentification fieldInclusion +func TestAddenda14FieldInclusionRDFIIdentification(t *testing.T) { + testAddenda14FieldInclusionRDFIIdentification(t) +} + +// BenchmarkAddenda14FieldInclusionRDFIIdentification benchmarks validating RDFIIdentification fieldInclusion +func BenchmarkAddenda14FieldInclusionRDFIIdentification(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14FieldInclusionRDFIIdentification(b) + } +} + +// testAddenda14FieldInclusionRDFIBranchCountryCode validates RDFIBranchCountryCode fieldInclusion +func testAddenda14FieldInclusionRDFIBranchCountryCode(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.RDFIBranchCountryCode = "" + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda14FieldInclusionRDFIBranchCountryCode tests validating RDFIBranchCountryCode fieldInclusion +func TestAddenda14FieldInclusionRDFIBranchCountryCode(t *testing.T) { + testAddenda14FieldInclusionRDFIBranchCountryCode(t) +} + +// BenchmarkAddenda14FieldInclusionRDFIBranchCountryCode benchmarks validating RDFIBranchCountryCode fieldInclusion +func BenchmarkAddenda14FieldInclusionRDFIBranchCountryCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14FieldInclusionRDFIBranchCountryCode(b) + } +} + +// testAddenda14FieldInclusionEntryDetailSequenceNumber validates EntryDetailSequenceNumber fieldInclusion +func testAddenda14FieldInclusionEntryDetailSequenceNumber(t testing.TB) { + addenda14 := mockAddenda14() + addenda14.EntryDetailSequenceNumber = 0 + if err := addenda14.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda14FieldInclusionEntryDetailSequenceNumber tests validating +// EntryDetailSequenceNumber fieldInclusion +func TestAddenda14FieldInclusionEntryDetailSequenceNumber(t *testing.T) { + testAddenda14FieldInclusionEntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda14FieldInclusionEntryDetailSequenceNumber benchmarks validating +// EntryDetailSequenceNumber fieldInclusion +func BenchmarkAddenda14FieldInclusionEntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14FieldInclusionEntryDetailSequenceNumber(b) + } +} + +// TestAddenda14String validates that a known parsed Addenda14 record can be return to a string of the same value +func testAddenda14String(t testing.TB) { + addenda14 := NewAddenda14() + var line = "714Citadel Bank 231380104 US 0000001" + + addenda14.Parse(line) + + if addenda14.String() != line { + t.Errorf("Strings do not match") + } + if addenda14.TypeCode() != "14" { + t.Errorf("TypeCode Expected 14 got: %v", addenda14.TypeCode()) + } +} + +// TestAddenda14String tests validating that a known parsed Addenda14 record can be return to a string of the same value +func TestAddenda14String(t *testing.T) { + testAddenda14String(t) +} + +// BenchmarkAddenda14String benchmarks validating that a known parsed Addenda14 record can be return to a string of the same value +func BenchmarkAddenda14String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14String(b) + } +} diff --git a/validators.go b/validators.go index 382be9931..e8263d902 100644 --- a/validators.go +++ b/validators.go @@ -35,6 +35,7 @@ var ( msgForeignExchangeIndicator = "is an invalid Foreign Exchange Indicator" msgForeignExchangeReferenceIndicator = "is an invalid Foreign Exchange Reference Indicator" msgTransactionTypeCode = "is an invalid Addenda10 Transaction Type Code" + msgIDNumberQualifier = "is an invalid Identification Number Qualifier" ) // validator is common validation and formatting of golang types to ach type strings @@ -170,6 +171,22 @@ func (v *validator) isForeignExchangeReferenceIndicator(code int) error { return errors.New(msgForeignExchangeReferenceIndicator) } +// isIDNumberQualifier ensures ODFI Identification Number Qualifier is valid +// For Inbound IATs: The 2-digit code that identifies the numbering scheme used in the +// Foreign DFI Identification Number field: +// 01 = National Clearing System +// 02 = BIC Code +// 03 = IBAN Code +// used for both ODFIIDNumberQualifier and RDFIIDNumberQualifier +func (v *validator) isIDNumberQualifier(s string) error { + switch s { + case + "01", "02", "03": + return nil + } + return errors.New(msgIDNumberQualifier) +} + // isOriginatorStatusCode ensures status code of a batch is valid func (v *validator) isOriginatorStatusCode(code int) error { switch code { From 83986e300e862f7f6dc15bd654978d2de78840f1 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 14:48:53 -0400 Subject: [PATCH 0283/1694] #211 Code Comment Modifications #211 Code Comment Modifications --- addenda02_test.go | 6 +++--- addenda10_test.go | 6 +++--- addenda11_test.go | 6 +++--- addenda12_test.go | 6 +++--- addenda13_test.go | 6 +++--- addenda14_test.go | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/addenda02_test.go b/addenda02_test.go index 26bcb2373..adb20c3ca 100644 --- a/addenda02_test.go +++ b/addenda02_test.go @@ -142,7 +142,7 @@ func BenchmarkAddenda02FieldInclusionRecordType(b *testing.B) { } } -// testAddenda02FieldInclusionRecordType validates TypeCode fieldInclusion +// testAddenda02FieldInclusionTypeCode validates TypeCode fieldInclusion func testAddenda02FieldInclusionTypeCode(t testing.TB) { addenda02 := mockAddenda02() addenda02.typeCode = "" @@ -155,12 +155,12 @@ func testAddenda02FieldInclusionTypeCode(t testing.TB) { } } -// TestAddenda02FieldInclusionRecordType tests validating TypeCode fieldInclusion +// TestAddenda02FieldInclusionTypeCode tests validating TypeCode fieldInclusion func TestAddenda02FieldInclusionTypeCode(t *testing.T) { testAddenda02FieldInclusionTypeCode(t) } -// BenchmarkAddenda02FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +// BenchmarkAddenda02FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion func BenchmarkAddenda02FieldInclusionTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/addenda10_test.go b/addenda10_test.go index 20e719e49..a76d802e9 100644 --- a/addenda10_test.go +++ b/addenda10_test.go @@ -217,7 +217,7 @@ func BenchmarkAddenda10FieldInclusionRecordType(b *testing.B) { } } -// testAddenda10FieldInclusionRecordType validates TypeCode fieldInclusion +// testAddenda10FieldInclusionTypeCode validates TypeCode fieldInclusion func testAddenda10FieldInclusionTypeCode(t testing.TB) { addenda10 := mockAddenda10() addenda10.typeCode = "" @@ -230,12 +230,12 @@ func testAddenda10FieldInclusionTypeCode(t testing.TB) { } } -// TestAddenda10FieldInclusionRecordType tests validating TypeCode fieldInclusion +// TestAddenda10FieldInclusionTypeCode tests validating TypeCode fieldInclusion func TestAddenda10FieldInclusionTypeCode(t *testing.T) { testAddenda10FieldInclusionTypeCode(t) } -// BenchmarkAddenda10FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +// BenchmarkAddenda10FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion func BenchmarkAddenda10FieldInclusionTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/addenda11_test.go b/addenda11_test.go index 8251ad68e..a36a835d3 100644 --- a/addenda11_test.go +++ b/addenda11_test.go @@ -187,7 +187,7 @@ func BenchmarkAddenda11FieldInclusionRecordType(b *testing.B) { } } -// testAddenda11FieldInclusionRecordType validates TypeCode fieldInclusion +// testAddenda11FieldInclusionTypeCode validates TypeCode fieldInclusion func testAddenda11FieldInclusionTypeCode(t testing.TB) { addenda11 := mockAddenda11() addenda11.typeCode = "" @@ -200,12 +200,12 @@ func testAddenda11FieldInclusionTypeCode(t testing.TB) { } } -// TestAddenda11FieldInclusionRecordType tests validating TypeCode fieldInclusion +// TestAddenda11FieldInclusionTypeCode tests validating TypeCode fieldInclusion func TestAddenda11FieldInclusionTypeCode(t *testing.T) { testAddenda11FieldInclusionTypeCode(t) } -// BenchmarkAddenda11FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +// BenchmarkAddenda11FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion func BenchmarkAddenda11FieldInclusionTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/addenda12_test.go b/addenda12_test.go index 10a67cec6..6e099dd2e 100644 --- a/addenda12_test.go +++ b/addenda12_test.go @@ -187,7 +187,7 @@ func BenchmarkAddenda12FieldInclusionRecordType(b *testing.B) { } } -// testAddenda12FieldInclusionRecordType validates TypeCode fieldInclusion +// testAddenda12FieldInclusionTypeCode validates TypeCode fieldInclusion func testAddenda12FieldInclusionTypeCode(t testing.TB) { addenda12 := mockAddenda12() addenda12.typeCode = "" @@ -200,12 +200,12 @@ func testAddenda12FieldInclusionTypeCode(t testing.TB) { } } -// TestAddenda12FieldInclusionRecordType tests validating TypeCode fieldInclusion +// TestAddenda12FieldInclusionTypeCode tests validating TypeCode fieldInclusion func TestAddenda12FieldInclusionTypeCode(t *testing.T) { testAddenda12FieldInclusionTypeCode(t) } -// BenchmarkAddenda12FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +// BenchmarkAddenda12FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion func BenchmarkAddenda12FieldInclusionTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/addenda13_test.go b/addenda13_test.go index e6d930c9e..29cf8b5ef 100644 --- a/addenda13_test.go +++ b/addenda13_test.go @@ -241,7 +241,7 @@ func BenchmarkAddenda13FieldInclusionRecordType(b *testing.B) { } } -// testAddenda13FieldInclusionRecordType validates TypeCode fieldInclusion +// testAddenda13FieldInclusionTypeCode validates TypeCode fieldInclusion func testAddenda13FieldInclusionTypeCode(t testing.TB) { addenda13 := mockAddenda13() addenda13.typeCode = "" @@ -254,12 +254,12 @@ func testAddenda13FieldInclusionTypeCode(t testing.TB) { } } -// TestAddenda13FieldInclusionRecordType tests validating TypeCode fieldInclusion +// TestAddenda13FieldInclusionTypeCode tests validating TypeCode fieldInclusion func TestAddenda13FieldInclusionTypeCode(t *testing.T) { testAddenda13FieldInclusionTypeCode(t) } -// BenchmarkAddenda13FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +// BenchmarkAddenda13FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion func BenchmarkAddenda13FieldInclusionTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/addenda14_test.go b/addenda14_test.go index 3106abb48..a2a3c654f 100644 --- a/addenda14_test.go +++ b/addenda14_test.go @@ -241,7 +241,7 @@ func BenchmarkAddenda14FieldInclusionRecordType(b *testing.B) { } } -// testAddenda14FieldInclusionRecordType validates TypeCode fieldInclusion +// testAddenda14FieldInclusionTypeCode validates TypeCode fieldInclusion func testAddenda14FieldInclusionTypeCode(t testing.TB) { addenda14 := mockAddenda14() addenda14.typeCode = "" @@ -254,12 +254,12 @@ func testAddenda14FieldInclusionTypeCode(t testing.TB) { } } -// TestAddenda14FieldInclusionRecordType tests validating TypeCode fieldInclusion +// TestAddenda14FieldInclusionTypeCode tests validating TypeCode fieldInclusion func TestAddenda14FieldInclusionTypeCode(t *testing.T) { testAddenda14FieldInclusionTypeCode(t) } -// BenchmarkAddenda14FieldInclusionRecordType benchmarks validating TypeCode fieldInclusion +// BenchmarkAddenda14FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion func BenchmarkAddenda14FieldInclusionTypeCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From a2fda039719ee0a19ecdab2ec579e903ba1532c9 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 16:34:33 -0400 Subject: [PATCH 0284/1694] #211 addenda15 and addenda16 #211 addenda15 and addenda16 --- addenda12.go | 2 +- addenda15.go | 147 +++++++++++++++++++++ addenda15_test.go | 295 +++++++++++++++++++++++++++++++++++++++++ addenda16.go | 158 ++++++++++++++++++++++ addenda16_test.go | 327 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 928 insertions(+), 1 deletion(-) create mode 100644 addenda15.go create mode 100644 addenda15_test.go create mode 100644 addenda16.go create mode 100644 addenda16_test.go diff --git a/addenda12.go b/addenda12.go index c591faf20..6f79f0f7d 100644 --- a/addenda12.go +++ b/addenda12.go @@ -60,7 +60,7 @@ func (addenda12 *Addenda12) Parse(record string) { addenda12.typeCode = record[1:3] // 4-38 addenda12.OriginatorCityStateProvince = record[3:38] - // 38-73 + // 39-73 addenda12.OriginatorCountryPostalCode = record[38:73] // 74-87 reserved - Leave blank addenda12.reserved = " " diff --git a/addenda15.go b/addenda15.go new file mode 100644 index 000000000..dd5388e23 --- /dev/null +++ b/addenda15.go @@ -0,0 +1,147 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// Addenda15 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 15 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// +// Addenda15 is mandatory for IAT entries +// +// The Addenda15 record identifies key information related to the Receiver. +type Addenda15 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. + recordType string + // TypeCode Addenda15 types code '15' + typeCode string + // Receiver Identification Number contains the accounting number by which the Originator is known to + // the Receiver for descriptive purposes. NACHA Rules recommend but do not require the RDFI to print + // the contents of this field on the receiver's statement. + ReceiverIDNumber string `json:"receiverIDNumber,omitempty"` + // Receiver Street Address contains the Receiver‟s physical address + ReceiverStreetAddress string `json:"receiverStreetAddress"` + // reserved - Leave blank + reserved string + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda15 returns a new Addenda15 with default values for none exported fields +func NewAddenda15() *Addenda15 { + addenda15 := new(Addenda15) + addenda15.recordType = "7" + addenda15.typeCode = "15" + return addenda15 +} + +// Parse takes the input record string and parses the Addenda15 values +func (addenda15 *Addenda15) Parse(record string) { + // 1-1 Always "7" + addenda15.recordType = "7" + // 2-3 Always 15 + addenda15.typeCode = record[1:3] + // 4-18 + addenda15.ReceiverIDNumber = record[3:18] + // 19-53 + addenda15.ReceiverStreetAddress = record[18:53] + // 54-87 reserved - Leave blank + addenda15.reserved = " " + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda15.EntryDetailSequenceNumber = addenda15.parseNumField(record[87:94]) +} + +// String writes the Addenda15 struct to a 94 character string. +func (addenda15 *Addenda15) String() string { + return fmt.Sprintf("%v%v%v%v%v%v", + addenda15.recordType, + addenda15.typeCode, + addenda15.ReceiverIDNumberField(), + addenda15.ReceiverStreetAddressField(), + addenda15.reservedField(), + addenda15.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda15 *Addenda15) Validate() error { + if err := addenda15.fieldInclusion(); err != nil { + return err + } + if addenda15.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda15.recordType, Msg: msg} + } + if err := addenda15.isTypeCode(addenda15.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda15.typeCode, Msg: err.Error()} + } + // Type Code must be 15 + if addenda15.typeCode != "15" { + return &FieldError{FieldName: "TypeCode", Value: addenda15.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda15.isAlphanumeric(addenda15.ReceiverIDNumber); err != nil { + return &FieldError{FieldName: "ReceiverIDNumber", Value: addenda15.ReceiverIDNumber, Msg: err.Error()} + } + if err := addenda15.isAlphanumeric(addenda15.ReceiverStreetAddress); err != nil { + return &FieldError{FieldName: "ReceiverStreetAddress", Value: addenda15.ReceiverStreetAddress, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda15 *Addenda15) fieldInclusion() error { + if addenda15.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda15.recordType, Msg: msgFieldInclusion} + } + if addenda15.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda15.typeCode, Msg: msgFieldInclusion} + } + if addenda15.ReceiverStreetAddress == "" { + return &FieldError{FieldName: "ReceiverStreetAddress", + Value: addenda15.ReceiverStreetAddress, Msg: msgFieldInclusion} + } + if addenda15.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", + Value: addenda15.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// ReceiverIDNumberField gets the ReceiverIDNumber field left padded +func (addenda15 *Addenda15) ReceiverIDNumberField() string { + return addenda15.alphaField(addenda15.ReceiverIDNumber, 15) +} + +// ReceiverStreetAddressField gets the ReceiverStreetAddressField field left padded +func (addenda15 *Addenda15) ReceiverStreetAddressField() string { + return addenda15.alphaField(addenda15.ReceiverStreetAddress, 35) +} + +// reservedField gets reserved - blank space +func (addenda15 *Addenda15) reservedField() string { + return addenda15.alphaField(addenda15.reserved, 34) +} + +// TypeCode Defines the specific explanation and format for the addenda15 information left padded +func (addenda15 *Addenda15) TypeCode() string { + return addenda15.typeCode +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda15 *Addenda15) EntryDetailSequenceNumberField() string { + return addenda15.numericField(addenda15.EntryDetailSequenceNumber, 7) +} diff --git a/addenda15_test.go b/addenda15_test.go new file mode 100644 index 000000000..631d611cf --- /dev/null +++ b/addenda15_test.go @@ -0,0 +1,295 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda15() creates a mock Addenda15 record +func mockAddenda15() *Addenda15 { + addenda15 := NewAddenda15() + addenda15.ReceiverIDNumber = "987465493213987" + addenda15.ReceiverStreetAddress = "2121 Front Street" + addenda15.EntryDetailSequenceNumber = 00000001 + return addenda15 +} + +// TestMockAddenda15 validates mockAddenda15 +func TestMockAddenda15(t *testing.T) { + addenda15 := mockAddenda15() + if err := addenda15.Validate(); err != nil { + t.Error("mockAddenda15 does not validate and will break other tests") + } +} + +// testAddenda15ValidRecordType validates Addenda15 recordType +func testAddenda15ValidRecordType(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.recordType = "63" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda15ValidRecordType tests validating Addenda15 recordType +func TestAddenda15ValidRecordType(t *testing.T) { + testAddenda15ValidRecordType(t) +} + +// BenchmarkAddenda15ValidRecordType benchmarks validating Addenda15 recordType +func BenchmarkAddenda15ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15ValidRecordType(b) + } +} + +// testAddenda15ValidTypeCode validates Addenda15 TypeCode +func testAddenda15ValidTypeCode(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.typeCode = "65" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda15ValidTypeCode tests validating Addenda15 TypeCode +func TestAddenda15ValidTypeCode(t *testing.T) { + testAddenda15ValidTypeCode(t) +} + +// BenchmarkAddenda15ValidTypeCode benchmarks validating Addenda15 TypeCode +func BenchmarkAddenda15ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15ValidTypeCode(b) + } +} + +// testAddenda15TypeCode15 TypeCode is 15 if typeCode is a valid TypeCode +func testAddenda15TypeCode15(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.typeCode = "05" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda15TypeCode15 tests TypeCode is 15 if typeCode is a valid TypeCode +func TestAddenda15TypeCode15(t *testing.T) { + testAddenda15TypeCode15(t) +} + +// BenchmarkAddenda15TypeCode15 benchmarks TypeCode is 15 if typeCode is a valid TypeCode +func BenchmarkAddenda15TypeCode15(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15TypeCode15(b) + } +} + +// testReceiverIDNumberAlphaNumeric validates ReceiverIDNumber is alphanumeric +func testReceiverIDNumberAlphaNumeric(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.ReceiverIDNumber = "9874654932®1398" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReceiverIDNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestReceiverIDNumberAlphaNumeric tests validating ReceiverIDNumber is alphanumeric +func TestReceiverIDNumberAlphaNumeric(t *testing.T) { + testReceiverIDNumberAlphaNumeric(t) +} + +// BenchmarkReceiverIDNumberAlphaNumeric benchmarks validating ReceiverIDNumber is alphanumeric +func BenchmarkReceiverIDNumberAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testReceiverIDNumberAlphaNumeric(b) + } +} + +// testReceiverStreetAddressAlphaNumeric validates ReceiverStreetAddress is alphanumeric +func testReceiverStreetAddressAlphaNumeric(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.ReceiverStreetAddress = "2121 Fr®nt Street" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReceiverStreetAddress" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestReceiverStreetAddressAlphaNumeric tests validating ReceiverStreetAddress is alphanumeric +func TestReceiverStreetAddressAlphaNumeric(t *testing.T) { + testReceiverStreetAddressAlphaNumeric(t) +} + +// BenchmarkReceiverStreetAddressAlphaNumeric benchmarks validating ReceiverStreetAddress is alphanumeric +func BenchmarkReceiverStreetAddressAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testReceiverStreetAddressAlphaNumeric(b) + } +} + +// testAddenda15FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda15FieldInclusionRecordType(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.recordType = "" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda15FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda15FieldInclusionRecordType(t *testing.T) { + testAddenda15FieldInclusionRecordType(t) +} + +// BenchmarkAddenda15FieldInclusionRecordType benchmarks validating recordType fieldInclusion +func BenchmarkAddenda15FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15FieldInclusionRecordType(b) + } +} + +// testAddenda15FieldInclusionTypeCode validates TypeCode fieldInclusion +func testAddenda15FieldInclusionTypeCode(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.typeCode = "" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda15FieldInclusionTypeCode tests validating TypeCode fieldInclusion +func TestAddenda15FieldInclusionTypeCode(t *testing.T) { + testAddenda15FieldInclusionTypeCode(t) +} + +// BenchmarkAddenda15FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda15FieldInclusionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15FieldInclusionTypeCode(b) + } +} + +// testAddenda15FieldInclusionReceiverStreetAddress validates ReceiverStreetAddress fieldInclusion +func testAddenda15FieldInclusionReceiverStreetAddress(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.ReceiverStreetAddress = "" + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda15FieldInclusionReceiverStreetAddress tests validating ReceiverStreetAddress fieldInclusion +func TestAddenda15FieldInclusionReceiverStreetAddress(t *testing.T) { + testAddenda15FieldInclusionReceiverStreetAddress(t) +} + +// BenchmarkAddenda15FieldInclusionReceiverStreetAddress benchmarks validating ReceiverStreetAddress fieldInclusion +func BenchmarkAddenda15FieldInclusionReceiverStreetAddress(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15FieldInclusionReceiverStreetAddress(b) + } +} + +// testAddenda15FieldInclusionEntryDetailSequenceNumber validates EntryDetailSequenceNumber fieldInclusion +func testAddenda15FieldInclusionEntryDetailSequenceNumber(t testing.TB) { + addenda15 := mockAddenda15() + addenda15.EntryDetailSequenceNumber = 0 + if err := addenda15.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda15FieldInclusionEntryDetailSequenceNumber tests validating +// EntryDetailSequenceNumber fieldInclusion +func TestAddenda15FieldInclusionEntryDetailSequenceNumber(t *testing.T) { + testAddenda15FieldInclusionEntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda15FieldInclusionEntryDetailSequenceNumber benchmarks validating +// EntryDetailSequenceNumber fieldInclusion +func BenchmarkAddenda15FieldInclusionEntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15FieldInclusionEntryDetailSequenceNumber(b) + } +} + +// TestAddenda15String validates that a known parsed Addenda15 record can be return to a string of the same value +func testAddenda15String(t testing.TB) { + addenda15 := NewAddenda15() + var line = "7159874654932139872121 Front Street 0000001" + addenda15.Parse(line) + + if addenda15.String() != line { + t.Errorf("Strings do not match") + } + if addenda15.TypeCode() != "15" { + t.Errorf("TypeCode Expected 15 got: %v", addenda15.TypeCode()) + } +} + +// TestAddenda15String tests validating that a known parsed Addenda15 record can be return to a string of the same value +func TestAddenda15String(t *testing.T) { + testAddenda15String(t) +} + +// BenchmarkAddenda15String benchmarks validating that a known parsed Addenda15 record can be return to a string of the same value +func BenchmarkAddenda15String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15String(b) + } +} diff --git a/addenda16.go b/addenda16.go new file mode 100644 index 000000000..f26f1c1c0 --- /dev/null +++ b/addenda16.go @@ -0,0 +1,158 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// Addenda16 is a Addendumer addenda which provides business transaction information for Addenda Type +// Code 16 in a machine readable format. It is usually formatted according to ANSI, ASC, X16 Standard. +// +// Addenda16 is mandatory for IAT entries +// +// The Addenda16 record identifies key information related to the Receiver. +type Addenda16 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. + recordType string + // TypeCode Addenda16 types code '16' + typeCode string + // Receiver City & State / Province + // Data elements City and State / Province should be separated with an asterisk (*) as a delimiter + // and the field should end with a backslash (\). + // For example: San FranciscoCA. + ReceiverCityStateProvince string `json:"receiverCityStateProvince"` + // Receiver Country & Postal Code + // Data elements must be separated by an asterisk (*) and must end with a backslash (\) + // For example: US10036\ + ReceiverCountryPostalCode string `json:"receiverCountryPostalCode"` + // reserved - Leave blank + reserved string + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda16 returns a new Addenda16 with default values for none exported fields +func NewAddenda16() *Addenda16 { + addenda16 := new(Addenda16) + addenda16.recordType = "7" + addenda16.typeCode = "16" + return addenda16 +} + +// Parse takes the input record string and parses the Addenda16 values +func (addenda16 *Addenda16) Parse(record string) { + // 1-1 Always "7" + addenda16.recordType = "7" + // 2-3 Always 16 + addenda16.typeCode = record[1:3] + // 4-38 + addenda16.ReceiverCityStateProvince = record[3:38] + // 39-73 + addenda16.ReceiverCountryPostalCode = record[38:73] + // 74-87 reserved - Leave blank + addenda16.reserved = " " + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda16.EntryDetailSequenceNumber = addenda16.parseNumField(record[87:94]) +} + +// String writes the Addenda16 struct to a 94 character string. +func (addenda16 *Addenda16) String() string { + return fmt.Sprintf("%v%v%v%v%v%v", + addenda16.recordType, + addenda16.typeCode, + addenda16.ReceiverCityStateProvinceField(), + // ToDo: Validator for backslash + addenda16.ReceiverCountryPostalCodeField(), + addenda16.reservedField(), + addenda16.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda16 *Addenda16) Validate() error { + if err := addenda16.fieldInclusion(); err != nil { + return err + } + if addenda16.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda16.recordType, Msg: msg} + } + if err := addenda16.isTypeCode(addenda16.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda16.typeCode, Msg: err.Error()} + } + // Type Code must be 16 + if addenda16.typeCode != "16" { + return &FieldError{FieldName: "TypeCode", Value: addenda16.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda16.isAlphanumeric(addenda16.ReceiverCityStateProvince); err != nil { + return &FieldError{FieldName: "ReceiverCityStateProvince", + Value: addenda16.ReceiverCityStateProvince, Msg: err.Error()} + } + if err := addenda16.isAlphanumeric(addenda16.ReceiverCountryPostalCode); err != nil { + return &FieldError{FieldName: "ReceiverCountryPostalCode", + Value: addenda16.ReceiverCountryPostalCode, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda16 *Addenda16) fieldInclusion() error { + if addenda16.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda16.recordType, Msg: msgFieldInclusion} + } + if addenda16.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda16.typeCode, Msg: msgFieldInclusion} + } + if addenda16.ReceiverCityStateProvince == "" { + return &FieldError{FieldName: "ReceiverCityStateProvince", + Value: addenda16.ReceiverCityStateProvince, Msg: msgFieldInclusion} + } + if addenda16.ReceiverCountryPostalCode == "" { + return &FieldError{FieldName: "ReceiverCountryPostalCode", + Value: addenda16.ReceiverCountryPostalCode, Msg: msgFieldInclusion} + } + if addenda16.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", + Value: addenda16.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// ReceiverCityStateProvinceField gets the ReceiverCityStateProvinceField left padded +func (addenda16 *Addenda16) ReceiverCityStateProvinceField() string { + return addenda16.alphaField(addenda16.ReceiverCityStateProvince, 35) +} + +// ReceiverCountryPostalCodeField gets the ReceiverCountryPostalCode field left padded +func (addenda16 *Addenda16) ReceiverCountryPostalCodeField() string { + return addenda16.alphaField(addenda16.ReceiverCountryPostalCode, 35) +} + +// reservedField gets reserved - blank space +func (addenda16 *Addenda16) reservedField() string { + return addenda16.alphaField(addenda16.reserved, 14) +} + +// TypeCode Defines the specific explanation and format for the addenda16 information left padded +func (addenda16 *Addenda16) TypeCode() string { + return addenda16.typeCode +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda16 *Addenda16) EntryDetailSequenceNumberField() string { + return addenda16.numericField(addenda16.EntryDetailSequenceNumber, 7) +} + diff --git a/addenda16_test.go b/addenda16_test.go new file mode 100644 index 000000000..474f571e4 --- /dev/null +++ b/addenda16_test.go @@ -0,0 +1,327 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda16() creates a mock Addenda16 record +func mockAddenda16() *Addenda16 { + addenda16 := NewAddenda16() + addenda16.ReceiverCityStateProvince = "LetterTown*CO\\" + addenda16.ReceiverCountryPostalCode = "US80014\\" + addenda16.EntryDetailSequenceNumber = 00000001 + return addenda16 +} + +// TestMockAddenda16 validates mockAddenda16 +func TestMockAddenda16(t *testing.T) { + addenda16 := mockAddenda16() + if err := addenda16.Validate(); err != nil { + t.Error("mockAddenda16 does not validate and will break other tests") + } +} + +// testAddenda16ValidRecordType validates Addenda16 recordType +func testAddenda16ValidRecordType(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.recordType = "63" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda16ValidRecordType tests validating Addenda16 recordType +func TestAddenda16ValidRecordType(t *testing.T) { + testAddenda16ValidRecordType(t) +} + +// BenchmarkAddenda16ValidRecordType benchmarks validating Addenda16 recordType +func BenchmarkAddenda16ValidRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16ValidRecordType(b) + } +} + +// testAddenda16ValidTypeCode validates Addenda16 TypeCode +func testAddenda16ValidTypeCode(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.typeCode = "65" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda16ValidTypeCode tests validating Addenda16 TypeCode +func TestAddenda16ValidTypeCode(t *testing.T) { + testAddenda16ValidTypeCode(t) +} + +// BenchmarkAddenda16ValidTypeCode benchmarks validating Addenda16 TypeCode +func BenchmarkAddenda16ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16ValidTypeCode(b) + } +} + +// testAddenda16TypeCode16 TypeCode is 16 if typeCode is a valid TypeCode +func testAddenda16TypeCode16(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.typeCode = "05" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda16TypeCode16 tests TypeCode is 16 if typeCode is a valid TypeCode +func TestAddenda16TypeCode16(t *testing.T) { + testAddenda16TypeCode16(t) +} + +// BenchmarkAddenda16TypeCode16 benchmarks TypeCode is 16 if typeCode is a valid TypeCode +func BenchmarkAddenda16TypeCode16(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16TypeCode16(b) + } +} + +// testReceiverCityStateProvinceAlphaNumeric validates ReceiverCityStateProvince is alphanumeric +func testReceiverCityStateProvinceAlphaNumeric(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.ReceiverCityStateProvince = "Jacobs®Town*PA" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReceiverCityStateProvince" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestReceiverCityStateProvinceAlphaNumeric tests validating ReceiverCityStateProvince is alphanumeric +func TestReceiverCityStateProvinceAlphaNumeric(t *testing.T) { + testReceiverCityStateProvinceAlphaNumeric(t) +} + +// BenchmarkReceiverCityStateProvinceAlphaNumeric benchmarks validating ReceiverCityStateProvince is alphanumeric +func BenchmarkReceiverCityStateProvinceAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testReceiverCityStateProvinceAlphaNumeric(b) + } +} + +// testReceiverCountryPostalCodeAlphaNumeric validates ReceiverCountryPostalCode is alphanumeric +func testReceiverCountryPostalCodeAlphaNumeric(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.ReceiverCountryPostalCode = "US19®305" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ReceiverCountryPostalCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestReceiverCountryPostalCodeAlphaNumeric tests validating ReceiverCountryPostalCode is alphanumeric +func TestReceiverCountryPostalCodeAlphaNumeric(t *testing.T) { + testReceiverCountryPostalCodeAlphaNumeric(t) +} + +// BenchmarkReceiverCountryPostalCodeAlphaNumeric benchmarks validating ReceiverCountryPostalCode is alphanumeric +func BenchmarkReceiverCountryPostalCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testReceiverCountryPostalCodeAlphaNumeric(b) + } +} + +// testAddenda16FieldInclusionRecordType validates recordType fieldInclusion +func testAddenda16FieldInclusionRecordType(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.recordType = "" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda16FieldInclusionRecordType tests validating recordType fieldInclusion +func TestAddenda16FieldInclusionRecordType(t *testing.T) { + testAddenda16FieldInclusionRecordType(t) +} + +// BenchmarkAddenda16FieldInclusionRecordType benchmarks validating recordType fieldInclusion +func BenchmarkAddenda16FieldInclusionRecordType(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16FieldInclusionRecordType(b) + } +} + +// testAddenda16FieldInclusionTypeCode validates TypeCode fieldInclusion +func testAddenda16FieldInclusionTypeCode(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.typeCode = "" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda16FieldInclusionTypeCode tests validating TypeCode fieldInclusion +func TestAddenda16FieldInclusionTypeCode(t *testing.T) { + testAddenda16FieldInclusionTypeCode(t) +} + +// BenchmarkAddenda16FieldInclusionTypeCode benchmarks validating TypeCode fieldInclusion +func BenchmarkAddenda16FieldInclusionTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16FieldInclusionTypeCode(b) + } +} + +// testAddenda16FieldInclusionReceiverCityStateProvince validates ReceiverCityStateProvince fieldInclusion +func testAddenda16FieldInclusionReceiverCityStateProvince(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.ReceiverCityStateProvince = "" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda16FieldInclusionReceiverCityStateProvince tests validating ReceiverCityStateProvince fieldInclusion +func TestAddenda16FieldInclusionReceiverCityStateProvince(t *testing.T) { + testAddenda16FieldInclusionReceiverCityStateProvince(t) +} + +// BenchmarkAddenda16FieldInclusionReceiverCityStateProvince benchmarks validating ReceiverCityStateProvince fieldInclusion +func BenchmarkAddenda16FieldInclusionReceiverCityStateProvince(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16FieldInclusionReceiverCityStateProvince(b) + } +} + +// testAddenda16FieldInclusionReceiverCountryPostalCode validates ReceiverCountryPostalCode fieldInclusion +func testAddenda16FieldInclusionReceiverCountryPostalCode(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.ReceiverCountryPostalCode = "" + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda16FieldInclusionReceiverCountryPostalCode tests validating ReceiverCountryPostalCode fieldInclusion +func TestAddenda16FieldInclusionReceiverCountryPostalCode(t *testing.T) { + testAddenda16FieldInclusionReceiverCountryPostalCode(t) +} + +// BenchmarkAddenda16FieldInclusionReceiverCountryPostalCode benchmarks validating ReceiverCountryPostalCode fieldInclusion +func BenchmarkAddenda16FieldInclusionReceiverCountryPostalCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16FieldInclusionReceiverCountryPostalCode(b) + } +} + +// testAddenda16FieldInclusionEntryDetailSequenceNumber validates EntryDetailSequenceNumber fieldInclusion +func testAddenda16FieldInclusionEntryDetailSequenceNumber(t testing.TB) { + addenda16 := mockAddenda16() + addenda16.EntryDetailSequenceNumber = 0 + if err := addenda16.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda16FieldInclusionEntryDetailSequenceNumber tests validating +// EntryDetailSequenceNumber fieldInclusion +func TestAddenda16FieldInclusionEntryDetailSequenceNumber(t *testing.T) { + testAddenda16FieldInclusionEntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda16FieldInclusionEntryDetailSequenceNumber benchmarks validating +// EntryDetailSequenceNumber fieldInclusion +func BenchmarkAddenda16FieldInclusionEntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16FieldInclusionEntryDetailSequenceNumber(b) + } +} + +// TestAddenda16String validates that a known parsed Addenda16 record can be return to a string of the same value +func testAddenda16String(t testing.TB) { + addenda16 := NewAddenda16() + // Backslash logic + var line = "716" + + "LetterTown*CO\\ " + + "US80014\\ " + + " " + + "0000001" + + addenda16.Parse(line) + + if addenda16.String() != line { + t.Errorf("Strings do not match") + } + if addenda16.TypeCode() != "16" { + t.Errorf("TypeCode Expected 16 got: %v", addenda16.TypeCode()) + } +} + +// TestAddenda16String tests validating that a known parsed Addenda16 record can be return to a string of the same value +func TestAddenda16String(t *testing.T) { + testAddenda16String(t) +} + +// BenchmarkAddenda16String benchmarks validating that a known parsed Addenda16 record can be return to a string of the same value +func BenchmarkAddenda16String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16String(b) + } +} From 4dd05173905a5fa67198e7b1c03b7805d56ffac2 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 16:41:46 -0400 Subject: [PATCH 0285/1694] #211 gofmt addenda16.go #211 gofmt addenda16.go --- addenda16.go | 1 - 1 file changed, 1 deletion(-) diff --git a/addenda16.go b/addenda16.go index f26f1c1c0..b9de7d1fa 100644 --- a/addenda16.go +++ b/addenda16.go @@ -155,4 +155,3 @@ func (addenda16 *Addenda16) TypeCode() string { func (addenda16 *Addenda16) EntryDetailSequenceNumberField() string { return addenda16.numericField(addenda16.EntryDetailSequenceNumber, 7) } - From 7b1486dc921d53997d56c700999204b2ccbed351 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 18:23:05 -0400 Subject: [PATCH 0286/1694] #211 Added ToDo #211 Added ToDo --- addenda10_test.go | 2 ++ addenda11_test.go | 2 ++ addenda13_test.go | 2 ++ addenda14_test.go | 2 ++ addenda15_test.go | 2 ++ addenda16_test.go | 2 ++ 6 files changed, 12 insertions(+) diff --git a/addenda10_test.go b/addenda10_test.go index a76d802e9..53efccf74 100644 --- a/addenda10_test.go +++ b/addenda10_test.go @@ -351,6 +351,8 @@ func BenchmarkAddenda10FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } +// ToDo Add Parse test for individual fields + // TestAddenda10String validates that a known parsed Addenda10 record can be return to a string of the same value func testAddenda10String(t testing.TB) { addenda10 := NewAddenda10() diff --git a/addenda11_test.go b/addenda11_test.go index a36a835d3..0e736a337 100644 --- a/addenda11_test.go +++ b/addenda11_test.go @@ -293,6 +293,8 @@ func BenchmarkAddenda11FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } +// ToDo Add Parse test for individual fields + // TestAddenda11String validates that a known parsed Addenda11 record can be return to a string of the same value func testAddenda11String(t testing.TB) { addenda11 := NewAddenda11() diff --git a/addenda13_test.go b/addenda13_test.go index 29cf8b5ef..73774bd25 100644 --- a/addenda13_test.go +++ b/addenda13_test.go @@ -399,6 +399,8 @@ func BenchmarkAddenda13FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } +// ToDo Add Parse test for individual fields + // TestAddenda13String validates that a known parsed Addenda13 record can be return to a string of the same value func testAddenda13String(t testing.TB) { addenda13 := NewAddenda13() diff --git a/addenda14_test.go b/addenda14_test.go index a2a3c654f..4f9db902e 100644 --- a/addenda14_test.go +++ b/addenda14_test.go @@ -399,6 +399,8 @@ func BenchmarkAddenda14FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } +// ToDo Add Parse test for individual fields + // TestAddenda14String validates that a known parsed Addenda14 record can be return to a string of the same value func testAddenda14String(t testing.TB) { addenda14 := NewAddenda14() diff --git a/addenda15_test.go b/addenda15_test.go index 631d611cf..125d755ab 100644 --- a/addenda15_test.go +++ b/addenda15_test.go @@ -267,6 +267,8 @@ func BenchmarkAddenda15FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } +// ToDo Add Parse test for individual fields + // TestAddenda15String validates that a known parsed Addenda15 record can be return to a string of the same value func testAddenda15String(t testing.TB) { addenda15 := NewAddenda15() diff --git a/addenda16_test.go b/addenda16_test.go index 474f571e4..9a8be7271 100644 --- a/addenda16_test.go +++ b/addenda16_test.go @@ -293,6 +293,8 @@ func BenchmarkAddenda16FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } +// ToDo Add Parse test for individual fields + // TestAddenda16String validates that a known parsed Addenda16 record can be return to a string of the same value func testAddenda16String(t testing.TB) { addenda16 := NewAddenda16() From 967d525ac62bf90d100ab97a445a2681fa5d5e13 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 22:49:28 -0400 Subject: [PATCH 0287/1694] #211 remove megacheck #211 remove megacheck --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9675d5e26..163aba0b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ env: secure: QPkcX77j8QEqTwOYyLGItqvxYwE6Na5WaSZWjmhp48OlxYatWRHxJBwcFYSn1OWD5FMn+3oW39fHknReIxtrnhXMaNvI7x3/0gy4zujD/xZ2xAg7NsQ+l5buvEFO8/LEwwo0fp4knItFcBv8xH/ziJBJyXvgfMtj7Is4Q/pB1p6pWDdVy1vtAj3zH02bcqh1yXXS3HvcD8UhTszfU017gVNXDN1ow0rp1L3ainr3btrVK9izUxZfKvb7PlWJO1ogah7xNr/dIOJLsx2SfKgzKp+3H28L2WegtbzON74Op4jXvRywCwqjmUt/nwJ/Y9anunMNHT136h+ye4ziG1i/VdbWq0Q4PopQ8yYqinujG7SjfQio+wNCV2cwc2r/WjNBjbH0N9/Pflogq3RHvgy/9VtPif1tY+RrZCSntohoEZbYpVcFQFE1xDyf6xq/uLxVeEcCU33gqq7cKEfpcUgyCITa+yCPfBdtgkLBJ8h7Sew1j08D1kTKUW6g3D1epmwlCh/Z16oHG5VwSnCLGDjJy8wm/hQk1i/g7qeP7g24CfNzffzlFBCy88HhjzmrhUpcaTyfVVDf4h8wK6Zu/J3dHjHXQYwfiQRqpMa+2DYyjGgZhniccuh4GWolGZauDQdmO9SD4Ugyt9PEMk02i32ax3A4XE/Q6VNOam+qszviX3Q= before_install: - go get github.com/mattn/goveralls -- go get -u honnef.co/go/tools/cmd/megacheck - go get -u github.com/client9/misspell/cmd/misspell - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo @@ -17,7 +16,6 @@ before_script: script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES -- megacheck $GO_FILES - misspell -error -locale US . - gocyclo -over 19 $GO_FILES - golint -set_exit_status $GO_FILES From e559bfa9009b6ea2b83876afa6bf6219d538c70d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 29 Jun 2018 23:02:56 -0400 Subject: [PATCH 0288/1694] #211 patseStringField Updates #211 patseStringField Updates --- addenda10.go | 3 ++- addenda13.go | 2 +- addenda14.go | 2 +- addenda15.go | 2 +- validators.go | 2 ++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/addenda10.go b/addenda10.go index c220caa21..e355b0d87 100644 --- a/addenda10.go +++ b/addenda10.go @@ -68,7 +68,8 @@ func (addenda10 *Addenda10) Parse(record string) { // 07-24 Payment Amount For inbound IAT payments this field should contain the USD amount or may be blank. addenda10.ForeignPaymentAmount = addenda10.parseNumField(record[06:24]) // 25-46 Insert blanks or zeros - addenda10.ForeignTraceNumber = addenda10.parseStringField(record[24:46]) + //addenda10.ForeignTraceNumber = addenda10.parseStringField(record[24:46]) + addenda10.ForeignTraceNumber = record[24:46] // 47-81 Receiving Company Name/Individual Name addenda10.Name = record[46:81] // 82-87 reserved - Leave blank diff --git a/addenda13.go b/addenda13.go index 874221db0..adede0dc8 100644 --- a/addenda13.go +++ b/addenda13.go @@ -79,7 +79,7 @@ func (addenda13 *Addenda13) Parse(record string) { // 39-40 ODFIIDNumberQualifier addenda13.ODFIIDNumberQualifier = record[38:40] // 41-74 ODFIIdentification - addenda13.ODFIIdentification = record[40:74] + addenda13.ODFIIdentification = addenda13.parseStringField(record[40:74]) // 75-77 addenda13.ODFIBranchCountryCode = record[74:77] // 78-87 reserved - Leave blank diff --git a/addenda14.go b/addenda14.go index 38cbc7299..f7874c622 100644 --- a/addenda14.go +++ b/addenda14.go @@ -75,7 +75,7 @@ func (addenda14 *Addenda14) Parse(record string) { // 39-40 RDFIIDNumberQualifier addenda14.RDFIIDNumberQualifier = record[38:40] // 41-74 RDFIIdentification - addenda14.RDFIIdentification = record[40:74] + addenda14.RDFIIdentification = addenda14.parseStringField(record[40:74]) // 75-77 addenda14.RDFIBranchCountryCode = record[74:77] // 78-87 reserved - Leave blank diff --git a/addenda15.go b/addenda15.go index dd5388e23..4548c15b3 100644 --- a/addenda15.go +++ b/addenda15.go @@ -55,7 +55,7 @@ func (addenda15 *Addenda15) Parse(record string) { // 2-3 Always 15 addenda15.typeCode = record[1:3] // 4-18 - addenda15.ReceiverIDNumber = record[3:18] + addenda15.ReceiverIDNumber = addenda15.parseStringField(record[3:18]) // 19-53 addenda15.ReceiverStreetAddress = record[18:53] // 54-87 reserved - Leave blank diff --git a/validators.go b/validators.go index e8263d902..b2d3abfda 100644 --- a/validators.go +++ b/validators.go @@ -48,6 +48,8 @@ type FieldError struct { Msg string // context of the error. } +// ToDo: Add state and country look-up or use a 3rd party look up -verify with Wade + // Error message is constructed // FieldName Msg Value // Example1: BatchCount $% has none alphanumeric characters From 1126bf802caea94bc3cc8ba7d6089ff230cb568e Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sat, 7 Jul 2018 12:05:03 -0500 Subject: [PATCH 0289/1694] Batch enpoint start --- cmd/server/main.go | 1 + server/endpoints.go | 43 ++++++++++++++++++++------- server/middlewear.go | 11 ++++++- server/repositoryInMemory.go | 11 +++++++ server/service.go | 56 +++++++++++++++++++++++++++++------- server/service_test.go | 31 ++++++++++++++++++-- server/transport.go | 30 +++++++++++++++++-- test/ach-cie-read/main.go | 3 +- 8 files changed, 158 insertions(+), 28 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 0a6124f37..2142e9939 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -15,6 +15,7 @@ import ( /** CreateFile curl -d '{"id":"1234"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/ +curl -d '{"id":"08B751B2","immediateDestination":"9876543210", "immediateDestinationName":"Federal Reserve Bank", "immediateOrigin":"1234567890","immediateOriginName":"My Bank Name"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/ GetFile curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/1234 diff --git a/server/endpoints.go b/server/endpoints.go index cbe80c5d9..b002724df 100644 --- a/server/endpoints.go +++ b/server/endpoints.go @@ -8,18 +8,20 @@ import ( ) type Endpoints struct { - CreateFileEndpoint endpoint.Endpoint - GetFileEndpoint endpoint.Endpoint - GetFilesEndpoint endpoint.Endpoint - DeleteFileEndpoint endpoint.Endpoint + CreateFileEndpoint endpoint.Endpoint + GetFileEndpoint endpoint.Endpoint + GetFilesEndpoint endpoint.Endpoint + DeleteFileEndpoint endpoint.Endpoint + CreateBatchEndpoint endpoint.Endpoint } func MakeServerEndpoints(s Service) Endpoints { return Endpoints{ - CreateFileEndpoint: MakeCreateFileEndpoint(s), - GetFileEndpoint: MakeGetFileEndpoint(s), - GetFilesEndpoint: MakeGetFilesEndpoint(s), - DeleteFileEndpoint: MakeDeleteFileEndpoint(s), + CreateFileEndpoint: MakeCreateFileEndpoint(s), + GetFileEndpoint: MakeGetFileEndpoint(s), + GetFilesEndpoint: MakeGetFilesEndpoint(s), + DeleteFileEndpoint: MakeDeleteFileEndpoint(s), + CreateBatchEndpoint: MakeCreateBatchEndpoint(s), } } @@ -27,13 +29,13 @@ func MakeServerEndpoints(s Service) Endpoints { func MakeCreateFileEndpoint(s Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(createFileRequest) - id, e := s.CreateFile(req.File) + id, e := s.CreateFile(req.FileHeader) return createFileResponse{ID: id, Err: e}, nil } } type createFileRequest struct { - File ach.File + FileHeader ach.FileHeader } type createFileResponse struct { @@ -95,3 +97,24 @@ type deleteFileResponse struct { } func (r deleteFileResponse) error() error { return r.Err } + +//** Batches ** // + +// MakeCreateFileEndpoint returns an endpoint via the passed service. +func MakeCreateBatchEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(createBatchRequest) + id, e := s.CreateBatch(req.FileID, req.BatchHeader) + return createBatchResponse{ID: id, Err: e}, nil + } +} + +type createBatchRequest struct { + FileID string + BatchHeader ach.BatchHeader +} + +type createBatchResponse struct { + ID string `json:"id,omitempty"` + Err error `json:"err,omitempty"` +} diff --git a/server/middlewear.go b/server/middlewear.go index 2d2e6ac14..a3131d5ed 100644 --- a/server/middlewear.go +++ b/server/middlewear.go @@ -24,7 +24,7 @@ type loggingMiddleware struct { logger log.Logger } -func (mw loggingMiddleware) CreateFile(f ach.File) (id string, err error) { +func (mw loggingMiddleware) CreateFile(f ach.FileHeader) (id string, err error) { defer func(begin time.Time) { mw.logger.Log("method", "CreateFile", "id", f.ID, "took", time.Since(begin), "err", err) }(time.Now()) @@ -51,3 +51,12 @@ func (mw loggingMiddleware) DeleteFile(id string) (err error) { }(time.Now()) return mw.next.DeleteFile(id) } + +//** BATCHES ** // + +func (mw loggingMiddleware) CreateBatch(fileID string, bh ach.BatchHeader) (id string, err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "CreateBatch", "FileID", fileID, "id", bh.ID, "took", time.Since(begin), "err", err) + }(time.Now()) + return mw.next.CreateBatch(fileID, bh) +} diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go index bb583c636..18fac642a 100644 --- a/server/repositoryInMemory.go +++ b/server/repositoryInMemory.go @@ -56,3 +56,14 @@ func (r *repositoryInMemory) DeleteFile(id string) error { delete(r.files, id) return nil } + +func (r *repositoryInMemory) StoreBatch(fileID string, batch ach.Batcher) error { + r.mtx.Lock() + defer r.mtx.Unlock() + if _, ok := r.files[fileID]; !ok { + return ErrNotFound + } + // range batches to see if batch already exist and error if it does + r.files[fileID].AddBatch(batch) + return nil +} diff --git a/server/service.go b/server/service.go index 39ba9f026..195284096 100644 --- a/server/service.go +++ b/server/service.go @@ -17,35 +17,48 @@ var ( // TODO: Add ctx to function parameters to pass the client security token type Service interface { // CreateFile creates a new ach file record and returns a resource ID - CreateFile(f ach.File) (string, error) + CreateFile(f ach.FileHeader) (string, error) // AddFile retrieves a file based on the File id GetFile(id string) (ach.File, error) // GetFiles retrieves all files accessible from the client. GetFiles() []ach.File - // DeleteFile takes a file resource ID and deletes it from the repository + // DeleteFile takes a file resource ID and deletes it from the store DeleteFile(id string) error // UpdateFile updates the changes properties of a matching File ID // UpdateFile(f ach.File) (string, error) + + // CreateBatch creates a new batch within and ach file and returns its resource ID + CreateBatch(fileID string, bh ach.BatchHeader) (string, error) } // service a concrete implementation of the service. type service struct { - repository Repository + store Repository } // NewService creates a new concrete service func NewService(r Repository) Service { return &service{ - repository: r, + store: r, } } // CreateFile add a file to storage -func (s *service) CreateFile(f ach.File) (string, error) { - if f.ID == "" { - f.ID = NextID() +func (s *service) CreateFile(fh ach.FileHeader) (string, error) { + // create a new file + f := ach.NewFile() + f.SetHeader(fh) + // set resource id's + if fh.ID == "" { + id := NextID() + f.ID = id + f.Header.ID = id + f.Control.ID = id + } else { + f.ID = fh.ID + f.Control.ID = fh.ID } - if err := s.repository.StoreFile(&f); err != nil { + if err := s.store.StoreFile(f); err != nil { return "", err } return f.ID, nil @@ -53,7 +66,7 @@ func (s *service) CreateFile(f ach.File) (string, error) { // GetFile returns a files based on the supplied id func (s *service) GetFile(id string) (ach.File, error) { - f, err := s.repository.FindFile(id) + f, err := s.store.FindFile(id) if err != nil { return ach.File{}, ErrNotFound } @@ -62,14 +75,34 @@ func (s *service) GetFile(id string) (ach.File, error) { func (s *service) GetFiles() []ach.File { var result []ach.File - for _, f := range s.repository.FindAllFiles() { + for _, f := range s.store.FindAllFiles() { result = append(result, *f) } return result } func (s *service) DeleteFile(id string) error { - return s.repository.DeleteFile(id) + return s.store.DeleteFile(id) +} + +func (s *service) CreateBatch(fileID string, bh ach.BatchHeader) (string, error) { + batch, err := ach.NewBatch(&bh) + if err != nil { + return bh.ID, err + } + if bh.ID == "" { + id := NextID() + batch.ID = id + batch.GetHeader().ID = id + batch.GetControl().ID = id + } else { + batch.ID = bh.ID + batch.GetControl().ID = bh.ID + } + if err := s.store.StoreBatch(fileID, batch); err != nil { + return "", err + } + return bh.ID, nil } // Repository concrete implementations @@ -81,6 +114,7 @@ type Repository interface { FindFile(id string) (*ach.File, error) FindAllFiles() []*ach.File DeleteFile(id string) error + StoreBatch(fileID string, batch ach.Batcher) error } // Utility Functions diff --git a/server/service_test.go b/server/service_test.go index 23fb333dd..f6aa8386e 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -15,14 +15,14 @@ func mockServiceInMemory() Service { // CreateFile tests func TestCreateFile(t *testing.T) { s := mockServiceInMemory() - id, err := s.CreateFile(ach.File{ID: "12345"}) + id, err := s.CreateFile(ach.FileHeader{ID: "12345"}) if id != "12345" { t.Errorf("expected %s received %s w/ error %s", "12345", id, err) } } func TestCreateFileIDExists(t *testing.T) { s := mockServiceInMemory() - id, err := s.CreateFile(ach.File{ID: "98765"}) + id, err := s.CreateFile(ach.FileHeader{ID: "98765"}) if err != ErrAlreadyExists { t.Errorf("expected %s received %s w/ error %s", "ErrAlreadyExists", id, err) } @@ -30,7 +30,7 @@ func TestCreateFileIDExists(t *testing.T) { func TestCreateFileNoID(t *testing.T) { s := mockServiceInMemory() - id, err := s.CreateFile(*ach.NewFile()) + id, err := s.CreateFile(ach.NewFileHeader()) if len(id) < 3 { t.Errorf("expected %s received %s w/ error %s", "NextID", id, err) } @@ -77,3 +77,28 @@ func TestDeleteFile(t *testing.T) { t.Errorf("expected %s received %s", "ErrNotFound", err) } } + +func mockBatchHeaderWeb() ach.BatchHeader { + bh := ach.BatchHeader{} + bh.ID = "54321" + bh.StandardEntryClassCode = "WEB" + bh.CompanyName = "Your Company, inc" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "Online Order" + bh.ODFIIdentification = "12104288" + return bh + +} +func TestCreateBatch(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateBatch("98765", mockBatchHeaderWeb()) + if id != "54321" { + t.Errorf("expected %s received %s w/ error %s", "54321", id, err) + } +} + +// test adding a batch to a file that doesn't exist +// test adding a batch without an ID. Make sure Batch Header, Batch Control, and Batch have the same ID +// test delete a batch w/ ID +// test get a batch w/ id +// test get all batches for a file diff --git a/server/transport.go b/server/transport.go index 501ed88d8..51d674147 100644 --- a/server/transport.go +++ b/server/transport.go @@ -35,6 +35,8 @@ func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { // GET /files/ retrieves a list of all file's // GET /files/:id retrieves the given file by id // DELETE /files/:id delete a file based on supplied id + + // POST /files/:id/batches/ Create a Batch // *** // GET /files/:id/validate validates the supplied file id for nacha compliance // PATCH /files/:id/build build batch and file controls in ach file with supplied values @@ -64,14 +66,21 @@ func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { encodeResponse, options..., )) + r.Methods("POST").Path("/files/{fileID}/batches/").Handler(httptransport.NewServer( + e.CreateBatchEndpoint, + decodeCreateBatchRequest, + encodeResponse, + options..., + )) return r } +//** FILES ** // func decodeCreateFileRequest(_ context.Context, r *http.Request) (request interface{}, err error) { var req createFileRequest // Sets default values - req.File = *ach.NewFile() - if e := json.NewDecoder(r.Body).Decode(&req.File); e != nil { + req.FileHeader = ach.NewFileHeader() + if e := json.NewDecoder(r.Body).Decode(&req.FileHeader); e != nil { return nil, e } return req, nil @@ -110,6 +119,23 @@ func encodeGetFileRequest(ctx context.Context, req *http.Request, request interf return encodeRequest(ctx, req, request) } +//** BATCHES **// + +func decodeCreateBatchRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + var req createBatchRequest + vars := mux.Vars(r) + id, ok := vars["fileID"] + if !ok { + return nil, ErrBadRouting + } + req.FileID = id + req.BatchHeader = *ach.NewBatchHeader() + if e := json.NewDecoder(r.Body).Decode(&req.BatchHeader); e != nil { + return nil, e + } + return req, nil +} + // errorer is implemented by all concrete response types that may contain // errors. It allows us to change the HTTP response code without needing to // trigger an endpoint (transport-level) error. For more information, read the diff --git a/test/ach-cie-read/main.go b/test/ach-cie-read/main.go index 8a4d14801..25116384a 100644 --- a/test/ach-cie-read/main.go +++ b/test/ach-cie-read/main.go @@ -2,9 +2,10 @@ package main import ( "fmt" - "github.com/bkmoovio/ach" "log" "os" + + "github.com/moov-io/ach" ) func main() { From 33d5519e1ebdbb4369a6459b2cda6307bda601e8 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sat, 7 Jul 2018 12:21:06 -0500 Subject: [PATCH 0290/1694] Batch ID getter/setter to interface --- batch.go | 12 +++++++++++- batcher.go | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/batch.go b/batch.go index 1fe6c3d6d..e000b2474 100644 --- a/batch.go +++ b/batch.go @@ -13,7 +13,7 @@ import ( // Batch holds the Batch Header and Batch Control and all Entry Records type batch struct { // ID is a client defined string used as a reference to this record. - ID string `json:"id"` + id string `json:"id"` Header *BatchHeader `json:"batchHeader,omitempty"` Entries []*EntryDetail `json:"entryDetails,omitempty"` Control *BatchControl `json:"batchControl,omitempty"` @@ -211,6 +211,16 @@ func (batch *batch) Category() string { return batch.category } +// ID returns the id of the batch +func (batch *batch) ID() string { + return batch.id +} + +// SetID sets the batch id +func (batch *batch) SetID(id string) { + batch.id = id +} + // isFieldInclusion iterates through all the records in the batch and verifies against default fields func (batch *batch) isFieldInclusion() error { if err := batch.Header.Validate(); err != nil { diff --git a/batcher.go b/batcher.go index a68894d22..ca541b2d9 100644 --- a/batcher.go +++ b/batcher.go @@ -25,6 +25,8 @@ type Batcher interface { AddEntry(*EntryDetail) Create() error Validate() error + SetID(string) + ID() string // Category defines if a Forward or Return Category() string } From 0554d450c7afc8a3ead25563ebf47fecc5ca093b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sat, 7 Jul 2018 12:21:43 -0500 Subject: [PATCH 0291/1694] Fix import of test --- test/ach-cie-read/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/ach-cie-read/main.go b/test/ach-cie-read/main.go index 8a4d14801..25116384a 100644 --- a/test/ach-cie-read/main.go +++ b/test/ach-cie-read/main.go @@ -2,9 +2,10 @@ package main import ( "fmt" - "github.com/bkmoovio/ach" "log" "os" + + "github.com/moov-io/ach" ) func main() { From dc6c8bad45c2777eceb86fa8be89cad969fed3a0 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sat, 7 Jul 2018 12:23:46 -0500 Subject: [PATCH 0292/1694] Export batch Id for json marshalling --- batch.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/batch.go b/batch.go index e000b2474..177596af2 100644 --- a/batch.go +++ b/batch.go @@ -13,7 +13,7 @@ import ( // Batch holds the Batch Header and Batch Control and all Entry Records type batch struct { // ID is a client defined string used as a reference to this record. - id string `json:"id"` + Id string `json:"id"` Header *BatchHeader `json:"batchHeader,omitempty"` Entries []*EntryDetail `json:"entryDetails,omitempty"` Control *BatchControl `json:"batchControl,omitempty"` @@ -213,12 +213,12 @@ func (batch *batch) Category() string { // ID returns the id of the batch func (batch *batch) ID() string { - return batch.id + return batch.Id } // SetID sets the batch id func (batch *batch) SetID(id string) { - batch.id = id + batch.Id = id } // isFieldInclusion iterates through all the records in the batch and verifies against default fields From f330eb4719fd85b1b6278116c626adc13251ae16 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sat, 7 Jul 2018 13:03:57 -0500 Subject: [PATCH 0293/1694] template batch --- server/service_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/service_test.go b/server/service_test.go index f6aa8386e..7797504ec 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -91,12 +91,21 @@ func mockBatchHeaderWeb() ach.BatchHeader { } func TestCreateBatch(t *testing.T) { s := mockServiceInMemory() + //b.Header = mockBatchHeaderWeb() id, err := s.CreateBatch("98765", mockBatchHeaderWeb()) if id != "54321" { t.Errorf("expected %s received %s w/ error %s", "54321", id, err) } } +type Batch struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + Header *ach.BatchHeader `json:"batchHeader,omitempty"` + Entries []*ach.EntryDetail `json:"entryDetails,omitempty"` + Control *ach.BatchControl `json:"batchControl,omitempty"` +} + // test adding a batch to a file that doesn't exist // test adding a batch without an ID. Make sure Batch Header, Batch Control, and Batch have the same ID // test delete a batch w/ ID From 6f7188ff21fc483ea0378cf1180a88f4d9fabe51 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sat, 7 Jul 2018 13:05:22 -0500 Subject: [PATCH 0294/1694] Make id private w/ setter --- batch.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/batch.go b/batch.go index 177596af2..6eae8d548 100644 --- a/batch.go +++ b/batch.go @@ -12,8 +12,8 @@ import ( // Batch holds the Batch Header and Batch Control and all Entry Records type batch struct { - // ID is a client defined string used as a reference to this record. - Id string `json:"id"` + // id is a client defined string used as a reference to this record. accessed via ID/SetID + id string Header *BatchHeader `json:"batchHeader,omitempty"` Entries []*EntryDetail `json:"entryDetails,omitempty"` Control *BatchControl `json:"batchControl,omitempty"` @@ -213,12 +213,12 @@ func (batch *batch) Category() string { // ID returns the id of the batch func (batch *batch) ID() string { - return batch.Id + return batch.id } // SetID sets the batch id func (batch *batch) SetID(id string) { - batch.Id = id + batch.id = id } // isFieldInclusion iterates through all the records in the batch and verifies against default fields From 2cda6019578592684ef794be404b6bbbf4f61c7b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 8 Jul 2018 12:42:25 -0500 Subject: [PATCH 0295/1694] import reference to moov-io --- test/ach-cie-write/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/ach-cie-write/main.go b/test/ach-cie-write/main.go index fc9f8ffc6..c8e98e1fd 100644 --- a/test/ach-cie-write/main.go +++ b/test/ach-cie-write/main.go @@ -1,10 +1,11 @@ package main import ( - "github.com/bkmoovio/ach" "log" "os" "time" + + "github.com/moov-io/ach" ) func main() { From db48f5d0d4004d492d4cb14f38f04133a2e28931 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 8 Jul 2018 13:34:18 -0500 Subject: [PATCH 0296/1694] Mock service enpoints --- server/mock_test.go | 71 ++++++++++++++++++++++++++++++++++++ server/repositoryInMemory.go | 2 +- server/service.go | 5 ++- server/service_test.go | 35 ++++++------------ 4 files changed, 86 insertions(+), 27 deletions(-) create mode 100644 server/mock_test.go diff --git a/server/mock_test.go b/server/mock_test.go new file mode 100644 index 000000000..9d9aeda6e --- /dev/null +++ b/server/mock_test.go @@ -0,0 +1,71 @@ +package server + +import ( + "time" + + "github.com/moov-io/ach" +) + +func mockServiceInMemory() Service { + repository := NewRepositoryInMemory() + repository.StoreFile(&ach.File{ID: "98765"}) + repository.StoreBatch("98765", mockBatchWEB()) + return NewService(repository) +} + +func mockFileHeader() ach.FileHeader { + fh := ach.NewFileHeader() + fh.ID = "12345" + fh.ImmediateDestination = "9876543210" + fh.ImmediateOrigin = "1234567890" + fh.FileCreationDate = time.Now() + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + return fh +} + +func mockBatchHeaderWeb() *ach.BatchHeader { + bh := ach.NewBatchHeader() + bh.ID = "54321" + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "WEB" + bh.CompanyName = "Your Company, inc" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "Online Order" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockWEBEntryDetail creates a WEB entry detail +func mockWEBEntryDetail() *ach.EntryDetail { + entry := ach.NewEntryDetail() + entry.ID = "98765" + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "123456789" + entry.Amount = 100000000 + entry.IndividualName = "Wade Arnold" + entry.SetTraceNumber(mockBatchHeaderWeb().ODFIIdentification, 1) + entry.SetPaymentType("S") + return entry +} + +// mockBatchWEB creates a WEB batch +func mockBatchWEB() *ach.BatchWEB { + mockBatch := ach.NewBatchWEB(mockBatchHeaderWeb()) + mockBatch.SetID(mockBatch.Header.ID) + mockBatch.AddEntry(mockWEBEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + if err := mockBatch.Create(); err != nil { + panic(err) + } + return mockBatch +} + +func mockAddenda05() *ach.Addenda05 { + addenda05 := ach.NewAddenda05() + addenda05.ID = "56789" + addenda05.SequenceNumber = 1 + addenda05.EntryDetailSequenceNumber = 0000001 + return addenda05 +} diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go index 18fac642a..dbb3a21bf 100644 --- a/server/repositoryInMemory.go +++ b/server/repositoryInMemory.go @@ -63,7 +63,7 @@ func (r *repositoryInMemory) StoreBatch(fileID string, batch ach.Batcher) error if _, ok := r.files[fileID]; !ok { return ErrNotFound } - // range batches to see if batch already exist and error if it does + // TODO range batches to see if batch already exist and error if it does r.files[fileID].AddBatch(batch) return nil } diff --git a/server/service.go b/server/service.go index 195284096..45273c925 100644 --- a/server/service.go +++ b/server/service.go @@ -92,13 +92,14 @@ func (s *service) CreateBatch(fileID string, bh ach.BatchHeader) (string, error) } if bh.ID == "" { id := NextID() - batch.ID = id + batch.SetID(id) batch.GetHeader().ID = id batch.GetControl().ID = id } else { - batch.ID = bh.ID + batch.SetID(bh.ID) batch.GetControl().ID = bh.ID } + if err := s.store.StoreBatch(fileID, batch); err != nil { return "", err } diff --git a/server/service_test.go b/server/service_test.go index 7797504ec..5c0ea1010 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -6,16 +6,12 @@ import ( "github.com/moov-io/ach" ) -func mockServiceInMemory() Service { - repository := NewRepositoryInMemory() - repository.StoreFile(&ach.File{ID: "98765"}) - return NewService(repository) -} +// test mocks are in mock_test.go // CreateFile tests func TestCreateFile(t *testing.T) { s := mockServiceInMemory() - id, err := s.CreateFile(ach.FileHeader{ID: "12345"}) + id, err := s.CreateFile(mockFileHeader()) if id != "12345" { t.Errorf("expected %s received %s w/ error %s", "12345", id, err) } @@ -78,33 +74,24 @@ func TestDeleteFile(t *testing.T) { } } -func mockBatchHeaderWeb() ach.BatchHeader { - bh := ach.BatchHeader{} - bh.ID = "54321" - bh.StandardEntryClassCode = "WEB" - bh.CompanyName = "Your Company, inc" - bh.CompanyIdentification = "121042882" - bh.CompanyEntryDescription = "Online Order" - bh.ODFIIdentification = "12104288" - return bh - -} +/** func TestCreateBatch(t *testing.T) { s := mockServiceInMemory() //b.Header = mockBatchHeaderWeb() - id, err := s.CreateBatch("98765", mockBatchHeaderWeb()) + id, err := s.CreateBatch("98765", *mockBatchHeaderWeb()) if id != "54321" { t.Errorf("expected %s received %s w/ error %s", "54321", id, err) } } -type Batch struct { - // ID is a client defined string used as a reference to this record. - ID string `json:"id"` - Header *ach.BatchHeader `json:"batchHeader,omitempty"` - Entries []*ach.EntryDetail `json:"entryDetails,omitempty"` - Control *ach.BatchControl `json:"batchControl,omitempty"` +func TestCreateBatchIDExists(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateBatch("98765", *mockBatchHeaderWeb()) + if id != "54321" { + t.Errorf("expected %s received %s w/ error %s", "54321", id, err) + } } +**/ // test adding a batch to a file that doesn't exist // test adding a batch without an ID. Make sure Batch Header, Batch Control, and Batch have the same ID From c8d3e88acf6cf3ddd233290484e6facbf8dee2a3 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 8 Jul 2018 14:06:25 -0500 Subject: [PATCH 0297/1694] GetBatch completed --- server/repositoryInMemory.go | 17 ++++++++- server/service.go | 12 ------- server/service_test.go | 70 +++++++++++++++++++++++++++++------- 3 files changed, 73 insertions(+), 26 deletions(-) diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go index dbb3a21bf..5500f366c 100644 --- a/server/repositoryInMemory.go +++ b/server/repositoryInMemory.go @@ -7,6 +7,14 @@ import ( "github.com/moov-io/ach" ) +// Repository is the Service storage mechanism abstraction +type Repository interface { + StoreFile(file *ach.File) error + FindFile(id string) (*ach.File, error) + FindAllFiles() []*ach.File + DeleteFile(id string) error + StoreBatch(fileID string, batch ach.Batcher) error +} type repositoryInMemory struct { mtx sync.RWMutex files map[string]*ach.File @@ -60,10 +68,17 @@ func (r *repositoryInMemory) DeleteFile(id string) error { func (r *repositoryInMemory) StoreBatch(fileID string, batch ach.Batcher) error { r.mtx.Lock() defer r.mtx.Unlock() + // Ensure the file does not already exist if _, ok := r.files[fileID]; !ok { return ErrNotFound } - // TODO range batches to see if batch already exist and error if it does + // ensure the batch does not already exist + for _, val := range r.files[fileID].Batches { + if val.ID() == batch.ID() { + return ErrAlreadyExists + } + } + // Add the batch to the file r.files[fileID].AddBatch(batch) return nil } diff --git a/server/service.go b/server/service.go index 45273c925..0ac2b7d8c 100644 --- a/server/service.go +++ b/server/service.go @@ -106,18 +106,6 @@ func (s *service) CreateBatch(fileID string, bh ach.BatchHeader) (string, error) return bh.ID, nil } -// Repository concrete implementations -// ******** - -// Repository is the Service storage mechanism abstraction -type Repository interface { - StoreFile(file *ach.File) error - FindFile(id string) (*ach.File, error) - FindAllFiles() []*ach.File - DeleteFile(id string) error - StoreBatch(fileID string, batch ach.Batcher) error -} - // Utility Functions // ***** diff --git a/server/service_test.go b/server/service_test.go index 5c0ea1010..e3e227d2e 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -74,27 +74,71 @@ func TestDeleteFile(t *testing.T) { } } -/** +// Service.CreateBatch tests + +// TestCreateBatch tests creating a new batch when file.ID exists and batch.id does not exist func TestCreateBatch(t *testing.T) { s := mockServiceInMemory() - //b.Header = mockBatchHeaderWeb() - id, err := s.CreateBatch("98765", *mockBatchHeaderWeb()) - if id != "54321" { - t.Errorf("expected %s received %s w/ error %s", "54321", id, err) + bh := mockBatchHeaderWeb() + bh.ID = "11111" + id, err := s.CreateBatch("98765", *bh) + if id != "11111" { + t.Errorf("expected %s received %s w/ error %s", "11111", id, err) } } +// TestCreateBatchIDExists Create a new batch with batch.id already present. Should fail. func TestCreateBatchIDExists(t *testing.T) { s := mockServiceInMemory() id, err := s.CreateBatch("98765", *mockBatchHeaderWeb()) - if id != "54321" { - t.Errorf("expected %s received %s w/ error %s", "54321", id, err) + if err != ErrAlreadyExists { + t.Errorf("expected %s received %s w/ error %s", "ErrAlreadyExists", id, err) + } +} + +// TestCreateBatchFileIDExits create a batch when the file.id does not exist. Should fail. +func TestCreateBatchFileIDExits(t *testing.T) { + s := mockServiceInMemory() + id, err := s.CreateBatch("55555", *mockBatchHeaderWeb()) + if err != ErrNotFound { + t.Errorf("expected %s received %s w/ error %s", "ErrNotFound", id, err) + } + +} + +// TestCreateBatchIDBank create a new batch when the batch.id is nil but file.id is valid. Should generate batch.id and save. +func TestCreateBatchIDBlank(t *testing.T) { + s := mockServiceInMemory() + bh := mockBatchHeaderWeb() + bh.ID = "" + id, err := s.CreateBatch("98765", *bh) + if len(id) < 3 { + t.Errorf("expected %s received %s w/ error %s", "NextID", id, err) } } -**/ -// test adding a batch to a file that doesn't exist -// test adding a batch without an ID. Make sure Batch Header, Batch Control, and Batch have the same ID -// test delete a batch w/ ID -// test get a batch w/ id -// test get all batches for a file +// Service.GetBatch + +// TestGetBatch return a batch for the existing file.id and batch.id +func TestGetBatch(t *testing.T) { + +} + +// TestGetBatchNotFound return a failure if the batch.id is not found +func TestGetBatchNotFound(t *testing.T) { + +} + +// Service.GetBatches + +// TestGetBatches return a list of batches for the supplied file.id +func TestGetBatches(t *testing.T) { + +} + +// Service.DeleteBatch + +// TestDeleteBatch removes a batch with existing file and batch id. +func TestDeleteBatch(t *testing.T) { + +} From 4dad44f14a4eb0f85053330bba3631dc842d25c9 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 8 Jul 2018 14:29:07 -0500 Subject: [PATCH 0298/1694] add store.findBatch & service.GetBatch --- server/middlewear.go | 7 +++++++ server/repositoryInMemory.go | 13 +++++++++++++ server/service.go | 10 ++++++++++ server/service_test.go | 12 ++++++++++-- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/server/middlewear.go b/server/middlewear.go index a3131d5ed..01e734744 100644 --- a/server/middlewear.go +++ b/server/middlewear.go @@ -60,3 +60,10 @@ func (mw loggingMiddleware) CreateBatch(fileID string, bh ach.BatchHeader) (id s }(time.Now()) return mw.next.CreateBatch(fileID, bh) } + +func (mw loggingMiddleware) GetBatch(fileID string, batchID string) (b ach.Batcher, err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "GetBatch", "fileID", fileID, "batchID", batchID, "took", time.Since(begin), "err", err) + }(time.Now()) + return mw.next.GetBatch(fileID, batchID) +} diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go index 5500f366c..16296331d 100644 --- a/server/repositoryInMemory.go +++ b/server/repositoryInMemory.go @@ -14,6 +14,7 @@ type Repository interface { FindAllFiles() []*ach.File DeleteFile(id string) error StoreBatch(fileID string, batch ach.Batcher) error + FindBatch(fileID string, batchID string) (*ach.Batcher, error) } type repositoryInMemory struct { mtx sync.RWMutex @@ -82,3 +83,15 @@ func (r *repositoryInMemory) StoreBatch(fileID string, batch ach.Batcher) error r.files[fileID].AddBatch(batch) return nil } + +// FindBatch retrieves a ach.Batcher based on the supplied ID +func (r *repositoryInMemory) FindBatch(fileID string, batchID string) (*ach.Batcher, error) { + r.mtx.RLock() + defer r.mtx.RUnlock() + for _, val := range r.files[fileID].Batches { + if val.ID() == batchID { + return &val, nil + } + } + return nil, ErrNotFound +} diff --git a/server/service.go b/server/service.go index 0ac2b7d8c..9dbcf1725 100644 --- a/server/service.go +++ b/server/service.go @@ -29,6 +29,8 @@ type Service interface { // CreateBatch creates a new batch within and ach file and returns its resource ID CreateBatch(fileID string, bh ach.BatchHeader) (string, error) + // GetBatch retrieves a batch based oin the file id and batch id + GetBatch(fileID string, batchID string) (ach.Batcher, error) } // service a concrete implementation of the service. @@ -106,6 +108,14 @@ func (s *service) CreateBatch(fileID string, bh ach.BatchHeader) (string, error) return bh.ID, nil } +func (s *service) GetBatch(fileID string, batchID string) (ach.Batcher, error) { + b, err := s.store.FindBatch(fileID, batchID) + if err != nil { + return nil, ErrNotFound + } + return *b, nil +} + // Utility Functions // ***** diff --git a/server/service_test.go b/server/service_test.go index e3e227d2e..dcba26104 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -121,12 +121,20 @@ func TestCreateBatchIDBlank(t *testing.T) { // TestGetBatch return a batch for the existing file.id and batch.id func TestGetBatch(t *testing.T) { - + s := mockServiceInMemory() + b, err := s.GetBatch("98765", "54321") + if b.ID() != "54321" { + t.Errorf("expected %s received %s w/ error %s", "54321", b.ID(), err) + } } // TestGetBatchNotFound return a failure if the batch.id is not found func TestGetBatchNotFound(t *testing.T) { - + s := mockServiceInMemory() + b, err := s.GetBatch("98765", "55555") + if err != ErrNotFound { + t.Errorf("expected %s received %s w/ error %s", "ErrNotFound", b.ID(), err) + } } // Service.GetBatches From 1cb6b2c52b80873552ffcb57cdbbf8bfbb08af0b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Sun, 8 Jul 2018 15:01:24 -0500 Subject: [PATCH 0299/1694] Support GetBatch in transport --- cmd/server/main.go | 11 ++++++++++- server/endpoints.go | 20 ++++++++++++++++++++ server/middlewear.go | 2 +- server/transport.go | 26 +++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 2142e9939..79ead5219 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -14,7 +14,6 @@ import ( /** CreateFile -curl -d '{"id":"1234"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/ curl -d '{"id":"08B751B2","immediateDestination":"9876543210", "immediateDestinationName":"Federal Reserve Bank", "immediateOrigin":"1234567890","immediateOriginName":"My Bank Name"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/ GetFile @@ -25,6 +24,16 @@ curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/ DeleteFile curl -H "Content-Type: application/json" -X DELETE http://localhost:8080/files/1234 + +CreateBatch +curl -d '{"id":"54321","serviceClassCode":"220","standardEntryClassCode":"WEB","companyName":"Your Company inc","companyIdentification":"121042882","companyEntryDescription":"Online Order","ODFIIdentification","12104288"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/08B751B2/batches/ + +GetBatch +curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/08B751B2/batches/54321 + +GetBatches + +DeleteBatch **/ func main() { diff --git a/server/endpoints.go b/server/endpoints.go index b002724df..454841e62 100644 --- a/server/endpoints.go +++ b/server/endpoints.go @@ -13,6 +13,7 @@ type Endpoints struct { GetFilesEndpoint endpoint.Endpoint DeleteFileEndpoint endpoint.Endpoint CreateBatchEndpoint endpoint.Endpoint + GetBatchEndpoint endpoint.Endpoint } func MakeServerEndpoints(s Service) Endpoints { @@ -22,6 +23,7 @@ func MakeServerEndpoints(s Service) Endpoints { GetFilesEndpoint: MakeGetFilesEndpoint(s), DeleteFileEndpoint: MakeDeleteFileEndpoint(s), CreateBatchEndpoint: MakeCreateBatchEndpoint(s), + GetBatchEndpoint: MakeGetBatchEndpoint(s), } } @@ -118,3 +120,21 @@ type createBatchResponse struct { ID string `json:"id,omitempty"` Err error `json:"err,omitempty"` } + +func MakeGetBatchEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(getBatchRequest) + batch, e := s.GetBatch(req.fileID, req.batchID) + return getBatchResponse{Batch: batch, Err: e}, nil + } +} + +type getBatchRequest struct { + fileID string + batchID string +} + +type getBatchResponse struct { + Batch ach.Batcher `json:"batch,omitempty"` + Err error `json:"err,omitempty"` +} diff --git a/server/middlewear.go b/server/middlewear.go index 01e734744..dd52122a1 100644 --- a/server/middlewear.go +++ b/server/middlewear.go @@ -56,7 +56,7 @@ func (mw loggingMiddleware) DeleteFile(id string) (err error) { func (mw loggingMiddleware) CreateBatch(fileID string, bh ach.BatchHeader) (id string, err error) { defer func(begin time.Time) { - mw.logger.Log("method", "CreateBatch", "FileID", fileID, "id", bh.ID, "took", time.Since(begin), "err", err) + mw.logger.Log("method", "CreateBatch", "FileID", fileID, "batchID", bh.ID, "took", time.Since(begin), "err", err) }(time.Now()) return mw.next.CreateBatch(fileID, bh) } diff --git a/server/transport.go b/server/transport.go index 51d674147..ba6f12485 100644 --- a/server/transport.go +++ b/server/transport.go @@ -36,7 +36,8 @@ func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { // GET /files/:id retrieves the given file by id // DELETE /files/:id delete a file based on supplied id - // POST /files/:id/batches/ Create a Batch + // POST /files/:id/batches/ Create a Batch + // GET /files/:id/batches/:id Retrieve the given batch by id // *** // GET /files/:id/validate validates the supplied file id for nacha compliance // PATCH /files/:id/build build batch and file controls in ach file with supplied values @@ -72,6 +73,12 @@ func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { encodeResponse, options..., )) + r.Methods("GET").Path("/files/{fileID}/batches/{batchID}").Handler(httptransport.NewServer( + e.GetBatchEndpoint, + decodeGetBatchRequest, + encodeResponse, + options..., + )) return r } @@ -136,6 +143,23 @@ func decodeCreateBatchRequest(_ context.Context, r *http.Request) (request inter return req, nil } +func decodeGetBatchRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + var req getBatchRequest + vars := mux.Vars(r) + fileID, ok := vars["fileID"] + if !ok { + return nil, ErrBadRouting + } + batchID, ok := vars["batchID"] + if !ok { + return nil, ErrBadRouting + } + + req.fileID = fileID + req.batchID = batchID + return req, nil +} + // errorer is implemented by all concrete response types that may contain // errors. It allows us to change the HTTP response code without needing to // trigger an endpoint (transport-level) error. For more information, read the From fc7341165d1ad2247d85f1a2845ca052e5d3b792 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 9 Jul 2018 22:37:20 -0400 Subject: [PATCH 0300/1694] # 211 Continue code support for IAT Removed IAT Batcher logic, just use IATBatch Extended initial logic in IATBatch to support Addenda 10-17 Added initialal test cases for IAtBatch Added Addenda17 and test cases fro Addenda17 Added Addenda10-17 properties to IATEntryDetail Modified Reader and Writer to use IATBatch instead of IATBatcher --- addenda10.go | 2 +- addenda10_test.go | 2 +- addenda11.go | 2 +- addenda11_test.go | 2 +- addenda12.go | 2 +- addenda12_test.go | 2 +- addenda13.go | 2 +- addenda13_test.go | 2 +- addenda14.go | 2 +- addenda14_test.go | 2 +- addenda15.go | 2 +- addenda15_test.go | 2 +- addenda16.go | 2 +- addenda16_test.go | 2 +- addenda17.go | 136 ++++++++++ addenda17_test.go | 186 +++++++++++++ batch.go | 6 +- batchIAT.go | 4 +- file.go | 4 +- iatBatch.go | 256 ++++++++++++------ iatBatch_test.go | 575 +++++++++++++++++++++++++++++++++++++++++ iatBatcher.go | 55 ---- iatEntryDetail.go | 10 +- iatEntryDetail_test.go | 4 +- reader.go | 9 +- writer.go | 34 ++- 26 files changed, 1143 insertions(+), 164 deletions(-) create mode 100644 addenda17.go create mode 100644 addenda17_test.go create mode 100644 iatBatch_test.go delete mode 100644 iatBatcher.go diff --git a/addenda10.go b/addenda10.go index e355b0d87..ee0afed8c 100644 --- a/addenda10.go +++ b/addenda10.go @@ -9,7 +9,7 @@ import ( "strconv" ) -// Addenda10 is a Addendumer addenda which provides business transaction information for Addenda Type +// Addenda10 is an addenda which provides business transaction information for Addenda Type // Code 10 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. // // Addenda10 is mandatory for IAT entries diff --git a/addenda10_test.go b/addenda10_test.go index 53efccf74..d101307b2 100644 --- a/addenda10_test.go +++ b/addenda10_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -// mockAddenda10() creates a mock Addenda10 record +// mockAddenda10 creates a mock Addenda10 record func mockAddenda10() *Addenda10 { addenda10 := NewAddenda10() addenda10.TransactionTypeCode = "ANN" diff --git a/addenda11.go b/addenda11.go index 1b3335269..98aec15ff 100644 --- a/addenda11.go +++ b/addenda11.go @@ -8,7 +8,7 @@ import ( "fmt" ) -// Addenda11 is a Addendumer addenda which provides business transaction information for Addenda Type +// Addenda11 is an addenda which provides business transaction information for Addenda Type // Code 11 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. // // Addenda11 is mandatory for IAT entries diff --git a/addenda11_test.go b/addenda11_test.go index 0e736a337..55867128d 100644 --- a/addenda11_test.go +++ b/addenda11_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -// mockAddenda11() creates a mock Addenda11 record +// mockAddenda11 creates a mock Addenda11 record func mockAddenda11() *Addenda11 { addenda11 := NewAddenda11() addenda11.OriginatorName = "BEK Solutions" diff --git a/addenda12.go b/addenda12.go index 6f79f0f7d..90bd946a7 100644 --- a/addenda12.go +++ b/addenda12.go @@ -8,7 +8,7 @@ import ( "fmt" ) -// Addenda12 is a Addendumer addenda which provides business transaction information for Addenda Type +// Addenda12 is an addenda which provides business transaction information for Addenda Type // Code 12 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. // // Addenda12 is mandatory for IAT entries diff --git a/addenda12_test.go b/addenda12_test.go index 6e099dd2e..1be3b1112 100644 --- a/addenda12_test.go +++ b/addenda12_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -// mockAddenda12() creates a mock Addenda12 record +// mockAddenda12 creates a mock Addenda12 record func mockAddenda12() *Addenda12 { addenda12 := NewAddenda12() addenda12.OriginatorCityStateProvince = "JacobsTown*PA\\" diff --git a/addenda13.go b/addenda13.go index adede0dc8..3cc20d77f 100644 --- a/addenda13.go +++ b/addenda13.go @@ -8,7 +8,7 @@ import ( "fmt" ) -// Addenda13 is a Addendumer addenda which provides business transaction information for Addenda Type +// Addenda13 is an addenda which provides business transaction information for Addenda Type // Code 13 in a machine readable format. It is usually formatted according to ANSI, ASC, X13 Standard. // // Addenda13 is mandatory for IAT entries diff --git a/addenda13_test.go b/addenda13_test.go index 73774bd25..7acced8c0 100644 --- a/addenda13_test.go +++ b/addenda13_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -// mockAddenda13() creates a mock Addenda13 record +// mockAddenda13 creates a mock Addenda13 record func mockAddenda13() *Addenda13 { addenda13 := NewAddenda13() addenda13.ODFIName = "Wells Fargo" diff --git a/addenda14.go b/addenda14.go index f7874c622..008fab489 100644 --- a/addenda14.go +++ b/addenda14.go @@ -8,7 +8,7 @@ import ( "fmt" ) -// Addenda14 is a Addendumer addenda which provides business transaction information for Addenda Type +// Addenda14 is an addenda which provides business transaction information for Addenda Type // Code 14 in a machine readable format. It is usually formatted according to ANSI, ASC, X14 Standard. // // Addenda14 is mandatory for IAT entries diff --git a/addenda14_test.go b/addenda14_test.go index 4f9db902e..511fde002 100644 --- a/addenda14_test.go +++ b/addenda14_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -// mockAddenda14() creates a mock Addenda14 record +// mockAddenda14 creates a mock Addenda14 record func mockAddenda14() *Addenda14 { addenda14 := NewAddenda14() addenda14.RDFIName = "Citadel Bank" diff --git a/addenda15.go b/addenda15.go index 4548c15b3..f32ac2419 100644 --- a/addenda15.go +++ b/addenda15.go @@ -8,7 +8,7 @@ import ( "fmt" ) -// Addenda15 is a Addendumer addenda which provides business transaction information for Addenda Type +// Addenda15 is an addenda which provides business transaction information for Addenda Type // Code 15 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. // // Addenda15 is mandatory for IAT entries diff --git a/addenda15_test.go b/addenda15_test.go index 125d755ab..07a44c10c 100644 --- a/addenda15_test.go +++ b/addenda15_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -// mockAddenda15() creates a mock Addenda15 record +// mockAddenda15 creates a mock Addenda15 record func mockAddenda15() *Addenda15 { addenda15 := NewAddenda15() addenda15.ReceiverIDNumber = "987465493213987" diff --git a/addenda16.go b/addenda16.go index b9de7d1fa..00c86c6e1 100644 --- a/addenda16.go +++ b/addenda16.go @@ -8,7 +8,7 @@ import ( "fmt" ) -// Addenda16 is a Addendumer addenda which provides business transaction information for Addenda Type +// Addenda16 is an addenda which provides business transaction information for Addenda Type // Code 16 in a machine readable format. It is usually formatted according to ANSI, ASC, X16 Standard. // // Addenda16 is mandatory for IAT entries diff --git a/addenda16_test.go b/addenda16_test.go index 9a8be7271..547b8ff88 100644 --- a/addenda16_test.go +++ b/addenda16_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -// mockAddenda16() creates a mock Addenda16 record +// mockAddenda16 creates a mock Addenda16 record func mockAddenda16() *Addenda16 { addenda16 := NewAddenda16() addenda16.ReceiverCityStateProvince = "LetterTown*CO\\" diff --git a/addenda17.go b/addenda17.go new file mode 100644 index 000000000..fe897f088 --- /dev/null +++ b/addenda17.go @@ -0,0 +1,136 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" + "strings" +) + +// Addenda17 is an addenda which provides business transaction information for Addenda Type +// Code 17 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// +// Addenda17 is optional for IAT entries +// +// The Addenda17 record identifies payment-related data. A maximum of two of these Addenda Records +// may be included with each IAT entry. +type Addenda17 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. entryAddenda17 Pos 7 + recordType string + // TypeCode Addenda17 types code '17' + typeCode string + // PaymentRelatedInformation + PaymentRelatedInformation string `json:"paymentRelatedInformation"` + // SequenceNumber is consecutively assigned to each Addenda17 Record following + // an Entry Detail Record. The first addenda17 sequence number must always + // be a "1". + SequenceNumber int `json:"sequenceNumber,omitempty"` + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda17 returns a new Addenda17 with default values for none exported fields +func NewAddenda17() *Addenda17 { + addenda17 := new(Addenda17) + addenda17.recordType = "7" + addenda17.typeCode = "17" + return addenda17 +} + +// Parse takes the input record string and parses the Addenda17 values +func (addenda17 *Addenda17) Parse(record string) { + // 1-1 Always "7" + addenda17.recordType = "7" + // 2-3 Always 17 + addenda17.typeCode = record[1:3] + // 4-83 Based on the information entered (04-83) 80 alphanumeric + addenda17.PaymentRelatedInformation = strings.TrimSpace(record[3:83]) + // 84-87 SequenceNumber is consecutively assigned to each Addenda17 Record following + // an Entry Detail Record + addenda17.SequenceNumber = addenda17.parseNumField(record[83:87]) + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda17.EntryDetailSequenceNumber = addenda17.parseNumField(record[87:94]) +} + +// String writes the Addenda17 struct to a 94 character string. +func (addenda17 *Addenda17) String() string { + return fmt.Sprintf("%v%v%v%v%v", + addenda17.recordType, + addenda17.typeCode, + addenda17.PaymentRelatedInformationField(), + addenda17.SequenceNumberField(), + addenda17.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda17 *Addenda17) Validate() error { + if err := addenda17.fieldInclusion(); err != nil { + return err + } + if addenda17.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda17.recordType, Msg: msg} + } + if err := addenda17.isTypeCode(addenda17.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda17.typeCode, Msg: err.Error()} + } + // Type Code must be 17 + if addenda17.typeCode != "17" { + return &FieldError{FieldName: "TypeCode", Value: addenda17.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda17.isAlphanumeric(addenda17.PaymentRelatedInformation); err != nil { + return &FieldError{FieldName: "PaymentRelatedInformation", Value: addenda17.PaymentRelatedInformation, Msg: err.Error()} + } + + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda17 *Addenda17) fieldInclusion() error { + if addenda17.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda17.recordType, Msg: msgFieldInclusion} + } + if addenda17.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda17.typeCode, Msg: msgFieldInclusion} + } + if addenda17.SequenceNumber == 0 { + return &FieldError{FieldName: "SequenceNumber", Value: addenda17.SequenceNumberField(), Msg: msgFieldInclusion} + } + if addenda17.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", Value: addenda17.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// PaymentRelatedInformationField returns a zero padded PaymentRelatedInformation string +func (addenda17 *Addenda17) PaymentRelatedInformationField() string { + return addenda17.alphaField(addenda17.PaymentRelatedInformation, 80) +} + +// SequenceNumberField returns a zero padded SequenceNumber string +func (addenda17 *Addenda17) SequenceNumberField() string { + return addenda17.numericField(addenda17.SequenceNumber, 4) +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda17 *Addenda17) EntryDetailSequenceNumberField() string { + return addenda17.numericField(addenda17.EntryDetailSequenceNumber, 7) +} + +// TypeCode Defines the specific explanation and format for the addenda17 information +func (addenda17 *Addenda17) TypeCode() string { + return addenda17.typeCode +} diff --git a/addenda17_test.go b/addenda17_test.go new file mode 100644 index 000000000..ea8fa2c87 --- /dev/null +++ b/addenda17_test.go @@ -0,0 +1,186 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda17 creates a mock Addenda17 record +func mockAddenda17() *Addenda17 { + addenda17 := NewAddenda17() + addenda17.SequenceNumber = 1 + addenda17.EntryDetailSequenceNumber = 0000001 + + return addenda17 +} + +// TestMockAddenda17 validates mockAddenda17 +func TestMockAddenda17(t *testing.T) { + addenda17 := mockAddenda17() + if err := addenda17.Validate(); err != nil { + t.Error("mockAddenda17 does not validate and will break other tests") + } + if addenda17.EntryDetailSequenceNumber != 0000001 { + t.Error("EntryDetailSequenceNumber dependent default value has changed") + } +} + +// ToDo: Add parse logic + +// testAddenda17String validates that a known parsed file can be return to a string of the same value +func testAddenda17String(t testing.TB) { + addenda17 := NewAddenda17() + var line = "717IAT DIEGO MAY 00010000001" + addenda17.Parse(line) + + if addenda17.String() != line { + t.Errorf("Strings do not match") + } +} + +// TestAddenda17 String tests validating that a known parsed file can be return to a string of the same value +func TestAddenda17String(t *testing.T) { + testAddenda17String(t) +} + +// BenchmarkAddenda17 String benchmarks validating that a known parsed file can be return to a string of the same value +func BenchmarkAddenda17String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda17String(b) + } +} + +func TestValidateAddenda17RecordType(t *testing.T) { + addenda17 := mockAddenda17() + addenda17.recordType = "63" + if err := addenda17.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda17TypeCodeFieldInclusion(t *testing.T) { + addenda17 := mockAddenda17() + addenda17.typeCode = "" + if err := addenda17.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda17FieldInclusion(t *testing.T) { + addenda17 := mockAddenda17() + addenda17.EntryDetailSequenceNumber = 0 + if err := addenda17.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "EntryDetailSequenceNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda17FieldInclusionRecordType(t *testing.T) { + addenda17 := mockAddenda17() + addenda17.recordType = "" + if err := addenda17.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +//testAddenda17PaymentRelatedInformationAlphaNumeric validates PaymentRelatedInformation is alphanumeric +func testAddenda17PaymentRelatedInformationAlphaNumeric(t testing.TB) { + addenda17 := mockAddenda17() + addenda17.PaymentRelatedInformation = "®©" + if err := addenda17.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "PaymentRelatedInformation" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda17PaymentRelatedInformationAlphaNumeric tests validating PaymentRelatedInformation is alphanumeric +func TestAddenda17PaymentRelatedInformationAlphaNumeric(t *testing.T) { + testAddenda17PaymentRelatedInformationAlphaNumeric(t) + +} + +// BenchmarkAddenda17PaymentRelatedInformationAlphaNumeric benchmarks PaymentRelatedInformation is alphanumeric +func BenchmarkAddenda17PaymentRelatedInformationAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda17PaymentRelatedInformationAlphaNumeric(b) + } +} + +// testAddenda17ValidTypeCode validates Addenda17 TypeCode +func testAddenda17ValidTypeCode(t testing.TB) { + addenda17 := mockAddenda17() + addenda17.typeCode = "65" + if err := addenda17.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda17ValidTypeCode tests validating Addenda17 TypeCode +func TestAddenda17ValidTypeCode(t *testing.T) { + testAddenda17ValidTypeCode(t) +} + +// BenchmarkAddenda17ValidTypeCode benchmarks validating Addenda17 TypeCode +func BenchmarkAddenda17ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda17ValidTypeCode(b) + } +} + +// testAddenda17TypeCode17 TypeCode is 17 if typeCode is a valid TypeCode +func testAddenda17TypeCode17(t testing.TB) { + addenda17 := mockAddenda17() + addenda17.typeCode = "05" + if err := addenda17.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda17TypeCode17 tests TypeCode is 17 if typeCode is a valid TypeCode +func TestAddenda17TypeCode17(t *testing.T) { + testAddenda17TypeCode17(t) +} + +// BenchmarkAddenda17TypeCode17 benchmarks TypeCode is 17 if typeCode is a valid TypeCode +func BenchmarkAddenda17TypeCode17(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda17TypeCode17(b) + } +} diff --git a/batch.go b/batch.go index 6eae8d548..07b2f9e7e 100644 --- a/batch.go +++ b/batch.go @@ -37,6 +37,10 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { return NewBatchCIE(bh), nil case "COR": return NewBatchCOR(bh), nil + case "IAT": + //ToDo: Update message to tell user to use iatBatch.go + msg := fmt.Sprintf(msgFileNoneSEC, bh.StandardEntryClassCode) + return nil, &FileError{FieldName: "StandardEntryClassCode", Msg: msg} case "POP": return NewBatchPOP(bh), nil case "POS": @@ -86,7 +90,7 @@ func (batch *batch) verify() error { } // batch number header and control must match if batch.Header.BatchNumber != batch.Control.BatchNumber { - msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.BatchNumber, batch.Control.BatchNumber) return &BatchError{BatchNumber: batchNumber, FieldName: "BatchNumber", Msg: msg} } diff --git a/batchIAT.go b/batchIAT.go index e6a0fcbed..67ecda6a1 100644 --- a/batchIAT.go +++ b/batchIAT.go @@ -4,9 +4,11 @@ package ach +// ToDo: Deprecate + // BatchIAT holds the Batch Header and Batch Control and all Entry Records for IAT Entries type BatchIAT struct { - iatBatch + IATBatch } // NewBatchIAT returns a *BatchIAT diff --git a/file.go b/file.go index fa983e0de..8edcc7f9d 100644 --- a/file.go +++ b/file.go @@ -56,7 +56,7 @@ type File struct { ID string `json:"id"` Header FileHeader `json:"fileHeader"` Batches []Batcher `json:"batches"` - IATBatches []IATBatcher `json:"IATBatches"` + IATBatches []IATBatch`json:"IATBatches"` Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches @@ -154,7 +154,7 @@ func (f *File) AddBatch(batch Batcher) []Batcher { } // AddIATBatch appends a IATBatch to the ach.File -func (f *File) AddIATBatch(iatBatch IATBatcher) []IATBatcher { +func (f *File) AddIATBatch(iatBatch IATBatch) []IATBatch { f.IATBatches = append(f.IATBatches, iatBatch) return f.IATBatches } diff --git a/iatBatch.go b/iatBatch.go index 6a3357b6d..ecbbb6cd0 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -9,12 +9,24 @@ import ( "strconv" ) -// Batch holds the Batch Header and Batch Control and all Entry Records -type iatBatch struct { +var ( + msgBatchIATAddendaRequired = "is required for an IAT detail entry" +) + +// IATBatch holds the Batch Header and Batch Control and all Entry Records for an IAT batch +// +// An IAT entry is a credit or debit ACH entry that is part of a payment transaction involving +// a financial agency’s office (i.e., depository financial institution or business issuing money +// orders) that is not located in the territorial jurisdiction of the United States. IAT entries +// can be made to or from a corporate or consumer account and must be accompanied by seven (7) +// mandatory addenda records identifying the name and physical address of the Originator, name +// and physical address of the Receiver, Receiver’s account number, Receiver’s bank identity and +// reason for the payment. +type IATBatch struct { // ID is a client defined string used as a reference to this record. ID string `json:"id"` - Header *IATBatchHeader `json:"IATbatchHeader,omitempty"` - Entries []*IATEntryDetail `json:"IATentryDetails,omitempty"` + Header *IATBatchHeader `json:"IATBatchHeader,omitempty"` + Entries []*IATEntryDetail `json:"IATEntryDetails,omitempty"` Control *BatchControl `json:"batchControl,omitempty"` // category defines if the entry is a Forward, Return, or NOC @@ -24,12 +36,15 @@ type iatBatch struct { } // IATNewBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported. -func IATNewBatch(bh *IATBatchHeader) (IATBatcher, error) { - return NewBatchIAT(bh), nil +func IATNewBatch(bh *IATBatchHeader) *IATBatch { + iatBatch := new(IATBatch) + iatBatch.SetControl(NewBatchControl()) + iatBatch.SetHeader(bh) + return iatBatch } // verify checks basic valid NACHA batch rules. Assumes properly parsed records. This does not mean it is a valid batch as validity is tied to each batch type -func (batch *iatBatch) verify() error { +func (batch *IATBatch) verify() error { batchNumber := batch.Header.BatchNumber // verify field inclusion in all the records of the batch. @@ -45,11 +60,6 @@ func (batch *iatBatch) verify() error { msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ServiceClassCode, batch.Control.ServiceClassCode) return &BatchError{BatchNumber: batchNumber, FieldName: "ServiceClassCode", Msg: msg} } - // Company Identification must match the Company ID from the batch header record - /* if batch.Header.CompanyIdentification != batch.Control.CompanyIdentification { - msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.CompanyIdentification, batch.Control.CompanyIdentification) - return &BatchError{BatchNumber: batchNumber, FieldName: "CompanyIdentification", Msg: msg} - }*/ // Control ODFIIdentification must be the same as batch header if batch.Header.ODFIIdentification != batch.Control.ODFIIdentification { msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) @@ -57,7 +67,7 @@ func (batch *iatBatch) verify() error { } // batch number header and control must match if batch.Header.BatchNumber != batch.Control.BatchNumber { - msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.ODFIIdentification, batch.Control.ODFIIdentification) + msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.BatchNumber, batch.Control.BatchNumber) return &BatchError{BatchNumber: batchNumber, FieldName: "BatchNumber", Msg: msg} } @@ -77,10 +87,6 @@ func (batch *iatBatch) verify() error { return err } - if err := batch.isOriginatorDNE(); err != nil { - return err - } - if err := batch.isTraceNumberODFI(); err != nil { return err } @@ -93,7 +99,7 @@ func (batch *iatBatch) verify() error { // Build creates valid batch by building sequence numbers and batch batch control. An error is returned if // the batch being built has invalid records. -func (batch *iatBatch) build() error { +func (batch *IATBatch) build() error { // Requires a valid BatchHeader if err := batch.Header.Validate(); err != nil { return err @@ -105,7 +111,21 @@ func (batch *iatBatch) build() error { entryCount := 0 seq := 1 for i, entry := range batch.Entries { - entryCount = entryCount + 1 + len(entry.Addendum) + entryCount = entryCount + 1 + 7 + //ToDo: Add Addenda17 and Addenda18 + if entry.Addenda17 != nil { + entryCount = entryCount + 1 + } + + /*if entry.Addenda18 != nil { + entryCount = entryCount + 1 + } */ + + // Verifies the required addenda* properties for an IAT entry detail are defined + if err := batch.addendaFieldInclusion(entry); err != nil { + return err + } + currentTraceNumberODFI, err := strconv.Atoi(entry.TraceNumberField()[:8]) if err != nil { return err @@ -121,21 +141,24 @@ func (batch *iatBatch) build() error { batch.Entries[i].SetTraceNumber(batch.Header.ODFIIdentification, seq) } seq++ - addendaSeq := 1 - for x := range entry.Addendum { - // sequences don't exist in NOC or Return addenda - if a, ok := batch.Entries[i].Addendum[x].(*Addenda05); ok { - a.SequenceNumber = addendaSeq - a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) - } - addendaSeq++ - } + + 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:]) + + //ToDo: Add Addenda17 and Addenda 18 logic for SequenceNUmber and EntryDetailSequenceNumber + + /* entry.Addenda17.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) + entry.Addenda18.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])*/ } // build a BatchControl record bc := NewBatchControl() bc.ServiceClassCode = batch.Header.ServiceClassCode - /*bc.CompanyIdentification = iatBatch.Header.CompanyIdentification*/ bc.ODFIIdentification = batch.Header.ODFIIdentification bc.BatchNumber = batch.Header.BatchNumber bc.EntryAddendaCount = entryCount @@ -147,43 +170,44 @@ func (batch *iatBatch) build() error { } // SetHeader appends an BatchHeader to the Batch -func (batch *iatBatch) SetHeader(batchHeader *IATBatchHeader) { +func (batch *IATBatch) SetHeader(batchHeader *IATBatchHeader) { batch.Header = batchHeader } // GetHeader returns the current Batch header -func (batch *iatBatch) GetHeader() *IATBatchHeader { +func (batch *IATBatch) GetHeader() *IATBatchHeader { return batch.Header } // SetControl appends an BatchControl to the Batch -func (batch *iatBatch) SetControl(batchControl *BatchControl) { +func (batch *IATBatch) SetControl(batchControl *BatchControl) { batch.Control = batchControl } // GetControl returns the current Batch Control -func (batch *iatBatch) GetControl() *BatchControl { +func (batch *IATBatch) GetControl() *BatchControl { return batch.Control } // GetEntries returns a slice of entry details for the batch -func (batch *iatBatch) GetEntries() []*IATEntryDetail { +func (batch *IATBatch) GetEntries() []*IATEntryDetail { return batch.Entries } // AddEntry appends an EntryDetail to the Batch -func (batch *iatBatch) AddEntry(entry *IATEntryDetail) { +func (batch *IATBatch) AddEntry(entry *IATEntryDetail) { batch.category = entry.Category batch.Entries = append(batch.Entries, entry) } -// IsReturn is true if the batch contains an Entry Return -func (batch *iatBatch) Category() string { +// Category returns IATBatch Category +// ToDo: Verify this process is the same as a non IAT Batch +func (batch *IATBatch) Category() string { return batch.category } // isFieldInclusion iterates through all the records in the batch and verifies against default fields -func (batch *iatBatch) isFieldInclusion() error { +func (batch *IATBatch) isFieldInclusion() error { if err := batch.Header.Validate(); err != nil { return err } @@ -191,10 +215,32 @@ func (batch *iatBatch) isFieldInclusion() error { if err := entry.Validate(); err != nil { return err } - for _, addenda := range entry.Addendum { - if err := addenda.Validate(); err != nil { - return nil - } + // Verifies the required Addenda* properties for an IAT entry detail are included + 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 } } return batch.Control.Validate() @@ -203,10 +249,16 @@ func (batch *iatBatch) isFieldInclusion() error { // isBatchEntryCount validate Entry count is accurate // The Entry/Addenda Count Field is a tally of each Entry Detail and Addenda // Record processed within the batch -func (batch *iatBatch) isBatchEntryCount() error { +func (batch *IATBatch) isBatchEntryCount() error { entryCount := 0 for _, entry := range batch.Entries { - entryCount = entryCount + 1 + len(entry.Addendum) + entryCount = entryCount + 1 + 7 + + //ToDo: Add logic for Addenda17 and Addenda18 + + if entry.Addenda17 != nil { + entryCount = entryCount + 1 + } } if entryCount != batch.Control.EntryAddendaCount { msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.Control.EntryAddendaCount) @@ -218,7 +270,7 @@ func (batch *iatBatch) isBatchEntryCount() error { // isBatchAmount validate Amount is the same as what is in the Entries // The Total Debit and Credit Entry Dollar Amount fields contain accumulated // Entry Detail debit and credit totals within a given batch -func (batch *iatBatch) isBatchAmount() error { +func (batch *IATBatch) isBatchAmount() error { credit, debit := batch.calculateBatchAmounts() if debit != batch.Control.TotalDebitEntryDollarAmount { msg := fmt.Sprintf(msgBatchCalculatedControlEquality, debit, batch.Control.TotalDebitEntryDollarAmount) @@ -232,7 +284,7 @@ func (batch *iatBatch) isBatchAmount() error { return nil } -func (batch *iatBatch) calculateBatchAmounts() (credit int, debit int) { +func (batch *IATBatch) calculateBatchAmounts() (credit int, debit int) { for _, entry := range batch.Entries { if entry.TransactionCode == 21 || entry.TransactionCode == 22 || entry.TransactionCode == 23 || entry.TransactionCode == 32 || entry.TransactionCode == 33 { credit = credit + entry.Amount @@ -246,7 +298,7 @@ func (batch *iatBatch) calculateBatchAmounts() (credit int, debit int) { // isSequenceAscending Individual Entry Detail Records within individual batches must // be in ascending Trace Number order (although Trace Numbers need not necessarily be consecutive). -func (batch *iatBatch) isSequenceAscending() error { +func (batch *IATBatch) isSequenceAscending() error { lastSeq := -1 for _, entry := range batch.Entries { if entry.TraceNumber <= lastSeq { @@ -259,7 +311,7 @@ func (batch *iatBatch) isSequenceAscending() error { } // isEntryHash validates the hash by recalculating the result -func (batch *iatBatch) isEntryHash() error { +func (batch *IATBatch) isEntryHash() error { hashField := batch.calculateEntryHash() if hashField != batch.Control.EntryHashField() { msg := fmt.Sprintf(msgBatchCalculatedControlEquality, hashField, batch.Control.EntryHashField()) @@ -270,7 +322,7 @@ func (batch *iatBatch) isEntryHash() error { // calculateEntryHash This field is prepared by hashing the 8-digit Routing Number in each entry. // The Entry Hash provides a check against inadvertent alteration of data -func (batch *iatBatch) calculateEntryHash() string { +func (batch *IATBatch) calculateEntryHash() string { hash := 0 for _, entry := range batch.Entries { @@ -281,22 +333,9 @@ func (batch *iatBatch) calculateEntryHash() string { return batch.numericField(hash, 10) } -// The Originator Status Code is not equal to “2” for DNE if the Transaction Code is 23 or 33 -func (batch *iatBatch) isOriginatorDNE() error { - if batch.Header.OriginatorStatusCode != 2 { - for _, entry := range batch.Entries { - if entry.TransactionCode == 23 || entry.TransactionCode == 33 { - msg := fmt.Sprintf(msgBatchOriginatorDNE, batch.Header.OriginatorStatusCode) - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "OriginatorStatusCode", Msg: msg} - } - } - } - return nil -} - // isTraceNumberODFI checks if the first 8 positions of the entry detail trace number // match the batch header ODFI -func (batch *iatBatch) isTraceNumberODFI() error { +func (batch *IATBatch) isTraceNumberODFI() error { for _, entry := range batch.Entries { if batch.Header.ODFIIdentificationField() != entry.TraceNumberField()[:8] { msg := fmt.Sprintf(msgBatchTraceNumberNotODFI, batch.Header.ODFIIdentificationField(), entry.TraceNumberField()[:8]) @@ -310,14 +349,14 @@ func (batch *iatBatch) isTraceNumberODFI() error { // ToDo: Adjustments for IAT Addenda // isAddendaSequence check multiple errors on addenda records in the batch entries -func (batch *iatBatch) isAddendaSequence() error { +func (batch *IATBatch) isAddendaSequence() error { for _, entry := range batch.Entries { - if len(entry.Addendum) > 0 { - // addenda without indicator flag of 1 - if entry.AddendaRecordIndicator != 1 { - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} - } - lastSeq := -1 + + // addenda without indicator flag of 1 + if entry.AddendaRecordIndicator != 1 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} + } + /* lastSeq := -1 // check if sequence is ascending for _, addenda := range entry.Addendum { // sequences don't exist in NOC or Return addenda @@ -328,20 +367,51 @@ func (batch *iatBatch) isAddendaSequence() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} } lastSeq = a.SequenceNumber - // check that we are in the correct Entry Detail - if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) { - msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} - } } - } + }*/ + + // 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} + } + //ToDo: Add Addenda17 and Addenda 18 logic for SequenceNUmber and EntryDetailSequenceNumber } return nil } // isCategory verifies that a Forward and Return Category are not in the same batch -func (batch *iatBatch) isCategory() error { +func (batch *IATBatch) isCategory() error { category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { for i := 1; i < len(batch.Entries); i++ { @@ -355,3 +425,35 @@ func (batch *iatBatch) isCategory() error { } return nil } + +func (batch *IATBatch) addendaFieldInclusion(entry *IATEntryDetail) error { + if entry.Addenda10 == nil { + msg := fmt.Sprint(msgBatchIATAddendaRequired) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda10", Msg: msg} + } + if entry.Addenda11 == nil { + msg := fmt.Sprint(msgBatchIATAddendaRequired) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda11", Msg: msg} + } + if entry.Addenda12 == nil { + msg := fmt.Sprint(msgBatchIATAddendaRequired) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda12", Msg: msg} + } + if entry.Addenda13 == nil { + msg := fmt.Sprint(msgBatchIATAddendaRequired) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda13", Msg: msg} + } + if entry.Addenda14 == nil { + msg := fmt.Sprint(msgBatchIATAddendaRequired) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda14", Msg: msg} + } + if entry.Addenda15 == nil { + msg := fmt.Sprint(msgBatchIATAddendaRequired) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda15", Msg: msg} + } + if entry.Addenda16 == nil { + msg := fmt.Sprint(msgBatchIATAddendaRequired) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda16", Msg: msg} + } + return nil +} diff --git a/iatBatch_test.go b/iatBatch_test.go new file mode 100644 index 000000000..9b8eed011 --- /dev/null +++ b/iatBatch_test.go @@ -0,0 +1,575 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "log" + "testing" +) + +// mockIATBatch +func mockIATBatch() *IATBatch { + mockBatch := &IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + mockBatch.GetEntries()[0].Addenda10 = mockAddenda10() + mockBatch.GetEntries()[0].Addenda11 = mockAddenda11() + mockBatch.GetEntries()[0].Addenda12 = mockAddenda12() + mockBatch.GetEntries()[0].Addenda13 = mockAddenda13() + mockBatch.GetEntries()[0].Addenda14 = mockAddenda14() + mockBatch.GetEntries()[0].Addenda15 = mockAddenda15() + mockBatch.GetEntries()[0].Addenda16 = mockAddenda16() + if err := mockBatch.build(); err != nil { + log.Fatal(err) + } + return mockBatch +} + +// TestMockIATBatch validates mockIATBatch +func TestMockIATBatch(t *testing.T) { + iatBatch := mockIATBatch() + if err := iatBatch.verify(); err != nil { + t.Error("mockIATBatch does not validate and will break other tests") + } +} + +// testIATBatchAddenda10Error validates IATBatch returns an error if Addenda10 is not included +func testIATBatchAddenda10Error(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda10 = nil + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "FieldError" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddenda10Error tests validating IATBatch returns an error +// if Addenda10 is not included +func TestIATBatchAddenda10Error(t *testing.T) { + testIATBatchAddenda10Error(t) +} + +// BenchmarkIATBatchAddenda10Error benchmarks validating IATBatch returns an error +// if Addenda10 is not included +func BenchmarkIATBatchAddenda10Error(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda10Error(b) + } +} + +// testIATBatchAddenda11Error validates IATBatch returns an error if Addenda11 is not included +func testIATBatchAddenda11Error(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda11 = nil + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "FieldError" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddenda11Error tests validating IATBatch returns an error +// if Addenda11 is not included +func TestIATBatchAddenda11Error(t *testing.T) { + testIATBatchAddenda11Error(t) +} + +// BenchmarkIATBatchAddenda11Error benchmarks validating IATBatch returns an error +// if Addenda11 is not included +func BenchmarkIATBatchAddenda11Error(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda11Error(b) + } +} + +// testIATBatchAddenda12Error validates IATBatch returns an error if Addenda12 is not included +func testIATBatchAddenda12Error(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda12 = nil + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "FieldError" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddenda12Error tests validating IATBatch returns an error +// if Addenda12 is not included +func TestIATBatchAddenda12Error(t *testing.T) { + testIATBatchAddenda12Error(t) +} + +// BenchmarkIATBatchAddenda12Error benchmarks validating IATBatch returns an error +// if Addenda12 is not included +func BenchmarkIATBatchAddenda12Error(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda12Error(b) + } +} + +// testIATBatchAddenda13Error validates IATBatch returns an error if Addenda13 is not included +func testIATBatchAddenda13Error(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda13 = nil + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "FieldError" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddenda13Error tests validating IATBatch returns an error +// if Addenda13 is not included +func TestIATBatchAddenda13Error(t *testing.T) { + testIATBatchAddenda13Error(t) +} + +// BenchmarkIATBatchAddenda13Error benchmarks validating IATBatch returns an error +// if Addenda13 is not included +func BenchmarkIATBatchAddenda13Error(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda13Error(b) + } +} + +// testIATBatchAddenda14Error validates IATBatch returns an error if Addenda14 is not included +func testIATBatchAddenda14Error(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda14 = nil + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "FieldError" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddenda14Error tests validating IATBatch returns an error +// if Addenda14 is not included +func TestIATBatchAddenda14Error(t *testing.T) { + testIATBatchAddenda14Error(t) +} + +// BenchmarkIATBatchAddenda14Error benchmarks validating IATBatch returns an error +// if Addenda14 is not included +func BenchmarkIATBatchAddenda14Error(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda14Error(b) + } +} + +// testIATBatchAddenda15Error validates IATBatch returns an error if Addenda15 is not included +func testIATBatchAddenda15Error(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda15 = nil + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "FieldError" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddenda15Error tests validating IATBatch returns an error +// if Addenda15 is not included +func TestIATBatchAddenda15Error(t *testing.T) { + testIATBatchAddenda15Error(t) +} + +// BenchmarkIATBatchAddenda15Error benchmarks validating IATBatch returns an error +// if Addenda15 is not included +func BenchmarkIATBatchAddenda15Error(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda15Error(b) + } +} + +// testIATBatchAddenda16Error validates IATBatch returns an error if Addenda16 is not included +func testIATBatchAddenda16Error(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda16 = nil + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "FieldError" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddenda16Error tests validating IATBatch returns an error +// if Addenda16 is not included +func TestIATBatchAddenda16Error(t *testing.T) { + testIATBatchAddenda16Error(t) +} + +// BenchmarkIATBatchAddenda16Error benchmarks validating IATBatch returns an error +// if Addenda16 is not included +func BenchmarkIATBatchAddenda16Error(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda16Error(b) + } +} + +// testAddenda10EntryDetailSequenceNumber validates IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func testAddenda10EntryDetailSequenceNumber(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda10.EntryDetailSequenceNumber = 00000005 + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda10EntryDetailSequenceNumber tests validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func TestAddenda10EntryDetailSequenceNumber(t *testing.T) { + testAddenda10EntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda10EntryDetailSequenceNumber benchmarks validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func BenchmarkAddenda10EntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10EntryDetailSequenceNumber(b) + } +} + +// testAddenda11EntryDetailSequenceNumber validates IATBatch returns an error if EntryDetailSequenceNumber +// is not valid +func testAddenda11EntryDetailSequenceNumber(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda11.EntryDetailSequenceNumber = 00000005 + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda11EntryDetailSequenceNumber tests validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func TestAddenda11EntryDetailSequenceNumber(t *testing.T) { + testAddenda11EntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda11EntryDetailSequenceNumber benchmarks validating IATBatch returns an error +// if EntryDetailSequenceNumber is not valid +func BenchmarkAddenda11EntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11EntryDetailSequenceNumber(b) + } +} + +// testAddenda12EntryDetailSequenceNumber validates IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func testAddenda12EntryDetailSequenceNumber(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda12.EntryDetailSequenceNumber = 00000005 + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda12EntryDetailSequenceNumber tests validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func TestAddenda12EntryDetailSequenceNumber(t *testing.T) { + testAddenda12EntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda12EntryDetailSequenceNumber benchmarks validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func BenchmarkAddenda12EntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12EntryDetailSequenceNumber(b) + } +} + +// testAddenda13EntryDetailSequenceNumber validates IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func testAddenda13EntryDetailSequenceNumber(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda13.EntryDetailSequenceNumber = 00000005 + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda13EntryDetailSequenceNumber tests validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func TestAddenda13EntryDetailSequenceNumber(t *testing.T) { + testAddenda13EntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda13EntryDetailSequenceNumber benchmarks validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func BenchmarkAddenda13EntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13EntryDetailSequenceNumber(b) + } +} + +// testAddenda14EntryDetailSequenceNumber validates IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func testAddenda14EntryDetailSequenceNumber(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda14.EntryDetailSequenceNumber = 00000005 + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda14EntryDetailSequenceNumber tests validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func TestAddenda14EntryDetailSequenceNumber(t *testing.T) { + testAddenda14EntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda14EntryDetailSequenceNumber benchmarks validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func BenchmarkAddenda14EntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14EntryDetailSequenceNumber(b) + } +} + +// testAddenda15EntryDetailSequenceNumber validates IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func testAddenda15EntryDetailSequenceNumber(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda15.EntryDetailSequenceNumber = 00000005 + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda15EntryDetailSequenceNumber tests validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func TestAddenda15EntryDetailSequenceNumber(t *testing.T) { + testAddenda15EntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda15EntryDetailSequenceNumber benchmarks validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func BenchmarkAddenda15EntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15EntryDetailSequenceNumber(b) + } +} + +// testAddenda16EntryDetailSequenceNumber validates IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func testAddenda16EntryDetailSequenceNumber(t testing.TB) { + iatBatch := mockIATBatch() + iatBatch.GetEntries()[0].Addenda16.EntryDetailSequenceNumber = 00000005 + if err := iatBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda16EntryDetailSequenceNumber tests validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func TestAddenda16EntryDetailSequenceNumber(t *testing.T) { + testAddenda16EntryDetailSequenceNumber(t) +} + +// BenchmarkAddenda16EntryDetailSequenceNumber benchmarks validating IATBatch returns an error if +// EntryDetailSequenceNumber is not valid +func BenchmarkAddenda16EntryDetailSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16EntryDetailSequenceNumber(b) + } +} + +// testIATBatchNumberMismatch validates BatchNumber mismatch +func testIATBatchNumberMismatch(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetControl().BatchNumber = 2 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "BatchNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchNumberMismatch tests validating BatchNumber mismatch +func TestIATBatchNumberMismatch(t *testing.T) { + testIATBatchNumberMismatch(t) +} + +// BenchmarkIATBatchNumberMismatch benchmarks validating BatchNumber mismatch +func BenchmarkIATBatchNumberMismatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchNumberMismatch(b) + } +} + +// testIATServiceClassCodeMismatch validates ServiceClassCode mismatch +func testIATServiceClassCodeMismatch(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetControl().ServiceClassCode = 225 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATServiceClassCodeMismatch tests validating ServiceClassCode mismatch +func TestServiceClassCodeMismatch(t *testing.T) { + testIATServiceClassCodeMismatch(t) +} + +// BenchmarkIATServiceClassCoderMismatch benchmarks validating ServiceClassCode mismatch +func BenchmarkIATServiceClassCodeMismatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATServiceClassCodeMismatch(b) + } +} + +// testIATODFIIdentificationMismatch validates ODFIIdentification mismatch +func testIATODFIIdentificationMismatch(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetControl().ODFIIdentification = "53158020" + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATODFIIdentificationMismatch tests validating ODFIIdentification mismatch +func TestODFIIdentificationMismatch(t *testing.T) { + testIATODFIIdentificationMismatch(t) +} + +// BenchmarkIATODFIIdentificationMismatch benchmarks validating ODFIIdentification mismatch +func BenchmarkIATODFIIdentificationMismatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATODFIIdentificationMismatch(b) + } +} + +// testIATAddendaRecordIndicator validates AddendaRecordIndicator FieldInclusion +func testIATAddendaRecordIndicator(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetEntries()[0].AddendaRecordIndicator = 0 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "AddendaRecordIndicator" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATAddendaRecordIndicator tests validating AddendaRecordIndicator FieldInclusion +func TestIATAddendaRecordIndicator(t *testing.T) { + testIATAddendaRecordIndicator(t) +} + +// BenchmarkIATAddendaRecordIndicator benchmarks validating AddendaRecordIndicator FieldInclusion +func BenchmarkIATAddendaRecordIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATAddendaRecordIndicator(b) + } +} diff --git a/iatBatcher.go b/iatBatcher.go deleted file mode 100644 index 9a60bc43e..000000000 --- a/iatBatcher.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018 The ACH Authors -// Use of this source code is governed by an Apache License -// license that can be found in the LICENSE file. - -package ach - -// IATBatcher abstract an IAT ACH batch type -type IATBatcher interface { - GetHeader() *IATBatchHeader - SetHeader(*IATBatchHeader) - GetControl() *BatchControl - SetControl(*BatchControl) - GetEntries() []*IATEntryDetail - AddEntry(*IATEntryDetail) - Create() error - Validate() error - // Category defines if a Forward or Return - Category() string -} - -/*// BatchError is an Error that describes batch validation issues -type IATBatchError struct { - BatchNumber int - FieldName string - Msg string -} - -func (e *BatchError) IATError() string { - return fmt.Sprintf("BatchNumber %d %s %s", e.BatchNumber, e.FieldName, e.Msg) -}*/ - -// Errors specific to parsing a Batch container -var ( -// generic messages -/* msgBatchHeaderControlEquality = "header %v is not equal to control %v" - msgBatchCalculatedControlEquality = "calculated %v is out-of-balance with control %v" - msgBatchAscending = "%v is less than last %v. Must be in ascending order" - // specific messages for error - msgBatchCompanyEntryDescription = "Company entry description %v is not valid for batch type %v" - msgBatchOriginatorDNE = "%v is not “2” for DNE with entry transaction code of 23 or 33" - msgBatchTraceNumberNotODFI = "%v in header does not match entry trace number %v" - msgBatchAddendaIndicator = "is 0 but found addenda record(s)" - msgBatchAddendaTraceNumber = "%v does not match proceeding entry detail trace number %v" - msgBatchEntries = "must have Entry Record(s) to be built" - msgBatchAddendaCount = "%v addendum found where %v is allowed for batch type %v" - msgBatchTransactionCodeCredit = "%v a credit is not allowed" - msgBatchSECType = "header SEC type code %v for batch type %v" - msgBatchTypeCode = "%v found in addenda and expecting %v for batch type %v" - msgBatchServiceClassCode = "Service Class Code %v is not valid for batch type %v" - msgBatchForwardReturn = "Forward and Return entries found in the same batch" - msgBatchAmount = "Amount must be less than %v for SEC code %v" - msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" - msgBatchTransactionCode = "Transaction code %v is not allowed for batch type %v" - msgBatchCardTransactionType = "Card Transaction Type %v is invalid"*/ -) diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 886e28e0b..6cb39c985 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -62,7 +62,15 @@ type IATEntryDetail struct { // with an entry or item rather than a physical record. TraceNumber int `json:"traceNumber,omitempty"` // Addendum a list of Addenda for the Entry Detail - Addendum []Addendumer `json:"addendum,omitempty"` + //Addendum []Addendumer `json:"addendum,omitempty"` + Addenda10 *Addenda10 `json:"addenda10,omitempty"` + Addenda11 *Addenda11 `json:"addenda11,omitempty"` + Addenda12 *Addenda12 `json:"addenda12,omitempty"` + Addenda13 *Addenda13 `json:"addenda13,omitempty"` + Addenda14 *Addenda14 `json:"addenda14,omitempty"` + Addenda15 *Addenda15 `json:"addenda15,omitempty"` + Addenda16 *Addenda16 `json:"addenda16,omitempty"` + Addenda17 *Addenda17 `json:"addenda17,omitempty"` // Category defines if the entry is a Forward, Return, or NOC Category string `json:"category,omitempty"` // validator is composed for data validation diff --git a/iatEntryDetail_test.go b/iatEntryDetail_test.go index ba1f293e0..cc27c2e7a 100644 --- a/iatEntryDetail_test.go +++ b/iatEntryDetail_test.go @@ -66,7 +66,7 @@ func BenchmarkIATMockEntryDetail(b *testing.B) { func testParseIATEntryDetail(t testing.TB) { var line = "6221210428820007 000010000012345678901234567890123456789012345 1231380100000001" r := NewReader(strings.NewReader(line)) - r.addIATCurrentBatch(NewBatchIAT(mockIATBatchHeaderFF())) + r.addIATCurrentBatch(IATNewBatch(mockIATBatchHeaderFF())) r.IATCurrentBatch.SetHeader(mockIATBatchHeaderFF()) r.line = line if err := r.parseIATEntryDetail(); err != nil { @@ -122,7 +122,7 @@ func BenchmarkParseIATEntryDetail(b *testing.B) { func testIATEDString(t testing.TB) { var line = "6221210428820007 000010000012345678901234567890123456789012345 1231380100000001" r := NewReader(strings.NewReader(line)) - r.addIATCurrentBatch(NewBatchIAT(mockIATBatchHeaderFF())) + r.addIATCurrentBatch(IATNewBatch(mockIATBatchHeaderFF())) r.IATCurrentBatch.SetHeader(mockIATBatchHeaderFF()) r.line = line if err := r.parseIATEntryDetail(); err != nil { diff --git a/reader.go b/reader.go index c39791610..064624060 100644 --- a/reader.go +++ b/reader.go @@ -37,7 +37,7 @@ type Reader struct { // currentBatch is the current Batch entries being parsed currentBatch Batcher // IATCurrentBatch is the current IATBatch entries being parsed - IATCurrentBatch IATBatcher + IATCurrentBatch *IATBatch // line number of the file being parsed lineNum int // recordName holds the current record name being parsed. @@ -61,7 +61,7 @@ func (r *Reader) addCurrentBatch(batch Batcher) { // addCurrentBatch creates the current batch type for the file being read. A successful // current batch will be added to r.File once parsed. -func (r *Reader) addIATCurrentBatch(iatBatch IATBatcher) { +func (r *Reader) addIATCurrentBatch(iatBatch *IATBatch) { r.IATCurrentBatch = iatBatch } @@ -325,10 +325,7 @@ func (r *Reader) parseIATBatchHeader() error { } // Passing BatchHeader into NewBatchIAT creates a Batcher of IAT SEC code type. - iatBatch, err := IATNewBatch(bh) - if err != nil { - return r.error(err) - } + iatBatch := IATNewBatch(bh) r.addIATCurrentBatch(iatBatch) diff --git a/writer.go b/writer.go index 1822d46ad..9c4404d33 100644 --- a/writer.go +++ b/writer.go @@ -106,12 +106,36 @@ func (w *Writer) writeIATBatch(file *File) error { return err } w.lineNum++ - for _, addenda := range entry.Addendum { - if _, err := w.w.WriteString(addenda.String() + "\n"); err != nil { - return err - } - w.lineNum++ + if _, err := w.w.WriteString(entry.Addenda10.String() + "\n"); err != nil { + return err + } + w.lineNum++ + if _, err := w.w.WriteString(entry.Addenda11.String() + "\n"); err != nil { + return err + } + w.lineNum++ + if _, err := w.w.WriteString(entry.Addenda12.String() + "\n"); err != nil { + return err + } + w.lineNum++ + if _, err := w.w.WriteString(entry.Addenda13.String() + "\n"); err != nil { + return err } + w.lineNum++ + if _, err := w.w.WriteString(entry.Addenda14.String() + "\n"); err != nil { + return err + } + w.lineNum++ + if _, err := w.w.WriteString(entry.Addenda15.String() + "\n"); err != nil { + return err + } + w.lineNum++ + if _, err := w.w.WriteString(entry.Addenda16.String() + "\n"); err != nil { + return err + } + w.lineNum++ + + // ToDo: 17 and 18 } if _, err := w.w.WriteString(iatBatch.GetControl().String() + "\n"); err != nil { return err From 8b2a8f4d97b99ed772a0bdb9a323299efbc85d16 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 9 Jul 2018 22:42:20 -0400 Subject: [PATCH 0301/1694] #211 gofmt #211 gofmt --- iatBatch.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iatBatch.go b/iatBatch.go index ecbbb6cd0..7050799bf 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -117,9 +117,9 @@ func (batch *IATBatch) build() error { entryCount = entryCount + 1 } - /*if entry.Addenda18 != nil { - entryCount = entryCount + 1 - } */ + /*if entry.Addenda18 != nil { + entryCount = entryCount + 1 + } */ // Verifies the required addenda* properties for an IAT entry detail are defined if err := batch.addendaFieldInclusion(entry); err != nil { From 30a1029b7ece398babc644fd277743a0d64b4149 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 9 Jul 2018 22:47:06 -0400 Subject: [PATCH 0302/1694] #211 file.go gofmt #211 file.go gofmt --- file.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/file.go b/file.go index 8edcc7f9d..d054806d7 100644 --- a/file.go +++ b/file.go @@ -53,11 +53,11 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { - ID string `json:"id"` - Header FileHeader `json:"fileHeader"` - Batches []Batcher `json:"batches"` - IATBatches []IATBatch`json:"IATBatches"` - Control FileControl `json:"fileControl"` + ID string `json:"id"` + Header FileHeader `json:"fileHeader"` + Batches []Batcher `json:"batches"` + IATBatches []IATBatch `json:"IATBatches"` + Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches NotificationOfChange []*BatchCOR From ca5da1011ffbd93c70d11f9bd3e4f754aacd9456 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 10 Jul 2018 09:39:30 -0400 Subject: [PATCH 0303/1694] #211 remove batchIAT #211 remove batchIAT --- batchIAT.go | 53 ----------------------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 batchIAT.go diff --git a/batchIAT.go b/batchIAT.go deleted file mode 100644 index 67ecda6a1..000000000 --- a/batchIAT.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 The ACH Authors -// Use of this source code is governed by an Apache License -// license that can be found in the LICENSE file. - -package ach - -// ToDo: Deprecate - -// BatchIAT holds the Batch Header and Batch Control and all Entry Records for IAT Entries -type BatchIAT struct { - IATBatch -} - -// NewBatchIAT returns a *BatchIAT -func NewBatchIAT(bh *IATBatchHeader) *BatchIAT { - iatBatch := new(BatchIAT) - iatBatch.SetControl(NewBatchControl()) - iatBatch.SetHeader(bh) - return iatBatch -} - -// Validate checks valid NACHA batch rules. Assumes properly parsed records. -func (batch *BatchIAT) Validate() error { - // basic verification of the batch before we validate specific rules. - if err := batch.verify(); err != nil { - return err - } - // Add configuration based validation for this type. - - // Batch can have one addenda per entry record - /* if err := batch.isAddendaCount(1); err != nil { - return err - } - if err := batch.isTypeCode("05"); err != nil { - return err - }*/ - - // Add type specific validation. - // ... - return nil -} - -// Create takes Batch Header and Entries and builds a valid batch -func (batch *BatchIAT) Create() error { - // generates sequence numbers and batch control - if err := batch.build(); err != nil { - return err - } - // Additional steps specific to batch type - // ... - - return batch.Validate() -} From 155422f68946b6dc79ad2e8ca0ee0d1ac112ea67 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 11 Jul 2018 14:05:58 -0400 Subject: [PATCH 0304/1694] #211 Additional Addenda Support #211 Additional Addenda Support - Update reader.go for IAT Addenda records 10-16 Update travis.yml (for now) to support over 20 for parseIATAddenda Add testIATWrite to writer_test.go Update file.go for IATBatches Minor updates to IATBatch --- .travis.yml | 2 +- addenda10.go | 2 +- file.go | 17 +++- iatBatch.go | 79 ++++++++++--------- iatBatch_test.go | 18 ++--- iatEntryDetail.go | 34 +++++++- reader.go | 196 +++++++++++++++++++++++++++++++++++++--------- writer_test.go | 73 +++++++++++++++++ 8 files changed, 332 insertions(+), 89 deletions(-) diff --git a/.travis.yml b/.travis.yml index 163aba0b0..7b1d31368 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES - misspell -error -locale US . -- gocyclo -over 19 $GO_FILES +- gocyclo -over 20 $GO_FILES - golint -set_exit_status $GO_FILES after_success: - goveralls -repotoken $COVERALLS_TOKEN diff --git a/addenda10.go b/addenda10.go index ee0afed8c..3e676b01b 100644 --- a/addenda10.go +++ b/addenda10.go @@ -14,7 +14,7 @@ import ( // // Addenda10 is mandatory for IAT entries // -// The First Addenda Record identifies the Receiver of the transaction and the dollar amount of +// The Addenda10 Record identifies the Receiver of the transaction and the dollar amount of // the payment. type Addenda10 struct { // ID is a client defined string used as a reference to this record. diff --git a/file.go b/file.go index d054806d7..4ddb0786c 100644 --- a/file.go +++ b/file.go @@ -168,7 +168,7 @@ func (f *File) SetHeader(h FileHeader) *File { // Validate NACHA rules on the entire batch before being added to a File func (f *File) Validate() error { // The value of the Batch Count Field is equal to the number of Company/Batch/Header Records in the file. - if f.Control.BatchCount != len(f.Batches) { + if f.Control.BatchCount != (len(f.Batches) + len(f.IATBatches)) { msg := fmt.Sprintf(msgFileCalculatedControlEquality, len(f.Batches), f.Control.BatchCount) return &FileError{FieldName: "BatchCount", Value: strconv.Itoa(len(f.Batches)), Msg: msg} } @@ -184,7 +184,7 @@ func (f *File) Validate() error { return f.isEntryHash() } -// isEntryAddenda is prepared by hashing the RDFI’s 8-digit Routing Number in each entry. +// isEntryAddendaCount is prepared by hashing the RDFI’s 8-digit Routing Number in each entry. //The Entry Hash provides a check against inadvertent alteration of data func (f *File) isEntryAddendaCount() error { count := 0 @@ -192,6 +192,10 @@ func (f *File) isEntryAddendaCount() error { for _, batch := range f.Batches { count += batch.GetControl().EntryAddendaCount } + // IAT + for _, iatBatch := range f.IATBatches { + count += iatBatch.GetControl().EntryAddendaCount + } if f.Control.EntryAddendaCount != count { msg := fmt.Sprintf(msgFileCalculatedControlEquality, count, f.Control.EntryAddendaCount) return &FileError{FieldName: "EntryAddendaCount", Value: f.Control.EntryAddendaCountField(), Msg: msg} @@ -208,6 +212,11 @@ func (f *File) isFileAmount() error { debit += batch.GetControl().TotalDebitEntryDollarAmount credit += batch.GetControl().TotalCreditEntryDollarAmount } + // IAT + for _, iatBatch := range f.IATBatches { + debit += iatBatch.GetControl().TotalDebitEntryDollarAmount + credit += iatBatch.GetControl().TotalCreditEntryDollarAmount + } if f.Control.TotalDebitEntryDollarAmountInFile != debit { msg := fmt.Sprintf(msgFileCalculatedControlEquality, debit, f.Control.TotalDebitEntryDollarAmountInFile) return &FileError{FieldName: "TotalDebitEntryDollarAmountInFile", Value: f.Control.TotalDebitEntryDollarAmountInFileField(), Msg: msg} @@ -236,5 +245,9 @@ func (f *File) calculateEntryHash() string { for _, batch := range f.Batches { hash = hash + batch.GetControl().EntryHash } + // IAT + for _, iatBatch := range f.IATBatches { + hash = hash + iatBatch.GetControl().EntryHash + } return f.numericField(hash, 10) } diff --git a/iatBatch.go b/iatBatch.go index 7050799bf..a761f2d5f 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -10,7 +10,7 @@ import ( ) var ( - msgBatchIATAddendaRequired = "is required for an IAT detail entry" + msgIATBatchAddendaRequired = "is required for an IAT detail entry" ) // IATBatch holds the Batch Header and Batch Control and all Entry Records for an IAT batch @@ -36,8 +36,8 @@ type IATBatch struct { } // IATNewBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported. -func IATNewBatch(bh *IATBatchHeader) *IATBatch { - iatBatch := new(IATBatch) +func IATNewBatch(bh *IATBatchHeader) IATBatch { + iatBatch := IATBatch{} iatBatch.SetControl(NewBatchControl()) iatBatch.SetHeader(bh) return iatBatch @@ -112,10 +112,10 @@ func (batch *IATBatch) build() error { seq := 1 for i, entry := range batch.Entries { entryCount = entryCount + 1 + 7 - //ToDo: Add Addenda17 and Addenda18 - if entry.Addenda17 != nil { - entryCount = entryCount + 1 - } + //ToDo: Add Addenda17 and Addenda18 maximum of 2 addenda17 and 5 addenda18 + /* if entry.Addenda17 != nil { + entryCount = entryCount + 1 + }*/ /*if entry.Addenda18 != nil { entryCount = entryCount + 1 @@ -346,30 +346,13 @@ func (batch *IATBatch) isTraceNumberODFI() error { return nil } -// ToDo: Adjustments for IAT Addenda - // isAddendaSequence check multiple errors on addenda records in the batch entries func (batch *IATBatch) isAddendaSequence() error { for _, entry := range batch.Entries { - // addenda without indicator flag of 1 if entry.AddendaRecordIndicator != 1 { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} } - /* lastSeq := -1 - // check if sequence is ascending - for _, addenda := range entry.Addendum { - // sequences don't exist in NOC or Return addenda - if a, ok := addenda.(*Addenda05); ok { - - if a.SequenceNumber < lastSeq { - msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastSeq) - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} - } - lastSeq = a.SequenceNumber - } - }*/ - // Verify Addenda* entry detail sequence numbers are valid entryTN := entry.TraceNumberField()[8:] @@ -385,22 +368,18 @@ func (batch *IATBatch) isAddendaSequence() error { 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} @@ -428,32 +407,62 @@ func (batch *IATBatch) isCategory() error { func (batch *IATBatch) addendaFieldInclusion(entry *IATEntryDetail) error { if entry.Addenda10 == nil { - msg := fmt.Sprint(msgBatchIATAddendaRequired) + msg := fmt.Sprint(msgIATBatchAddendaRequired) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda10", Msg: msg} } if entry.Addenda11 == nil { - msg := fmt.Sprint(msgBatchIATAddendaRequired) + msg := fmt.Sprint(msgIATBatchAddendaRequired) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda11", Msg: msg} } if entry.Addenda12 == nil { - msg := fmt.Sprint(msgBatchIATAddendaRequired) + msg := fmt.Sprint(msgIATBatchAddendaRequired) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda12", Msg: msg} } if entry.Addenda13 == nil { - msg := fmt.Sprint(msgBatchIATAddendaRequired) + msg := fmt.Sprint(msgIATBatchAddendaRequired) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda13", Msg: msg} } if entry.Addenda14 == nil { - msg := fmt.Sprint(msgBatchIATAddendaRequired) + msg := fmt.Sprint(msgIATBatchAddendaRequired) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda14", Msg: msg} } if entry.Addenda15 == nil { - msg := fmt.Sprint(msgBatchIATAddendaRequired) + msg := fmt.Sprint(msgIATBatchAddendaRequired) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda15", Msg: msg} } if entry.Addenda16 == nil { - msg := fmt.Sprint(msgBatchIATAddendaRequired) + msg := fmt.Sprint(msgIATBatchAddendaRequired) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda16", Msg: msg} } return nil } + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *IATBatch) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + return batch.Validate() +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *IATBatch) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // IATBatch must have the following mandatory addenda per entry detail: + // Addenda10,Addenda11,Addenda12,Addenda13,Addenda14,Addenda15,Addenda16 + + // ToDo: IATBatch can have a maximum of 2 optional Addenda17 records + // ToDo: IAtBatch can have a maximum of 5 optional Addenda18 records + + // Add type specific validation. + // ... + return nil +} diff --git a/iatBatch_test.go b/iatBatch_test.go index 9b8eed011..ca0259244 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -10,17 +10,17 @@ import ( ) // mockIATBatch -func mockIATBatch() *IATBatch { - mockBatch := &IATBatch{} +func mockIATBatch() IATBatch { + mockBatch := IATBatch{} mockBatch.SetHeader(mockIATBatchHeaderFF()) mockBatch.AddEntry(mockIATEntryDetail()) - mockBatch.GetEntries()[0].Addenda10 = mockAddenda10() - mockBatch.GetEntries()[0].Addenda11 = mockAddenda11() - mockBatch.GetEntries()[0].Addenda12 = mockAddenda12() - mockBatch.GetEntries()[0].Addenda13 = mockAddenda13() - mockBatch.GetEntries()[0].Addenda14 = mockAddenda14() - mockBatch.GetEntries()[0].Addenda15 = mockAddenda15() - mockBatch.GetEntries()[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() if err := mockBatch.build(); err != nil { log.Fatal(err) } diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 6cb39c985..1d3dd4368 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -61,17 +61,45 @@ type IATEntryDetail struct { // in the associated Entry Detail Record, since the Trace Number is associated // with an entry or item rather than a physical record. TraceNumber int `json:"traceNumber,omitempty"` - // Addendum a list of Addenda for the Entry Detail - //Addendum []Addendumer `json:"addendum,omitempty"` + // Addenda10 is mandatory for IAT entries + // + // The Addenda10 Record identifies the Receiver of the transaction and the dollar amount of + // the payment. Addenda10 *Addenda10 `json:"addenda10,omitempty"` + // Addenda11 is mandatory for IAT entries + // + // The Addenda11 record identifies key information related to the Originator of + // the entry. Addenda11 *Addenda11 `json:"addenda11,omitempty"` + // Addenda12 is mandatory for IAT entries + // + // The Addenda12 record identifies key information related to the Originator of + // the entry. Addenda12 *Addenda12 `json:"addenda12,omitempty"` + // Addenda13 is mandatory for IAT entries + // + // The Addenda13 contains information related to the financial institution originating the entry. + // For inbound IAT entries, the Fourth Addenda Record must contain information to identify the + // foreign financial institution that is providing the funding and payment instruction for + // the IAT entry. Addenda13 *Addenda13 `json:"addenda13,omitempty"` + // Addenda14 is mandatory for IAT entries + // + // The Addenda14 identifies the Receiving financial institution holding the Receiver's account. Addenda14 *Addenda14 `json:"addenda14,omitempty"` + // Addenda15 is mandatory for IAT entries + // + // The Addenda15 record identifies key information related to the Receiver. Addenda15 *Addenda15 `json:"addenda15,omitempty"` + // Addenda16 Addenda16 *Addenda16 `json:"addenda16,omitempty"` + // Addenda16 is mandatory for IAT entries + // + // The Addenda16 record identifies key information related to the Receiver. Addenda17 *Addenda17 `json:"addenda17,omitempty"` - // Category defines if the entry is a Forward, Return, or NOC + // Addenda17 is optional for IAT entries + // + // The Addenda17 record identifies payment-related data. A maximum of two of these Addenda Records Category string `json:"category,omitempty"` // validator is composed for data validation validator diff --git a/reader.go b/reader.go index 064624060..64039d3e4 100644 --- a/reader.go +++ b/reader.go @@ -37,7 +37,7 @@ type Reader struct { // currentBatch is the current Batch entries being parsed currentBatch Batcher // IATCurrentBatch is the current IATBatch entries being parsed - IATCurrentBatch *IATBatch + IATCurrentBatch IATBatch // line number of the file being parsed lineNum int // recordName holds the current record name being parsed. @@ -61,7 +61,7 @@ func (r *Reader) addCurrentBatch(batch Batcher) { // addCurrentBatch creates the current batch type for the file being read. A successful // current batch will be added to r.File once parsed. -func (r *Reader) addIATCurrentBatch(iatBatch *IATBatch) { +func (r *Reader) addIATCurrentBatch(iatBatch IATBatch) { r.IATCurrentBatch = iatBatch } @@ -144,19 +144,28 @@ func (r *Reader) parseLine() error { return err } case entryAddendaPos: - if err := r.parseAddenda(); err != nil { + if err := r.parseEDAddenda(); err != nil { return err } case batchControlPos: if err := r.parseBatchControl(); err != nil { return err } - if err := r.currentBatch.Validate(); err != nil { - r.recordName = "Batches" - return r.error(err) + if r.currentBatch != nil { + if err := r.currentBatch.Validate(); err != nil { + r.recordName = "Batches" + return r.error(err) + } + r.File.AddBatch(r.currentBatch) + r.currentBatch = nil + } else { + if err := r.IATCurrentBatch.Validate(); err != nil { + r.recordName = "Batches" + return r.error(err) + } + r.File.AddIATBatch(r.IATCurrentBatch) + r.IATCurrentBatch = IATBatch{} } - r.File.AddBatch(r.currentBatch) - r.currentBatch = nil case fileControlPos: if r.line[:2] == "99" { // final blocking padding @@ -172,6 +181,52 @@ func (r *Reader) parseLine() error { return nil } +// parseBH parses determines whether to parse an IATBatchHeader or BatchHeader +func (r *Reader) parseBH() error { + if r.line[50:53] == "IAT" { + if err := r.parseIATBatchHeader(); err != nil { + return err + } + } else { + if err := r.parseBatchHeader(); err != nil { + return err + } + } + return nil +} + +// parseEd parses determines whether to parse an IATEntryDetail or EntryDetail +func (r *Reader) parseED() error { + // ToDo: Review if this can be true for domestic files. + // IATIndicator field + if r.line[16:29] == " " { + if err := r.parseIATEntryDetail(); err != nil { + return err + } + } else { + if err := r.parseEntryDetail(); err != nil { + return err + } + } + return nil +} + +// parseEd parses determines whether to parse an IATEntryDetail Addenda or EntryDetail Addenda +func (r *Reader) parseEDAddenda() error { + switch r.line[1:3] { + //ToDo; What to do about 98 and 99 ? + case "10", "11", "12", "13", "14", "15", "16", "17", "18": + if err := r.parseIATAddenda(); err != nil { + return err + } + default: + if err := r.parseAddenda(); err != nil { + return err + } + } + return nil +} + // parseFileHeader takes the input record string and parses the FileHeaderRecord values func (r *Reader) parseFileHeader() error { r.recordName = "FileHeader" @@ -243,6 +298,7 @@ func (r *Reader) parseAddenda() error { entry := r.currentBatch.GetEntries()[entryIndex] if entry.AddendaRecordIndicator == 1 { + switch r.line[1:3] { case "02": addenda02 := NewAddenda02() @@ -284,13 +340,22 @@ func (r *Reader) parseAddenda() error { // parseBatchControl takes the input record string and parses the BatchControlRecord values func (r *Reader) parseBatchControl() error { r.recordName = "BatchControl" - if r.currentBatch == nil { + if r.currentBatch == nil && r.IATCurrentBatch.GetEntries() == nil { // batch Control without a current batch return r.error(&FileError{Msg: msgFileBatchOutside}) } - r.currentBatch.GetControl().Parse(r.line) - if err := r.currentBatch.GetControl().Validate(); err != nil { - return r.error(err) + + if r.currentBatch != nil { + r.currentBatch.GetControl().Parse(r.line) + if err := r.currentBatch.GetControl().Validate(); err != nil { + return r.error(err) + } + } else { + r.IATCurrentBatch.GetControl().Parse(r.line) + if err := r.IATCurrentBatch.GetControl().Validate(); err != nil { + return r.error(err) + } + } return nil } @@ -309,10 +374,12 @@ func (r *Reader) parseFileControl() error { return nil } +// IAT specific reader functions + // parseIATBatchHeader takes the input record string and parses the FileHeaderRecord values func (r *Reader) parseIATBatchHeader() error { - r.recordName = "IATBatchHeader" - if r.IATCurrentBatch != nil { + r.recordName = "BatchHeader" + if r.IATCurrentBatch.Header != nil { // batch header inside of current batch return r.error(&FileError{Msg: msgFileBatchInside}) } @@ -334,11 +401,12 @@ func (r *Reader) parseIATBatchHeader() error { // parseIATEntryDetail takes the input record string and parses the EntryDetailRecord values func (r *Reader) parseIATEntryDetail() error { - r.recordName = "IATEntryDetail" + r.recordName = "EntryDetail" - if r.IATCurrentBatch == nil { + if r.IATCurrentBatch.Header == nil { return r.error(&FileError{Msg: msgFileBatchOutside}) } + ed := new(IATEntryDetail) ed.Parse(r.line) if err := ed.Validate(); err != nil { @@ -348,32 +416,84 @@ func (r *Reader) parseIATEntryDetail() error { return nil } -// parseBH parses determines whether to parse an IATBatchHeader or BatchHeader -func (r *Reader) parseBH() error { - if r.line[50:53] == "IAT" { - if err := r.parseIATBatchHeader(); err != nil { - return err - } - } else { - if err := r.parseBatchHeader(); err != nil { - return err - } +// parseAddendaRecord takes the input record string and create an Addenda Type appended to the last EntryDetail +func (r *Reader) parseIATAddenda() error { + r.recordName = "Addenda" + + if r.IATCurrentBatch.GetEntries() == nil { + msg := fmt.Sprint(msgFileBatchOutside) + return r.error(&FileError{FieldName: "Addenda", Msg: msg}) } - return nil -} + if len(r.IATCurrentBatch.GetEntries()) == 0 { + return r.error(&FileError{FieldName: "Addenda", Msg: msgFileBatchOutside}) + } + entryIndex := len(r.IATCurrentBatch.GetEntries()) - 1 + entry := r.IATCurrentBatch.GetEntries()[entryIndex] -// parseEd parses determines whether to parse an IATEntryDetail or EntryDetail -func (r *Reader) parseED() error { - // ToDo: Review if this can be true for domestic files. - // IATIndicator field - if r.line[16:29] == " " { - if err := r.parseIATEntryDetail(); err != nil { - return err + if entry.AddendaRecordIndicator == 1 { + switch r.line[1:3] { + case "10": + addenda10 := NewAddenda10() + addenda10.Parse(r.line) + if err := addenda10.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda10 = addenda10 + case "11": + addenda11 := NewAddenda11() + addenda11.Parse(r.line) + if err := addenda11.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda11 = addenda11 + case "12": + addenda12 := NewAddenda12() + addenda12.Parse(r.line) + if err := addenda12.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda12 = addenda12 + case "13": + + addenda13 := NewAddenda13() + addenda13.Parse(r.line) + if err := addenda13.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda13 = addenda13 + case "14": + addenda14 := NewAddenda14() + addenda14.Parse(r.line) + if err := addenda14.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda14 = addenda14 + case "15": + addenda15 := NewAddenda15() + addenda15.Parse(r.line) + if err := addenda15.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda15 = addenda15 + case "16": + addenda16 := NewAddenda16() + addenda16.Parse(r.line) + if err := addenda16.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda16 = addenda16 + case "17": + addenda17 := NewAddenda17() + addenda17.Parse(r.line) + if err := addenda17.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda17 = addenda17 } } else { - if err := r.parseEntryDetail(); err != nil { - return err - } + msg := fmt.Sprint(msgBatchAddendaIndicator) + return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) } + return nil } diff --git a/writer_test.go b/writer_test.go index 2de2e9455..cc8ac30e0 100644 --- a/writer_test.go +++ b/writer_test.go @@ -104,3 +104,76 @@ func BenchmarkFileWriteErr(b *testing.B) { testFileWriteErr(b) } } + +// testIATWrite writes a IAT ACH file +func testIATWrite(t testing.TB) { + file := NewFile().SetHeader(mockFileHeader()) + iatBatch := IATBatch{} + iatBatch.SetHeader(mockIATBatchHeaderFF()) + iatBatch.AddEntry(mockIATEntryDetail()) + iatBatch.Entries[0].Addenda10 = mockAddenda10() + iatBatch.Entries[0].Addenda11 = mockAddenda11() + iatBatch.Entries[0].Addenda12 = mockAddenda12() + iatBatch.Entries[0].Addenda13 = mockAddenda13() + iatBatch.Entries[0].Addenda14 = mockAddenda14() + iatBatch.Entries[0].Addenda15 = mockAddenda15() + iatBatch.Entries[0].Addenda16 = mockAddenda16() + iatBatch.Create() + file.AddIATBatch(iatBatch) + + /* iatBatch2 := IATBatch{} + iatBatch2.SetHeader(mockIATBatchHeaderFF()) + iatBatch2.AddEntry(mockIATEntryDetail()) + iatBatch2.Entries[0].Addenda10 = mockAddenda10() + iatBatch2.Entries[0].Addenda11 = mockAddenda11() + iatBatch2.Entries[0].Addenda12 = mockAddenda12() + iatBatch2.Entries[0].Addenda13 = mockAddenda13() + iatBatch2.Entries[0].Addenda14 = mockAddenda14() + iatBatch2.Entries[0].Addenda15 = mockAddenda15() + iatBatch2.Entries[0].Addenda16 = mockAddenda16() + iatBatch2.Create() + file.AddIATBatch(iatBatch2)*/ + + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + b := &bytes.Buffer{} + f := NewWriter(b) + + if err := f.Write(file); err != nil { + t.Errorf("%T: %s", err, err) + } + + r := NewReader(strings.NewReader(b.String())) + _, err := r.Read() + if err != nil { + t.Errorf("%T: %s", err, err) + } + if err = r.File.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + /* // Write IAT records to standard output. Anything io.Writer + w := NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush()*/ +} + +// TestIATWrite tests writing a IAT ACH file +func TestIATWrite(t *testing.T) { + testIATWrite(t) +} + +// BenchmarkIATWrite benchmarks validating writing a IAT ACH file +func BenchmarkIATWrite(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATWrite(b) + } +} From 3b68afa6b56f89e0bce04ff8242c42560d4a0497 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 12 Jul 2018 09:47:51 -0400 Subject: [PATCH 0305/1694] #211 Updates for reading an achfile with IAT entries #211 Updates for reading an achfile with IAT entries Modified sequence numbers, debit/credit balance control, and batch header ODFIIdentification in 20110729.ach Modified File control string length in 20110805A.ach Added tests to read achfiles to reader_test.go Modified isCategory to return nil if there are no entries for a batch - could be a temporary fix Modified addenda10 to not return an error if foreign exchange amount is 0. -could be a temporary fix. Follow up - Review isCategory logic when reading an ach file. --- addenda10.go | 8 +- batch.go | 18 +- iatBatch.go | 14 +- reader_test.go | 60 ++++++ test/data/20110729A.ach | 398 ++++++++++++++++++++-------------------- test/data/20110805A.ach | 2 +- 6 files changed, 281 insertions(+), 219 deletions(-) diff --git a/addenda10.go b/addenda10.go index 3e676b01b..27d85fdc0 100644 --- a/addenda10.go +++ b/addenda10.go @@ -6,7 +6,6 @@ package ach import ( "fmt" - "strconv" ) // Addenda10 is an addenda which provides business transaction information for Addenda Type @@ -112,7 +111,7 @@ func (addenda10 *Addenda10) Validate() error { if err := addenda10.isTransactionTypeCode(addenda10.TransactionTypeCode); err != nil { return &FieldError{FieldName: "TransactionTypeCode", Value: addenda10.TransactionTypeCode, Msg: err.Error()} } - // ToDo: Foreign Exchange Amount blank ? + // ToDo: Foreign Payment Amount blank ? if err := addenda10.isAlphanumeric(addenda10.ForeignTraceNumber); err != nil { return &FieldError{FieldName: "ForeignTraceNumber", Value: addenda10.ForeignTraceNumber, Msg: err.Error()} } @@ -135,10 +134,11 @@ func (addenda10 *Addenda10) fieldInclusion() error { return &FieldError{FieldName: "TransactionTypeCode", Value: addenda10.TransactionTypeCode, Msg: msgFieldRequired} } - if addenda10.ForeignPaymentAmount == 0 { + // ToDo: Commented because it appears this value can be all 000 (maybe blank?) + /* if addenda10.ForeignPaymentAmount == 0 { return &FieldError{FieldName: "ForeignPaymentAmount", Value: strconv.Itoa(addenda10.ForeignPaymentAmount), Msg: msgFieldRequired} - } + }*/ if addenda10.Name == "" { return &FieldError{FieldName: "Name", Value: addenda10.Name, Msg: msgFieldInclusion} } diff --git a/batch.go b/batch.go index 07b2f9e7e..4f1736bda 100644 --- a/batch.go +++ b/batch.go @@ -93,27 +93,21 @@ func (batch *batch) verify() error { msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.BatchNumber, batch.Control.BatchNumber) return &BatchError{BatchNumber: batchNumber, FieldName: "BatchNumber", Msg: msg} } - if err := batch.isBatchEntryCount(); err != nil { return err } - if err := batch.isSequenceAscending(); err != nil { return err } - if err := batch.isBatchAmount(); err != nil { return err } - if err := batch.isEntryHash(); err != nil { return err } - if err := batch.isOriginatorDNE(); err != nil { return err } - if err := batch.isTraceNumberODFI(); err != nil { return err } @@ -121,7 +115,10 @@ func (batch *batch) verify() error { if err := batch.isAddendaSequence(); err != nil { return err } - return batch.isCategory() + if err := batch.isCategory(); err != nil { + return err + } + return nil } // Build creates valid batch by building sequence numbers and batch batch control. An error is returned if @@ -346,7 +343,6 @@ func (batch *batch) isTraceNumberODFI() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ODFIIdentificationField", Msg: msg} } } - return nil } @@ -410,9 +406,13 @@ func (batch *batch) isTypeCode(typeCode string) error { // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *batch) isCategory() error { + // ToDo: Add temporarily - ./test/data/20110805A.ach contains a batch without a detail entry + if len(batch.GetEntries()) == 0 { + return nil + } category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { - for i := 1; i < len(batch.Entries); i++ { + for i := 0; i < len(batch.Entries); i++ { if batch.Entries[i].Category == CategoryNOC { continue } diff --git a/iatBatch.go b/iatBatch.go index a761f2d5f..f26695df8 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -70,23 +70,18 @@ func (batch *IATBatch) verify() error { msg := fmt.Sprintf(msgBatchHeaderControlEquality, batch.Header.BatchNumber, batch.Control.BatchNumber) return &BatchError{BatchNumber: batchNumber, FieldName: "BatchNumber", Msg: msg} } - if err := batch.isBatchEntryCount(); err != nil { return err } - if err := batch.isSequenceAscending(); err != nil { return err } - if err := batch.isBatchAmount(); err != nil { return err } - if err := batch.isEntryHash(); err != nil { return err } - if err := batch.isTraceNumberODFI(); err != nil { return err } @@ -94,7 +89,10 @@ func (batch *IATBatch) verify() error { if err := batch.isAddendaSequence(); err != nil { return err } - return batch.isCategory() + if err := batch.isCategory(); err != nil { + return err + } + return nil } // Build creates valid batch by building sequence numbers and batch batch control. An error is returned if @@ -391,6 +389,10 @@ func (batch *IATBatch) isAddendaSequence() error { // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *IATBatch) isCategory() error { + // ToDo: Add temporarily - ./test/data/20110805A.ach contains a batch without a detail entry + if len(batch.GetEntries()) == 0 { + return nil + } category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { for i := 1; i < len(batch.Entries); i++ { diff --git a/reader_test.go b/reader_test.go index d0e57a14c..f85903f26 100644 --- a/reader_test.go +++ b/reader_test.go @@ -998,3 +998,63 @@ func BenchmarkFileFHImmediateOrigin(b *testing.B) { testFileFHImmediateOrigin(b) } } + +// testACHFileRead validates reading a file with PPD and IAT entries +func testACHFileRead(t testing.TB) { + f, err := os.Open("./test/data/20110805A.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + if err != nil { + t.Errorf("%T: %s", err, err) + } + if err = r.File.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestACHFileRead tests validating reading a file with PPD and IAT entries +func TestACHFileRead(t *testing.T) { + testACHFileRead(t) +} + +// BenchmarkACHFileRead benchmarks validating reading a file with PPD and IAT entries +func BenchmarkACHFileRead(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileRead(b) + } +} + +// testACHFileRead2 validates reading a file with PPD and IAT entries +func testACHFileRead2(t testing.TB) { + f, err := os.Open("./test/data/20110729A.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + if err != nil { + t.Errorf("%T: %s", err, err) + } + if err = r.File.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestACHFileRead2 tests validating reading a file with PPD and IAT entries +func TestACHFileRead2(t *testing.T) { + testACHFileRead2(t) +} + +// BenchmarkACHFileRead2 benchmarks validating reading a file with PPD and IAT entries +func BenchmarkACHFileRead2(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileRead2(b) + } +} diff --git a/test/data/20110729A.ach b/test/data/20110729A.ach index e2153b518..9e4932baf 100644 --- a/test/data/20110729A.ach +++ b/test/data/20110729A.ach @@ -1,175 +1,175 @@ 101 04200001312345678981107291600A094101US BANK NA TEST COMPANY -5225TEST COMPANY 11234567898PPDTEST BUYS 110801110801 1042000010000001 +5225TEST COMPANY 11234567898PPDTEST BUYS 110801110801 1098765430000001 627021200025998412345 0011900000OA313 AYDEN DERS 0098765434200001 -627021200025998412345 0009900000OA235 DYLAN HUNTER 0098765434200001 -627021200025998412345 0015100000OA357 COLTON LOWE 0098765434200001 -627021200025998412345 0026600000OA264 ISAIAH CARPENTER 0098765434200001 -627021200025998412345 0025500000OA363 COOPER BARNETT 0098765434200001 -627021200025998412345 0022800000OA298 KATHERINE GRAVES 0098765434200001 -627021200025998412345 0016100000OA332 JORDAN FRANKLIN 0098765434200001 -627021200025998412345 0022400000OA270 JACOB SIMPSON 0098765434200001 -627021200025998412345 0008900000OA268 ADAM WILLIS 0098765434200001 -627021200025998412345 0026600000OA354 ISAIAH CARPENTER 0098765434200001 -627021200025998412345 0026600000OA333 ISAIAH CARPENTER 0098765434200001 -627021200025998412345 0029700000OA250 JOSE JORDAN 0098765434200001 -627021200025998412345 0026900000OA300 JULIAN PERKINS 0098765434200001 -627021200025998412345 0029000000OA359 JACK GAS 0098765434200001 -627021200025998412345 0029900000OA281 SOFIA WATSON 0098765434200001 -627021200025998412345 0023800000OA316 ARIANNA KING 0098765434200001 -627021200025998412345 0029400000OA292 AYDEN SIMPSON 0098765434200001 -627021200025998412345 0024700000OA319 SOPHIE HENRY 0098765434200001 -627021200025998412345 0005900000OA267 MAKAYLA TERRY 0098765434200001 -627021200025998412345 0016100000OA257 JORDAN FRANKLIN 0098765434200001 -627021200025998412345 0016600000OA349 JASMINE CARTER 0098765434200001 -627021200025998412345 0015800000OA352 JACOB LEE 0098765434200001 -627021200025998412345 0022900000OA233 LIAM RTINEZ 0098765434200001 -627021200025998412345 0018200000OA283 LILY ROBERTSON 0098765434200001 -627021200025998412345 0028200000OA360 MORGAN NELSON 0098765434200001 -627021200025998412345 0018700000OA345 LUCAS THOMPSON 0098765434200001 -627021200025998412345 0022800000OA368 NOAH 0098765434200001 -627021200025998412345 0007600000OA340 ARIANNA LONG 0098765434200001 -627021200025998412345 0029000000OA338 JACK GAS 0098765434200001 -627021200025998412345 0020700000OA272 MORGAN FOX 0098765434200001 -627021200025998412345 0025500000OA342 COOPER BARNETT 0098765434200001 -627021200025998412345 0018500000OA224 JESSICA SILVA 0098765434200001 -627021200025998412345 0007800000OA273 JASMINE CARTER 0098765434200001 -627021200025998412345 0011700000OA299 ISABELLE ERRY 0098765434200001 -627021200025998412345 0006300000OA263 ZACHARY MEDINA 0098765434200001 -627021200025998412345 0001800000OA266 MATTHEW ALLEN 0098765434200001 -627021200025998412345 0015000000OA312 BROOKE SUTTON 0098765434200001 -627021200025998412345 0006600000OA242 ABIGAIL EVANS 0098765434200001 -627021200025998412345 0028000000OA304 ISAAC JORDAN 0098765434200001 -627021200025998412345 0007800000OA269 JUAN GILBERT 0098765434200001 -627021200025998412345 0020800000OA249 KEVIN CASTILLO 0098765434200001 -627021200025998412345 0010600000OA311 EVAN HARVEY 0098765434200001 -627021200025998412345 0019500000OA318 JESUS BRADLEY 0098765434200001 -627021200025998412345 0008100000OA260 MARIAH HERNANDEZ 0098765434200001 -627021200025998412345 0007600000OA361 ARIANNA LONG 0098765434200001 -627021200025998412345 0003700000OA265 ARIANNA BISHOP 0098765434200001 -627021200025998412345 0005900000OA254 CARLOS WILLIS 0098765434200001 -627021200025998412345 0025800000OA337 ISABELLA GREGORY 0098765434200001 -627021200025998412345 0011600000OA307 ALLISON SUTTON 0098765434200001 -627021200025998412345 0016200000OA290 COOPER LEZ 0098765434200001 -627021200025998412345 0010000000OA335 SOPHIA BAILEY 0098765434200001 -627021200025998412345 0017700000OA288 CHRISTIAN PEARSON 0098765434200001 -627021200025998412345 0028000000OA367 ISAAC JORDAN 0098765434200001 -627021200025998412345 0026100000OA244 LUKE CRAIG 0098765434200001 -627021200025998412345 0008300000OA278 GRACE HOWARD 0098765434200001 -627021200025998412345 0020300000OA228 SOPHIE NICHOLS 0098765434200001 -627021200025998412345 0027400000OA275 ALEXA WALKER 0098765434200001 -627021200025998412345 0006700000OA310 ADRIAN HOLLAND 0098765434200001 -627021200025998412345 0015400000OA314 GABRIEL MEDINA 0098765434200001 -627021200025998412345 0021600000OA344 KEVIN WOOD 0098765434200001 -627021200025998412345 0004700000OA262 KADEN POWELL 0098765434200001 -627021200025998412345 0009300000OA232 AVA GIBSON 0098765434200001 -627021200025998412345 0018700000OA303 LUCAS THOMPSON 0098765434200001 -627021200025998412345 0008500000OA308 ANGEL MCKINNEY 0098765434200001 -627021200025998412345 0002100000OA274 SEAN BRADLEY 0098765434200001 -627021200025998412345 0019100000OA245 IAN HOLLAND 0098765434200001 -627021200025998412345 0017500000OA323 ISABELLE FOX 0098765434200001 -627021200025998412345 0027400000OA355 ALEXA WALKER 0098765434200001 -627021200025998412345 0026100000OA226 JASON ANDERSON 0098765434200001 -627021200025998412345 0008600000OA243 GIANNA RUIZ 0098765434200001 -627021200025998412345 0011900000OA348 AYDEN DERS 0098765434200001 -627021200025998412345 0015100000OA336 COLTON LOWE 0098765434200001 -627021200025998412345 0002200000OA239 ARIANA THOMPSON 0098765434200001 -627021200025998412345 0025500000OA294 COOPER BARNETT 0098765434200001 -627021200025998412345 0028400000OA325 CAMILA HOLMES 0098765434200001 -627021200025998412345 0025800000OA358 ISABELLA GREGORY 0098765434200001 -627021200025998412345 0025800000OA259 HUNTER MILLS 0098765434200001 -627021200025998412345 0010000000OA279 SOPHIA BAILEY 0098765434200001 -627021200025998412345 0025600000OA293 ANGEL ORTIZ 0098765434200001 -627021200025998412345 0004600000OA252 JOSIAH GRIFFIN 0098765434200001 -627021200025998412345 0011200000OA364 SEBASTIAN MCDONALD 0098765434200001 -627021200025998412345 0022800000OA309 NOAH 0098765434200001 -627021200025998412345 0020500000OA324 AUBREY GRANT 0098765434200001 -627021200025998412345 0022800000OA256 KEVIN PERKINS 0098765434200001 -627021200025998412345 0014600000OA238 HENRY DAY 0098765434200001 -627021200025998412345 0011200000OA297 SEBASTIAN MCDONALD 0098765434200001 -627021200025998412345 0021100000OA282 CARLOS GREGORY 0098765434200001 -627021200025998412345 0009600000OA350 JACOB MORRISON 0098765434200001 -627021200025998412345 0009000000OA291 NATALIE JACOBS 0098765434200001 -627021200025998412345 0025800000OA285 ISABELLA GREGORY 0098765434200001 -627021200025998412345 0028200000OA339 MORGAN NELSON 0098765434200001 -627021200025998412345 0016100000OA353 JORDAN FRANKLIN 0098765434200001 -627021200025998412345 0016400000OA322 ELI BOWMAN 0098765434200001 -627021200025998412345 0003700000OA253 ANGEL SMITH 0098765434200001 -627021200025998412345 0026300000OA247 DIEGO WASHINGTON 0098765434200001 -627021200025998412345 0001900000OA328 ANGEL BROOKS 0098765434200001 -627021200025998412345 0015100000OA236 BAILEY PEREZ 0098765434200001 -627021200025998412345 0021600000OA301 KEVIN WOOD 0098765434200001 -627021200025998412345 0015800000OA331 JACOB LEE 0098765434200001 -627021200025998412345 0011200000OA343 SEBASTIAN MCDONALD 0098765434200001 -627021200025998412345 0026100000OA330 LUKE CRAIG 0098765434200001 -627021200025998412345 0016000000OA280 ABIGAIL ELTON 0098765434200001 -627021200025998412345 0010000000OA356 SOPHIA BAILEY 0098765434200001 -627021200025998412345 0029400000OA251 JAYDEN LYNCH 0098765434200001 -627021200025998412345 0011600000OA321 DESTINY MCDANIEL 0098765434200001 -627021200025998412345 0016600000OA315 JASMINE CARTER 0098765434200001 -627021200025998412345 0004000000OA306 MARY MARSHALL 0098765434200001 -627021200025998412345 0025600000OA362 ANGEL ORTIZ 0098765434200001 -627021200025998412345 0020800000OA258 SYDNEY REYES 0098765434200001 -627021200025998412345 0022800000OA347 NOAH 0098765434200001 -627021200025998412345 0006100000OA248 SYDNEY BUTLER 0098765434200001 -627021200025998412345 0015500000OA327 SOFIA GOMEZ 0098765434200001 -627021200025998412345 0017100000OA240 MARIA MITCHELL 0098765434200001 -627021200025998412345 0029500000OA241 MICHAEL WALLACE 0098765434200001 -627021200025998412345 0015100000OA305 HAYDEN DES 0098765434200001 -627021200025998412345 0006100000OA317 CAMILA T 0098765434200001 -627021200025998412345 0025600000OA341 ANGEL ORTIZ 0098765434200001 -627021200025998412345 0003600000OA329 LILY COLE 0098765434200001 -627021200025998412345 0012700000OA229 BRANDON WILSON 0098765434200001 -627021200025998412345 0018300000OA230 CONNOR GARDNER 0098765434200001 -627021200025998412345 0028200000OA287 MORGAN NELSON 0098765434200001 -627021200025998412345 0021600000OA365 KEVIN WOOD 0098765434200001 -627021200025998412345 0030000000OA326 ISABEL WEBB 0098765434200001 -627021200025998412345 0025600000OA261 MATTHEW ROSE 0098765434200001 -627021200025998412345 0029000000OA286 JACK GAS 0098765434200001 -627021200025998412345 0027400000OA302 JOSEPH DES 0098765434200001 -627021200025998412345 0013500000OA227 SARA NICHOLS 0098765434200001 -627021200025998412345 0027400000OA334 ALEXA WALKER 0098765434200001 -627021200025998412345 0007900000OA225 AIDEN MILES 0098765434200001 -627021200025998412345 0018700000OA366 LUCAS THOMPSON 0098765434200001 -627021200025998412345 0018000000OA234 CONNOR WILLIAMS 0098765434200001 -627021200025998412345 0009500000OA276 GABRIEL JENSEN 0098765434200001 -627021200025998412345 0015000000OA295 JAMES NEWMAN 0098765434200001 -627021200025998412345 0009600000OA320 JACOB MORRISON 0098765434200001 -627021200025998412345 0015800000OA255 JACOB LEE 0098765434200001 -627021200025998412345 0015100000OA284 COLTON LOWE 0098765434200001 -627021200025998412345 0013000000OA271 JUAN NICHOLS 0098765434200001 -627021200025998412345 0008200000OA237 ZOEY DAVIDSON 0098765434200001 -627021200025998412345 0013100000OA296 MARIA FLORES 0098765434200001 -627021200025998412345 0014000000OA277 SYDNEY TUCKER 0098765434200001 -627021200025998412345 0007600000OA289 ARIANNA LONG 0098765434200001 -627021200025998412345 0028000000OA346 ISAAC JORDAN 0098765434200001 -627021200025998412345 0005900000OA246 JESSICA WALKER 0098765434200001 -627021200025998412345 0008700000OA231 ELIZABETH COLE 0098765434200001 -627021200025998412345 0026100000OA351 LUKE CRAIG 0098765434200001 -822500014503074002900000247980000000000000001234567898 042000010000001 -5220TEST COMPANY 11234567898PPDVERIFY 110801110801 1042000010000003 +627021200025998412345 0009900000OA235 DYLAN HUNTER 0098765434200002 +627021200025998412345 0015100000OA357 COLTON LOWE 0098765434200003 +627021200025998412345 0026600000OA264 ISAIAH CARPENTER 0098765434200004 +627021200025998412345 0025500000OA363 COOPER BARNETT 0098765434200005 +627021200025998412345 0022800000OA298 KATHERINE GRAVES 0098765434200006 +627021200025998412345 0016100000OA332 JORDAN FRANKLIN 0098765434200007 +627021200025998412345 0022400000OA270 JACOB SIMPSON 0098765434200008 +627021200025998412345 0008900000OA268 ADAM WILLIS 0098765434200009 +627021200025998412345 0026600000OA354 ISAIAH CARPENTER 0098765434200010 +627021200025998412345 0026600000OA333 ISAIAH CARPENTER 0098765434200011 +627021200025998412345 0029700000OA250 JOSE JORDAN 0098765434200012 +627021200025998412345 0026900000OA300 JULIAN PERKINS 0098765434200013 +627021200025998412345 0029000000OA359 JACK GAS 0098765434200014 +627021200025998412345 0029900000OA281 SOFIA WATSON 0098765434200015 +627021200025998412345 0023800000OA316 ARIANNA KING 0098765434200016 +627021200025998412345 0029400000OA292 AYDEN SIMPSON 0098765434200017 +627021200025998412345 0024700000OA319 SOPHIE HENRY 0098765434200018 +627021200025998412345 0005900000OA267 MAKAYLA TERRY 0098765434200019 +627021200025998412345 0016100000OA257 JORDAN FRANKLIN 0098765434200020 +627021200025998412345 0016600000OA349 JASMINE CARTER 0098765434200021 +627021200025998412345 0015800000OA352 JACOB LEE 0098765434200022 +627021200025998412345 0022900000OA233 LIAM RTINEZ 0098765434200023 +627021200025998412345 0018200000OA283 LILY ROBERTSON 0098765434200024 +627021200025998412345 0028200000OA360 MORGAN NELSON 0098765434200025 +627021200025998412345 0018700000OA345 LUCAS THOMPSON 0098765434200026 +627021200025998412345 0022800000OA368 NOAH 0098765434200027 +627021200025998412345 0007600000OA340 ARIANNA LONG 0098765434200028 +627021200025998412345 0029000000OA338 JACK GAS 0098765434200029 +627021200025998412345 0020700000OA272 MORGAN FOX 0098765434200030 +627021200025998412345 0025500000OA342 COOPER BARNETT 0098765434200031 +627021200025998412345 0018500000OA224 JESSICA SILVA 0098765434200032 +627021200025998412345 0007800000OA273 JASMINE CARTER 0098765434200033 +627021200025998412345 0011700000OA299 ISABELLE ERRY 0098765434200034 +627021200025998412345 0006300000OA263 ZACHARY MEDINA 0098765434200035 +627021200025998412345 0001800000OA266 MATTHEW ALLEN 0098765434200036 +627021200025998412345 0015000000OA312 BROOKE SUTTON 0098765434200037 +627021200025998412345 0006600000OA242 ABIGAIL EVANS 0098765434200038 +627021200025998412345 0028000000OA304 ISAAC JORDAN 0098765434200039 +627021200025998412345 0007800000OA269 JUAN GILBERT 0098765434200040 +627021200025998412345 0020800000OA249 KEVIN CASTILLO 0098765434200041 +627021200025998412345 0010600000OA311 EVAN HARVEY 0098765434200042 +627021200025998412345 0019500000OA318 JESUS BRADLEY 0098765434200043 +627021200025998412345 0008100000OA260 MARIAH HERNANDEZ 0098765434200044 +627021200025998412345 0007600000OA361 ARIANNA LONG 0098765434200045 +627021200025998412345 0003700000OA265 ARIANNA BISHOP 0098765434200046 +627021200025998412345 0005900000OA254 CARLOS WILLIS 0098765434200047 +627021200025998412345 0025800000OA337 ISABELLA GREGORY 0098765434200048 +627021200025998412345 0011600000OA307 ALLISON SUTTON 0098765434200049 +627021200025998412345 0016200000OA290 COOPER LEZ 0098765434200050 +627021200025998412345 0010000000OA335 SOPHIA BAILEY 0098765434200051 +627021200025998412345 0017700000OA288 CHRISTIAN PEARSON 0098765434200052 +627021200025998412345 0028000000OA367 ISAAC JORDAN 0098765434200053 +627021200025998412345 0026100000OA244 LUKE CRAIG 0098765434200054 +627021200025998412345 0008300000OA278 GRACE HOWARD 0098765434200055 +627021200025998412345 0020300000OA228 SOPHIE NICHOLS 0098765434200056 +627021200025998412345 0027400000OA275 ALEXA WALKER 0098765434200057 +627021200025998412345 0006700000OA310 ADRIAN HOLLAND 0098765434200058 +627021200025998412345 0015400000OA314 GABRIEL MEDINA 0098765434200059 +627021200025998412345 0021600000OA344 KEVIN WOOD 0098765434200060 +627021200025998412345 0004700000OA262 KADEN POWELL 0098765434200061 +627021200025998412345 0009300000OA232 AVA GIBSON 0098765434200062 +627021200025998412345 0018700000OA303 LUCAS THOMPSON 0098765434200063 +627021200025998412345 0008500000OA308 ANGEL MCKINNEY 0098765434200064 +627021200025998412345 0002100000OA274 SEAN BRADLEY 0098765434200065 +627021200025998412345 0019100000OA245 IAN HOLLAND 0098765434200066 +627021200025998412345 0017500000OA323 ISABELLE FOX 0098765434200067 +627021200025998412345 0027400000OA355 ALEXA WALKER 0098765434200068 +627021200025998412345 0026100000OA226 JASON ANDERSON 0098765434200069 +627021200025998412345 0008600000OA243 GIANNA RUIZ 0098765434200070 +627021200025998412345 0011900000OA348 AYDEN DERS 0098765434200071 +627021200025998412345 0015100000OA336 COLTON LOWE 0098765434200072 +627021200025998412345 0002200000OA239 ARIANA THOMPSON 0098765434200073 +627021200025998412345 0025500000OA294 COOPER BARNETT 0098765434200074 +627021200025998412345 0028400000OA325 CAMILA HOLMES 0098765434200075 +627021200025998412345 0025800000OA358 ISABELLA GREGORY 0098765434200076 +627021200025998412345 0025800000OA259 HUNTER MILLS 0098765434200077 +627021200025998412345 0010000000OA279 SOPHIA BAILEY 0098765434200078 +627021200025998412345 0025600000OA293 ANGEL ORTIZ 0098765434200079 +627021200025998412345 0004600000OA252 JOSIAH GRIFFIN 0098765434200080 +627021200025998412345 0011200000OA364 SEBASTIAN MCDONALD 0098765434200081 +627021200025998412345 0022800000OA309 NOAH 0098765434200082 +627021200025998412345 0020500000OA324 AUBREY GRANT 0098765434200083 +627021200025998412345 0022800000OA256 KEVIN PERKINS 0098765434200084 +627021200025998412345 0014600000OA238 HENRY DAY 0098765434200085 +627021200025998412345 0011200000OA297 SEBASTIAN MCDONALD 0098765434200086 +627021200025998412345 0021100000OA282 CARLOS GREGORY 0098765434200087 +627021200025998412345 0009600000OA350 JACOB MORRISON 0098765434200088 +627021200025998412345 0009000000OA291 NATALIE JACOBS 0098765434200089 +627021200025998412345 0025800000OA285 ISABELLA GREGORY 0098765434200090 +627021200025998412345 0028200000OA339 MORGAN NELSON 0098765434200091 +627021200025998412345 0016100000OA353 JORDAN FRANKLIN 0098765434200092 +627021200025998412345 0016400000OA322 ELI BOWMAN 0098765434200093 +627021200025998412345 0003700000OA253 ANGEL SMITH 0098765434200094 +627021200025998412345 0026300000OA247 DIEGO WASHINGTON 0098765434200095 +627021200025998412345 0001900000OA328 ANGEL BROOKS 0098765434200096 +627021200025998412345 0015100000OA236 BAILEY PEREZ 0098765434200097 +627021200025998412345 0021600000OA301 KEVIN WOOD 0098765434200098 +627021200025998412345 0015800000OA331 JACOB LEE 0098765434200099 +627021200025998412345 0011200000OA343 SEBASTIAN MCDONALD 0098765434200100 +627021200025998412345 0026100000OA330 LUKE CRAIG 0098765434200101 +627021200025998412345 0016000000OA280 ABIGAIL ELTON 0098765434200102 +627021200025998412345 0010000000OA356 SOPHIA BAILEY 0098765434200103 +627021200025998412345 0029400000OA251 JAYDEN LYNCH 0098765434200104 +627021200025998412345 0011600000OA321 DESTINY MCDANIEL 0098765434200105 +627021200025998412345 0016600000OA315 JASMINE CARTER 0098765434200106 +627021200025998412345 0004000000OA306 MARY MARSHALL 0098765434200107 +627021200025998412345 0025600000OA362 ANGEL ORTIZ 0098765434200108 +627021200025998412345 0020800000OA258 SYDNEY REYES 0098765434200109 +627021200025998412345 0022800000OA347 NOAH 0098765434200110 +627021200025998412345 0006100000OA248 SYDNEY BUTLER 0098765434200111 +627021200025998412345 0015500000OA327 SOFIA GOMEZ 0098765434200112 +627021200025998412345 0017100000OA240 MARIA MITCHELL 0098765434200113 +627021200025998412345 0029500000OA241 MICHAEL WALLACE 0098765434200114 +627021200025998412345 0015100000OA305 HAYDEN DES 0098765434200115 +627021200025998412345 0006100000OA317 CAMILA T 0098765434200116 +627021200025998412345 0025600000OA341 ANGEL ORTIZ 0098765434200117 +627021200025998412345 0003600000OA329 LILY COLE 0098765434200118 +627021200025998412345 0012700000OA229 BRANDON WILSON 0098765434200119 +627021200025998412345 0018300000OA230 CONNOR GARDNER 0098765434200120 +627021200025998412345 0028200000OA287 MORGAN NELSON 0098765434200121 +627021200025998412345 0021600000OA365 KEVIN WOOD 0098765434200122 +627021200025998412345 0030000000OA326 ISABEL WEBB 0098765434200123 +627021200025998412345 0025600000OA261 MATTHEW ROSE 0098765434200124 +627021200025998412345 0029000000OA286 JACK GAS 0098765434200125 +627021200025998412345 0027400000OA302 JOSEPH DES 0098765434200126 +627021200025998412345 0013500000OA227 SARA NICHOLS 0098765434200127 +627021200025998412345 0027400000OA334 ALEXA WALKER 0098765434200128 +627021200025998412345 0007900000OA225 AIDEN MILES 0098765434200129 +627021200025998412345 0018700000OA366 LUCAS THOMPSON 0098765434200130 +627021200025998412345 0018000000OA234 CONNOR WILLIAMS 0098765434200131 +627021200025998412345 0009500000OA276 GABRIEL JENSEN 0098765434200132 +627021200025998412345 0015000000OA295 JAMES NEWMAN 0098765434200133 +627021200025998412345 0009600000OA320 JACOB MORRISON 0098765434200134 +627021200025998412345 0015800000OA255 JACOB LEE 0098765434200135 +627021200025998412345 0015100000OA284 COLTON LOWE 0098765434200136 +627021200025998412345 0013000000OA271 JUAN NICHOLS 0098765434200137 +627021200025998412345 0008200000OA237 ZOEY DAVIDSON 0098765434200138 +627021200025998412345 0013100000OA296 MARIA FLORES 0098765434200139 +627021200025998412345 0014000000OA277 SYDNEY TUCKER 0098765434200140 +627021200025998412345 0007600000OA289 ARIANNA LONG 0098765434200141 +627021200025998412345 0028000000OA346 ISAAC JORDAN 0098765434200142 +627021200025998412345 0005900000OA246 JESSICA WALKER 0098765434200143 +627021200025998412345 0008700000OA231 ELIZABETH COLE 0098765434200144 +627021200025998412345 0026100000OA351 LUKE CRAIG 0098765434200145 +822500014503074002900024798000000000000000001234567898 098765430000001 +5220TEST COMPANY 11234567898PPDVERIFY 110801110801 1098765430000003 622021200025998412345 0000001800OA370 CHASE BLACK 0098765434200001 -622021200025998412345 0000001300OA379 ANDREA BUTLER 0098765434200001 -622021200025998412345 0000000700OA371 BLAKE REYES 0098765434200001 -622021200025998412345 0000000300OA385 CONNOR MEDINA 0098765434200001 -622021200025998412345 0000001100OA369 CHASE BLACK 0098765434200001 -622021200025998412345 0000001300OA384 BRANDON ARMSTRONG 0098765434200001 -622021200025998412345 0000000300OA375 LUIS MEYER 0098765434200001 -622021200025998412345 0000001300OA374 BLAKE HICKS 0098765434200001 -622021200025998412345 0000000400OA372 BLAKE REYES 0098765434200001 -622021200025998412345 0000000100OA377 DOMINIC RAY 0098765434200001 -622021200025998412345 0000001900OA382 JONATHAN MORALES 0098765434200001 -622021200025998412345 0000000400OA381 JONATHAN MORALES 0098765434200001 -622021200025998412345 0000000200OA378 DOMINIC RAY 0098765434200001 -622021200025998412345 0000001100OA376 LUIS MEYER 0098765434200001 -622021200025998412345 0000002000OA373 BLAKE HICKS 0098765434200001 -622021200025998412345 0000000200OA386 CONNOR MEDINA 0098765434200001 -622021200025998412345 0000001600OA380 ANDREA BUTLER 0098765434200001 -622021200025998412345 0000001200OA383 BRANDON ARMSTRONG 0098765434200001 -822000001800381600360000000000000000000001721234567898 042000010000003 +622021200025998412345 0000001300OA379 ANDREA BUTLER 0098765434200002 +622021200025998412345 0000000700OA371 BLAKE REYES 0098765434200003 +622021200025998412345 0000000300OA385 CONNOR MEDINA 0098765434200004 +622021200025998412345 0000001100OA369 CHASE BLACK 0098765434200005 +622021200025998412345 0000001300OA384 BRANDON ARMSTRONG 0098765434200006 +622021200025998412345 0000000300OA375 LUIS MEYER 0098765434200007 +622021200025998412345 0000001300OA374 BLAKE HICKS 0098765434200008 +622021200025998412345 0000000400OA372 BLAKE REYES 0098765434200009 +622021200025998412345 0000000100OA377 DOMINIC RAY 0098765434200010 +622021200025998412345 0000001900OA382 JONATHAN MORALES 0098765434200011 +622021200025998412345 0000000400OA381 JONATHAN MORALES 0098765434200012 +622021200025998412345 0000000200OA378 DOMINIC RAY 0098765434200013 +622021200025998412345 0000001100OA376 LUIS MEYER 0098765434200014 +622021200025998412345 0000002000OA373 BLAKE HICKS 0098765434200015 +622021200025998412345 0000000200OA386 CONNOR MEDINA 0098765434200016 +622021200025998412345 0000001600OA380 ANDREA BUTLER 0098765434200017 +622021200025998412345 0000001200OA383 BRANDON ARMSTRONG 0098765434200018 +822000001800381600360000000000000000000172001234567898 098765430000003 5220TEST COMPANY 11234567898PPDTEST SALES110801110801 1042000010000002 822000000000000000000000000000000000000000001234567898 042000010000002 -5225 FV3 CA1234567898IATTEST BUYS USDCAD110802 1042000010000004 -6270910502340 0012200000998412345 1098765430420000 +5225 FV3 CA1234567898IATTEST BUYS USDCAD110802 1098765430000004 +627091050234007 0012200000998412345 1098765430000001 710WEB DIEGO MAY 0000001 711TEST COMPANY 123 EASY STREET 0000001 712ANYTOWN*KS\ US*12345\ 0000001 @@ -177,7 +177,7 @@ 714CENTRAL 01021200025 CA 0000001 715OA391 PO Box 450 0000001 716Metropolis*ON\ CA*01234\ 0000001 -6270910502340 0004000000998412345 1098765430420000 +627091050234007 0004000000998412345 1098765430000002 710WEB JOSHUA SILVA 0000002 711TEST COMPANY 123 EASY STREET 0000002 712ANYTOWN*KS\ US*12345\ 0000002 @@ -185,7 +185,7 @@ 714CENTRAL 01021200025 CA 0000002 715OA389 PO Box 230 0000002 716Metropolis*ON\ CA*01234\ 0000002 -6270910502340 0012200000998412345 1098765430420000 +627091050234007 0012200000998412345 1098765430000003 710WEB DIEGO MAY 0000003 711TEST COMPANY 123 EASY STREET 0000003 712ANYTOWN*KS\ US*12345\ 0000003 @@ -193,7 +193,7 @@ 714CENTRAL 01021200025 CA 0000003 715OA398 PO Box 450 0000003 716Metropolis*ON\ CA*01234\ 0000003 -6270910502340 0026800000998412345 1098765430420000 +6270910502340007 0026800000998412345 1098765430000004 710WEB ZOEY PAYNE 0000004 711TEST COMPANY 123 EASY STREET 0000004 712ANYTOWN*KS\ US*12345\ 0000004 @@ -201,7 +201,7 @@ 714CENTRAL 01021200025 CA 0000004 715OA392 PO Box 150 0000004 716Metropolis*ON\ CA*01234\ 0000004 -6270910502340 0012200000998412345 1098765430420000 +627091050234007 0012200000998412345 1098765430000005 710WEB DIEGO MAY 0000005 711TEST COMPANY 123 EASY STREET 0000005 712ANYTOWN*KS\ US*12345\ 0000005 @@ -209,7 +209,7 @@ 714CENTRAL 01021200025 CA 0000005 715OA395 PO Box 450 0000005 716Metropolis*ON\ CA*01234\ 0000005 -6270910502340 0014800000998412345 1098765430420000 +627091050234007 0014800000998412345 1098765430000006 710WEB DYLAN RAMOS 0000006 711TEST COMPANY 123 EASY STREET 0000006 712ANYTOWN*KS\ US*12345\ 0000006 @@ -217,7 +217,7 @@ 714CENTRAL 01021200025 CA 0000006 715OA390 PO Box 800 0000006 716Metropolis*ON\ CA*01234\ 0000006 -6270910502340 0026800000998412345 1098765430420000 +627091050234007 0026800000998412345 1098765430000007 710WEB ZOEY PAYNE 0000007 711TEST COMPANY 123 EASY STREET 0000007 712ANYTOWN*KS\ US*12345\ 0000007 @@ -225,7 +225,7 @@ 714CENTRAL 01021200025 CA 0000007 715OA396 PO Box 150 0000007 716Metropolis*ON\ CA*01234\ 0000007 -6270910502340 0020300000998412345 1098765430420000 +627091050234007 0020300000998412345 1098765430000008 710WEB JAMES ROMERO 0000008 711TEST COMPANY 123 EASY STREET 0000008 712ANYTOWN*KS\ US*12345\ 0000008 @@ -233,7 +233,7 @@ 714CENTRAL 01021200025 CA 0000008 715OA393 PO Box 810 0000008 716Metropolis*ON\ CA*01234\ 0000008 -6270910502340 0008700000998412345 1098765430420000 +627091050234007 0008700000998412345 1098765430000009 710WEB KEVIN LARSON 0000009 711TEST COMPANY 123 EASY STREET 0000009 712ANYTOWN*KS\ US*12345\ 0000009 @@ -241,7 +241,7 @@ 714CENTRAL 01021200025 CA 0000009 715OA387 PO Box 340 0000009 716Metropolis*ON\ CA*01234\ 0000009 -6270910502340 0008700000998412345 1098765430420000 +627091050234007 0008700000998412345 1098765430000010 710WEB KEVIN LARSON 0000010 711TEST COMPANY 123 EASY STREET 0000010 712ANYTOWN*KS\ US*12345\ 0000010 @@ -249,7 +249,7 @@ 714CENTRAL 01021200025 CA 0000010 715OA394 PO Box 340 0000010 716Metropolis*ON\ CA*01234\ 0000010 -6270910502340 0025100000998412345 1098765430420000 +627091050234007 0025100000998412345 1098765430000011 710WEB SEBASTIAN NEWMAN 0000011 711TEST COMPANY 123 EASY STREET 0000011 712ANYTOWN*KS\ US*12345\ 0000011 @@ -257,7 +257,7 @@ 714CENTRAL 01021200025 CA 0000011 715OA388 PO Box 600 0000011 716Metropolis*ON\ CA*01234\ 0000011 -6270910502340 0026800000998412345 1098765430420000 +627091050234007 0026800000998412345 1098765430000012 710WEB ZOEY PAYNE 0000012 711TEST COMPANY 123 EASY STREET 0000012 712ANYTOWN*KS\ US*12345\ 0000012 @@ -265,7 +265,7 @@ 714CENTRAL 01021200025 CA 0000012 715OA399 PO Box 150 0000012 716Metropolis*ON\ CA*01234\ 0000012 -6270910502340 0008700000998412345 1098765430420000 +627091050234007 0008700000998412345 1098765430000013 710WEB KEVIN LARSON 0000013 711TEST COMPANY 123 EASY STREET 0000013 712ANYTOWN*KS\ US*12345\ 0000013 @@ -273,23 +273,23 @@ 714CENTRAL 01021200025 CA 0000013 715OA397 PO Box 340 0000013 716Metropolis*ON\ CA*01234\ 0000013 -822500010401183652990000020730000000000000001234567898 042000010000004 -5220 FV3 CA1234567898IATVERIFY USDCAD110802 1042000010000005 -6220910502340 0000001200998412345 1098765430420000 -710WEB LEAH BRYANT 0000014 -711TEST COMPANY 123 EASY STREET 0000014 -712ANYTOWN*KS\ US*12345\ 0000014 -713U.S. BANK 0104200001 US 0000014 -714CENTRAL 01021200025 CA 0000014 -715OA400 PO Box 410 0000014 -716Metropolis*ON\ CA*01234\ 0000014 -6220910502340 0000000100998412345 1098765430420000 -710WEB LEAH BRYANT 0000015 -711TEST COMPANY 123 EASY STREET 0000015 -712ANYTOWN*KS\ US*12345\ 0000015 -713U.S. BANK 0104200001 US 0000015 -714CENTRAL 01021200025 CA 0000015 -715OA401 PO Box 410 0000015 -716Metropolis*ON\ CA*01234\ 0000015 -822000001600182100460000000000000000000000131234567898 042000010000005 -9000005000030000002830482135671000026871000000000000185 \ No newline at end of file +822500010401183652990002073000000000000000001234567898 098765430000004 +5220 FV3 CA1234567898IATVERIFY USDCAD110802 1098765430000005 +622091050234007 0000001200998412345 1098765430000001 +710WEB LEAH BRYANT 0000001 +711TEST COMPANY 123 EASY STREET 0000001 +712ANYTOWN*KS\ US*12345\ 0000001 +713U.S. BANK 0104200001 US 0000001 +714CENTRAL 01021200025 CA 0000001 +715OA400 PO Box 410 0000001 +716Metropolis*ON\ CA*01234\ 0000001 +622091050234007 0000000100998412345 1098765430000002 +710WEB LEAH BRYANT 0000002 +711TEST COMPANY 123 EASY STREET 0000002 +712ANYTOWN*KS\ US*12345\ 0000002 +713U.S. BANK 0104200001 US 0000002 +714CENTRAL 01021200025 CA 0000002 +715OA401 PO Box 410 0000002 +716Metropolis*ON\ CA*01234\ 0000002 +822000001600182100460000000000000000000013001234567898 098765430000005 +9000005000030000002830482135671002687100000000000018500 \ No newline at end of file diff --git a/test/data/20110805A.ach b/test/data/20110805A.ach index 799cd5a26..523f29e9e 100644 --- a/test/data/20110805A.ach +++ b/test/data/20110805A.ach @@ -92,4 +92,4 @@ 715A258 PO Box 160 0000002 716Metropolis*ON\ CA*01234\ 0000002 822000001600182100460000000000000000000000240123456789 042000010000005 -9000005000010000000830136685201000005101000000000000200 +9000005000010000000830136685201000005101000000000000200000000000000000000000000000000000000000 From cd0669a5515e12ba1677043f5d0eb2cf521004f5 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 12 Jul 2018 17:28:40 -0400 Subject: [PATCH 0306/1694] #211 Additional Code Coverage #211 Additional Code Coverage In batch.go and IATBatch.go return error in isCategory if there are no entries. Updated reader_test.go to check for entries errors. Added message msgIATBatchAddendaIndicator --- batch.go | 5 +- batch_test.go | 7 ++ iatBatch.go | 10 +- iatBatch_test.go | 240 +++++++++++++++++++++++++++++++++++++++-- iatEntryDetail_test.go | 4 +- reader.go | 2 +- reader_test.go | 34 +++++- 7 files changed, 278 insertions(+), 24 deletions(-) diff --git a/batch.go b/batch.go index 4f1736bda..dfbdd4358 100644 --- a/batch.go +++ b/batch.go @@ -406,9 +406,8 @@ func (batch *batch) isTypeCode(typeCode string) error { // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *batch) isCategory() error { - // ToDo: Add temporarily - ./test/data/20110805A.ach contains a batch without a detail entry - if len(batch.GetEntries()) == 0 { - return nil + if len(batch.Entries) <= 0 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} } category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { diff --git a/batch_test.go b/batch_test.go index 47e9b14ce..24ddeafd6 100644 --- a/batch_test.go +++ b/batch_test.go @@ -125,6 +125,7 @@ func BenchmarkCreditBatchisBatchAmount(b *testing.B) { } } + func testSavingsBatchisBatchAmount(t testing.TB) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) @@ -524,6 +525,7 @@ func BenchmarkBatchFieldInclusion(b *testing.B) { } } +// testBatchInvalidTraceNumberODFI validates TraceNumberODFI func testBatchInvalidTraceNumberODFI(t testing.TB) { mockBatch := mockBatchInvalidTraceNumberODFI() if err := mockBatch.build(); err != nil { @@ -531,10 +533,12 @@ func testBatchInvalidTraceNumberODFI(t testing.TB) { } } +// TestBatchInvalidTraceNumberODFI tests validating TraceNumberODFI func TestBatchInvalidTraceNumberODFI(t *testing.T) { testBatchInvalidTraceNumberODFI(t) } +// BenchmarkBatchInvalidTraceNumberODFI benchmarks validating TraceNumberODFI func BenchmarkBatchInvalidTraceNumberODFI(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -566,6 +570,7 @@ func BenchmarkBatchNoEntry(b *testing.B) { } } +// testBatchControl validates BatchControl ODFIIdentification func testBatchControl(t testing.TB) { mockBatch := mockBatch() mockBatch.Control.ODFIIdentification = "" @@ -580,10 +585,12 @@ func testBatchControl(t testing.TB) { } } +// TestBatchControl tests validating BatchControl ODFIIdentification func TestBatchControl(t *testing.T) { testBatchControl(t) } +// BenchmarkBatchControl benchmarks validating BatchControl ODFIIdentification func BenchmarkBatchControl(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { diff --git a/iatBatch.go b/iatBatch.go index f26695df8..db7b8b95b 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -10,7 +10,8 @@ import ( ) var ( - msgIATBatchAddendaRequired = "is required for an IAT detail entry" + msgIATBatchAddendaRequired = "is required for an IAT detail entry" + msgIATBatchAddendaIndicator = "is invalid for addenda record(s) found" ) // IATBatch holds the Batch Header and Batch Control and all Entry Records for an IAT batch @@ -349,7 +350,7 @@ func (batch *IATBatch) isAddendaSequence() error { for _, entry := range batch.Entries { // addenda without indicator flag of 1 if entry.AddendaRecordIndicator != 1 { - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator} + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgIATBatchAddendaIndicator} } // Verify Addenda* entry detail sequence numbers are valid entryTN := entry.TraceNumberField()[8:] @@ -389,9 +390,8 @@ func (batch *IATBatch) isAddendaSequence() error { // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *IATBatch) isCategory() error { - // ToDo: Add temporarily - ./test/data/20110805A.ach contains a batch without a detail entry - if len(batch.GetEntries()) == 0 { - return nil + if len(batch.Entries) <= 0 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} } category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { diff --git a/iatBatch_test.go b/iatBatch_test.go index ca0259244..2f0a087ab 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -518,6 +518,178 @@ func BenchmarkIATServiceClassCodeMismatch(b *testing.B) { } } +// testIATBatchCreditIsBatchAmount validates credit isBatchAmount +func testIATBatchCreditIsBatchAmount(t testing.TB) { + mockBatch := mockIATBatch() + e1 := mockBatch.GetEntries()[0] + e2 := mockIATEntryDetail() + e2.TransactionCode = 22 + e2.Amount = 5000 + e2.TraceNumber = e1.TraceNumber + 10 + mockBatch.AddEntry(e2) + mockBatch.Entries[1].Addenda10 = mockAddenda10() + mockBatch.Entries[1].Addenda11 = mockAddenda11() + mockBatch.Entries[1].Addenda12 = mockAddenda12() + mockBatch.Entries[1].Addenda13 = mockAddenda13() + mockBatch.Entries[1].Addenda14 = mockAddenda14() + mockBatch.Entries[1].Addenda15 = mockAddenda15() + mockBatch.Entries[1].Addenda16 = mockAddenda16() + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + mockBatch.GetControl().TotalCreditEntryDollarAmount = 1000 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TotalCreditEntryDollarAmount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchCreditIsBatchAmount tests validating credit isBatchAmount +func TestIATBatchCreditIsBatchAmount(t *testing.T) { + testIATBatchCreditIsBatchAmount(t) +} + +// BenchmarkIATBatchCreditIsBatchAmount benchmarks validating credit isBatchAmount +func BenchmarkIATBatchCreditIsBatchAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchCreditIsBatchAmount(b) + } + +} + +// testIATBatchDebitIsBatchAmount validates debit isBatchAmount +func testIATBatchDebitIsBatchAmount(t testing.TB) { + mockBatch := mockIATBatch() + e1 := mockBatch.GetEntries()[0] + e1.TransactionCode = 27 + e2 := mockIATEntryDetail() + e2.TransactionCode = 27 + e2.Amount = 5000 + e2.TraceNumber = e1.TraceNumber + 10 + mockBatch.AddEntry(e2) + mockBatch.Entries[1].Addenda10 = mockAddenda10() + mockBatch.Entries[1].Addenda11 = mockAddenda11() + mockBatch.Entries[1].Addenda12 = mockAddenda12() + mockBatch.Entries[1].Addenda13 = mockAddenda13() + mockBatch.Entries[1].Addenda14 = mockAddenda14() + mockBatch.Entries[1].Addenda15 = mockAddenda15() + mockBatch.Entries[1].Addenda16 = mockAddenda16() + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + mockBatch.GetControl().TotalDebitEntryDollarAmount = 1000 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TotalDebitEntryDollarAmount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchDebitIsBatchAmount tests validating debit isBatchAmount +func TestIATBatchDebitIsBatchAmount(t *testing.T) { + testIATBatchDebitIsBatchAmount(t) +} + +// BenchmarkIATBatchDebitIsBatchAmount benchmarks validating debit isBatchAmount +func BenchmarkIATBatchDebitIsBatchAmount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchDebitIsBatchAmount(b) + } + +} + +// testIATBatchFieldInclusion validates IATBatch FieldInclusion +func testIATBatchFieldInclusion(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch2 := mockIATBatch() + mockBatch2.Header.recordType = "4" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + if err := mockBatch2.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + if err := mockBatch2.build(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchFieldInclusion tests validating IATBatch FieldInclusion +func TestIATBatchFieldInclusion(t *testing.T) { + testIATBatchFieldInclusion(t) +} + +// BenchmarkIATBatchFieldInclusion benchmarks validating IATBatch FieldInclusion +func BenchmarkIATBatchFieldInclusion(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchFieldInclusion(b) + } + +} + +// testIATBatchBuildError validates IATBatch build error +func testIATBatchBuild(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + + if err := mockBatch.build(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchBuild tests validating IATBatch build error +func TestIATBatchBuild(t *testing.T) { + testIATBatchBuild(t) +} + +// BenchmarkIATBatchBuild benchmarks validating IATBatch build error +func BenchmarkIATBatchBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchBuild(b) + } + +} + // testIATODFIIdentificationMismatch validates ODFIIdentification mismatch func testIATODFIIdentificationMismatch(t testing.TB) { mockBatch := mockIATBatch() @@ -546,10 +718,10 @@ func BenchmarkIATODFIIdentificationMismatch(b *testing.B) { } } -// testIATAddendaRecordIndicator validates AddendaRecordIndicator FieldInclusion -func testIATAddendaRecordIndicator(t testing.TB) { +// testIATBatchAddendaRecordIndicator validates IATEntryDetail AddendaRecordIndicator +func testIATBatchAddendaRecordIndicator(t testing.TB) { mockBatch := mockIATBatch() - mockBatch.GetEntries()[0].AddendaRecordIndicator = 0 + mockBatch.GetEntries()[0].AddendaRecordIndicator = 2 if err := mockBatch.verify(); err != nil { if e, ok := err.(*BatchError); ok { if e.FieldName != "AddendaRecordIndicator" { @@ -561,15 +733,65 @@ func testIATAddendaRecordIndicator(t testing.TB) { } } -// TestIATAddendaRecordIndicator tests validating AddendaRecordIndicator FieldInclusion -func TestIATAddendaRecordIndicator(t *testing.T) { - testIATAddendaRecordIndicator(t) +// TestIATBatchAddendaRecordIndicator tests validating IATEntryDetail AddendaRecordIndicator +func TestIATBatchAddendaRecordIndicator(t *testing.T) { + testIATBatchAddendaRecordIndicator(t) +} + +// BenchmarkIATBatchAddendaRecordIndicator benchmarks IATEntryDetail AddendaRecordIndicator +func BenchmarkIATBatchAddendaRecordIndicator(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddendaRecordIndicator(b) + } +} + +// testIATBatchInvalidTraceNumberODFI validates TraceNumberODFI +func testIATBatchInvalidTraceNumberODFI(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetEntries()[0].SetTraceNumber("9928272", 1) + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestIATBatchInvalidTraceNumberODFI tests validating TraceNumberODFI +func TestIATBatchInvalidTraceNumberODFI(t *testing.T) { + testIATBatchInvalidTraceNumberODFI(t) +} + +// BenchmarkIATBatchInvalidTraceNumberODFI benchmarks validating TraceNumberODFI +func BenchmarkIATBatchInvalidTraceNumberODFI(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchInvalidTraceNumberODFI(b) + } +} + +// testIATBatchControl validates BatchControl ODFIIdentification +func testIATBatchControl(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.Control.ODFIIdentification = "" + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchControl tests validating BatchControl ODFIIdentification +func TestIATBatchControl(t *testing.T) { + testIATBatchControl(t) } -// BenchmarkIATAddendaRecordIndicator benchmarks validating AddendaRecordIndicator FieldInclusion -func BenchmarkIATAddendaRecordIndicator(b *testing.B) { +// BenchmarkIATBatchControl benchmarks validating BatchControl ODFIIdentification +func BenchmarkIATBatchControl(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testIATAddendaRecordIndicator(b) + testIATBatchControl(b) } } diff --git a/iatEntryDetail_test.go b/iatEntryDetail_test.go index cc27c2e7a..4d92a18fd 100644 --- a/iatEntryDetail_test.go +++ b/iatEntryDetail_test.go @@ -14,9 +14,9 @@ func mockIATEntryDetail() *IATEntryDetail { entry := NewIATEntryDetail() entry.TransactionCode = 22 entry.SetRDFI("121042882") - entry.AddendaRecords = 7 + entry.AddendaRecords = 007 entry.DFIAccountNumber = "123456789" - entry.Amount = 100000 + entry.Amount = 100000 // 1000.00 entry.SetTraceNumber(mockIATBatchHeaderFF().ODFIIdentification, 1) entry.Category = CategoryForward return entry diff --git a/reader.go b/reader.go index 64039d3e4..56efa2cad 100644 --- a/reader.go +++ b/reader.go @@ -491,7 +491,7 @@ func (r *Reader) parseIATAddenda() error { r.IATCurrentBatch.GetEntries()[entryIndex].Addenda17 = addenda17 } } else { - msg := fmt.Sprint(msgBatchAddendaIndicator) + msg := fmt.Sprint(msgIATBatchAddendaIndicator) return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) } diff --git a/reader_test.go b/reader_test.go index f85903f26..1c9d86444 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1008,10 +1008,23 @@ func testACHFileRead(t testing.TB) { defer f.Close() r := NewReader(f) _, err = r.Read() - if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", e, e) + } + } + } else { t.Errorf("%T: %s", err, err) } - if err = r.File.Validate(); err != nil { + + err = r.File.Validate() + + if e, ok := err.(*FileError); ok { + if e.FieldName != "BatchCount" { + t.Errorf("%T: %s", e, e) + } + } else { t.Errorf("%T: %s", err, err) } } @@ -1038,10 +1051,23 @@ func testACHFileRead2(t testing.TB) { defer f.Close() r := NewReader(f) _, err = r.Read() - if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", e, e) + } + } + } else { t.Errorf("%T: %s", err, err) } - if err = r.File.Validate(); err != nil { + + err = r.File.Validate() + + if e, ok := err.(*FileError); ok { + if e.FieldName != "BatchCount" { + t.Errorf("%T: %s", e, e) + } + } else { t.Errorf("%T: %s", err, err) } } From 385b025f415d339c0eff137ece5056e942ac8a1f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 12 Jul 2018 17:33:03 -0400 Subject: [PATCH 0307/1694] #211 gofmt #211 gofmt --- iatEntryDetail_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iatEntryDetail_test.go b/iatEntryDetail_test.go index 4d92a18fd..122dbd056 100644 --- a/iatEntryDetail_test.go +++ b/iatEntryDetail_test.go @@ -16,7 +16,7 @@ func mockIATEntryDetail() *IATEntryDetail { entry.SetRDFI("121042882") entry.AddendaRecords = 007 entry.DFIAccountNumber = "123456789" - entry.Amount = 100000 // 1000.00 + entry.Amount = 100000 // 1000.00 entry.SetTraceNumber(mockIATBatchHeaderFF().ODFIIdentification, 1) entry.Category = CategoryForward return entry From 0255f2cc8becdb32b1ae53a84381b3162019035b Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Thu, 12 Jul 2018 19:29:26 -0500 Subject: [PATCH 0308/1694] GetBatches & DeleteBatches --- cmd/server/main.go | 4 ++- server/endpoints.go | 47 ++++++++++++++++++++++++++++++++++++ server/middlewear.go | 14 +++++++++++ server/repositoryInMemory.go | 28 ++++++++++++++++++++- server/service.go | 16 ++++++++++++ server/service_test.go | 12 +++++++-- server/transport.go | 42 ++++++++++++++++++++++++++++++++ 7 files changed, 159 insertions(+), 4 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 79ead5219..bedd426d4 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -26,14 +26,16 @@ DeleteFile curl -H "Content-Type: application/json" -X DELETE http://localhost:8080/files/1234 CreateBatch -curl -d '{"id":"54321","serviceClassCode":"220","standardEntryClassCode":"WEB","companyName":"Your Company inc","companyIdentification":"121042882","companyEntryDescription":"Online Order","ODFIIdentification","12104288"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/08B751B2/batches/ +curl -d '{"id":"54321","serviceClassCode":220,"standardEntryClassCode":"WEB","companyName":"Your Company inc","companyIdentification":"121042882","companyEntryDescription":"Online Order","ODFIIdentification":"12104288"}' -H "Content-Type: application/json" -X POST http://localhost:8080/files/08B751B2/batches/ GetBatch curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/08B751B2/batches/54321 GetBatches +curl -H "Content-Type: application/json" -X GET http://localhost:8080/files/08B751B2/batches/ DeleteBatch +curl -H "Content-Type: application/json" -X DELETE http://localhost:8080/files/08B751B2/batches/54321 **/ func main() { diff --git a/server/endpoints.go b/server/endpoints.go index 454841e62..44db84456 100644 --- a/server/endpoints.go +++ b/server/endpoints.go @@ -13,7 +13,9 @@ type Endpoints struct { GetFilesEndpoint endpoint.Endpoint DeleteFileEndpoint endpoint.Endpoint CreateBatchEndpoint endpoint.Endpoint + GetBatchesEndpoint endpoint.Endpoint GetBatchEndpoint endpoint.Endpoint + DeleteBatchEndpoint endpoint.Endpoint } func MakeServerEndpoints(s Service) Endpoints { @@ -23,7 +25,9 @@ func MakeServerEndpoints(s Service) Endpoints { GetFilesEndpoint: MakeGetFilesEndpoint(s), DeleteFileEndpoint: MakeDeleteFileEndpoint(s), CreateBatchEndpoint: MakeCreateBatchEndpoint(s), + GetBatchesEndpoint: MakeGetBatchesEndpoint(s), GetBatchEndpoint: MakeGetBatchEndpoint(s), + DeleteBatchEndpoint: MakeDeleteBatchEndpoint(s), } } @@ -61,6 +65,8 @@ type getFilesResponse struct { Err error `json:"error,omitempty"` } +func (r getFilesResponse) error() error { return r.Err } + // MakeGetFileEndpoint returns an endpoint via the passed service. // Primarily useful in a server. func MakeGetFileEndpoint(s Service) endpoint.Endpoint { @@ -121,6 +127,26 @@ type createBatchResponse struct { Err error `json:"err,omitempty"` } +func (r createBatchResponse) error() error { return r.Err } + +func MakeGetBatchesEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(getBatchesRequest) + return getBatchesResponse{Batches: s.GetBatches(req.fileID), Err: nil}, nil + } +} + +type getBatchesRequest struct { + fileID string +} + +type getBatchesResponse struct { + Batches []ach.Batcher `json:"batches,omitempty"` + Err error `json:"error,omitempty"` +} + +func (r getBatchesResponse) error() error { return r.Err } + func MakeGetBatchEndpoint(s Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(getBatchRequest) @@ -138,3 +164,24 @@ type getBatchResponse struct { Batch ach.Batcher `json:"batch,omitempty"` Err error `json:"err,omitempty"` } + +func (r getBatchResponse) error() error { return r.Err } + +func MakeDeleteBatchEndpoint(s Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(deleteBatchRequest) + e := s.DeleteBatch(req.fileID, req.batchID) + return deleteBatchResponse{Err: e}, nil + } +} + +type deleteBatchRequest struct { + fileID string + batchID string +} + +type deleteBatchResponse struct { + Err error `json:"err,omitempty"` +} + +func (r deleteBatchResponse) error() error { return r.Err } diff --git a/server/middlewear.go b/server/middlewear.go index dd52122a1..56522fe45 100644 --- a/server/middlewear.go +++ b/server/middlewear.go @@ -67,3 +67,17 @@ func (mw loggingMiddleware) GetBatch(fileID string, batchID string) (b ach.Batch }(time.Now()) return mw.next.GetBatch(fileID, batchID) } + +func (mw loggingMiddleware) GetBatches(fileID string) []ach.Batcher { + defer func(begin time.Time) { + mw.logger.Log("method", "GetBatches", "fileID", fileID, "took", time.Since(begin)) + }(time.Now()) + return mw.next.GetBatches(fileID) +} + +func (mw loggingMiddleware) DeleteBatch(fileID string, batchID string) (err error) { + defer func(begin time.Time) { + mw.logger.Log("method", "DeleteBatch", "fileID", fileID, "batchID", batchID, "took", time.Since(begin), "err", err) + }(time.Now()) + return mw.next.DeleteBatch(fileID, batchID) +} diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go index 16296331d..b02dce03d 100644 --- a/server/repositoryInMemory.go +++ b/server/repositoryInMemory.go @@ -1,6 +1,5 @@ package server -// TODO: rename to InMemory and move into a repository directory when stable. import ( "sync" @@ -15,6 +14,8 @@ type Repository interface { DeleteFile(id string) error StoreBatch(fileID string, batch ach.Batcher) error FindBatch(fileID string, batchID string) (*ach.Batcher, error) + FindAllBatches(fileID string) []*ach.Batcher + DeleteBatch(fileID string, batchID string) error } type repositoryInMemory struct { mtx sync.RWMutex @@ -95,3 +96,28 @@ func (r *repositoryInMemory) FindBatch(fileID string, batchID string) (*ach.Batc } return nil, ErrNotFound } + +// FindAllBatches +func (r *repositoryInMemory) FindAllBatches(fileID string) []*ach.Batcher { + r.mtx.RLock() + defer r.mtx.RUnlock() + batches := make([]*ach.Batcher, 0, len(r.files[fileID].Batches)) + for _, val := range r.files[fileID].Batches { + batches = append(batches, &val) + } + return batches +} + +func (r *repositoryInMemory) DeleteBatch(fileID string, batchID string) error { + r.mtx.RLock() + defer r.mtx.RUnlock() + for _, val := range r.files[fileID].Batches { + if val.ID() == batchID { + //append() + //delete(val) + // TODO delete the bach from the file + return ErrNotFound + } + } + return nil +} diff --git a/server/service.go b/server/service.go index 9dbcf1725..07985f21a 100644 --- a/server/service.go +++ b/server/service.go @@ -31,6 +31,10 @@ type Service interface { CreateBatch(fileID string, bh ach.BatchHeader) (string, error) // GetBatch retrieves a batch based oin the file id and batch id GetBatch(fileID string, batchID string) (ach.Batcher, error) + // GetBatches retrieves all batches associated with the file id. + GetBatches(fileID string) []ach.Batcher + // DeleteBatch takes a fileID and BatchID and removes the batch from the file + DeleteBatch(fileID string, batchID string) error } // service a concrete implementation of the service. @@ -116,6 +120,18 @@ func (s *service) GetBatch(fileID string, batchID string) (ach.Batcher, error) { return *b, nil } +func (s *service) GetBatches(fileID string) []ach.Batcher { + var result []ach.Batcher + for _, b := range s.store.FindAllBatches(fileID) { + result = append(result, *b) + } + return result +} + +func (s *service) DeleteBatch(fileID string, batchID string) error { + return s.store.DeleteBatch(fileID, batchID) +} + // Utility Functions // ***** diff --git a/server/service_test.go b/server/service_test.go index dcba26104..713c1ed0c 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -141,12 +141,20 @@ func TestGetBatchNotFound(t *testing.T) { // TestGetBatches return a list of batches for the supplied file.id func TestGetBatches(t *testing.T) { - + s := mockServiceInMemory() + batches := s.GetBatches("98765") + if len(batches) != 1 { + t.Errorf("expected %s received %v", "1", len(batches)) + } } // Service.DeleteBatch // TestDeleteBatch removes a batch with existing file and batch id. func TestDeleteBatch(t *testing.T) { - + s := mockServiceInMemory() + err := s.DeleteBatch("98765", "54321") + if err != nil { + t.Errorf("expected %s received error %s", "nil", err) + } } diff --git a/server/transport.go b/server/transport.go index ba6f12485..321fb3137 100644 --- a/server/transport.go +++ b/server/transport.go @@ -37,7 +37,9 @@ func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { // DELETE /files/:id delete a file based on supplied id // POST /files/:id/batches/ Create a Batch + // GET /files/:fileID/batches Retrieve a list of all batches for the file id. // GET /files/:id/batches/:id Retrieve the given batch by id + // DELETE /files/:fileID/batches/:batchID delete the batch based on the supplied file and batch id // *** // GET /files/:id/validate validates the supplied file id for nacha compliance // PATCH /files/:id/build build batch and file controls in ach file with supplied values @@ -73,12 +75,24 @@ func MakeHTTPHandler(s Service, logger log.Logger) http.Handler { encodeResponse, options..., )) + r.Methods("GET").Path("/files/{fileID}/batches/").Handler(httptransport.NewServer( + e.GetBatchesEndpoint, + decodeGetBatchesRequest, + encodeResponse, + options..., + )) r.Methods("GET").Path("/files/{fileID}/batches/{batchID}").Handler(httptransport.NewServer( e.GetBatchEndpoint, decodeGetBatchRequest, encodeResponse, options..., )) + r.Methods("DELETE").Path("/files/{fileID}/batches/{batchID}").Handler(httptransport.NewServer( + e.DeleteBatchEndpoint, + decodeDeleteBatchRequest, + encodeResponse, + options..., + )) return r } @@ -143,6 +157,17 @@ func decodeCreateBatchRequest(_ context.Context, r *http.Request) (request inter return req, nil } +func decodeGetBatchesRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + var req getBatchesRequest + vars := mux.Vars(r) + id, ok := vars["fileID"] + if !ok { + return nil, ErrBadRouting + } + req.fileID = id + return req, nil +} + func decodeGetBatchRequest(_ context.Context, r *http.Request) (request interface{}, err error) { var req getBatchRequest vars := mux.Vars(r) @@ -160,6 +185,23 @@ func decodeGetBatchRequest(_ context.Context, r *http.Request) (request interfac return req, nil } +func decodeDeleteBatchRequest(_ context.Context, r *http.Request) (request interface{}, err error) { + var req deleteBatchRequest + vars := mux.Vars(r) + fileID, ok := vars["fileID"] + if !ok { + return nil, ErrBadRouting + } + batchID, ok := vars["batchID"] + if !ok { + return nil, ErrBadRouting + } + + req.fileID = fileID + req.batchID = batchID + return req, nil +} + // errorer is implemented by all concrete response types that may contain // errors. It allows us to change the HTTP response code without needing to // trigger an endpoint (transport-level) error. For more information, read the From b8336ac5d9d68e8d7e11f2e53b779edb82791e6e Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 13 Jul 2018 05:26:16 -0400 Subject: [PATCH 0309/1694] #211 Moved batch.entries check from isCategory to verify #211 Moved batch.entries check from isCategory to verify --- batch.go | 7 ++++--- iatBatch.go | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/batch.go b/batch.go index dfbdd4358..274b2255a 100644 --- a/batch.go +++ b/batch.go @@ -65,6 +65,10 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { func (batch *batch) verify() error { batchNumber := batch.Header.BatchNumber + // No entries in batch + if len(batch.Entries) <= 0 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} + } // verify field inclusion in all the records of the batch. if err := batch.isFieldInclusion(); err != nil { // convert the field error in to a batch error for a consistent api @@ -406,9 +410,6 @@ func (batch *batch) isTypeCode(typeCode string) error { // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *batch) isCategory() error { - if len(batch.Entries) <= 0 { - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} - } category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { for i := 0; i < len(batch.Entries); i++ { diff --git a/iatBatch.go b/iatBatch.go index db7b8b95b..8123d63a0 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -48,6 +48,10 @@ func IATNewBatch(bh *IATBatchHeader) IATBatch { func (batch *IATBatch) verify() error { batchNumber := batch.Header.BatchNumber + // No entries in batch + if len(batch.Entries) <= 0 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} + } // verify field inclusion in all the records of the batch. if err := batch.isFieldInclusion(); err != nil { // convert the field error in to a batch error for a consistent api @@ -390,9 +394,6 @@ func (batch *IATBatch) isAddendaSequence() error { // isCategory verifies that a Forward and Return Category are not in the same batch func (batch *IATBatch) isCategory() error { - if len(batch.Entries) <= 0 { - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "entries", Msg: msgBatchEntries} - } category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { for i := 1; i < len(batch.Entries); i++ { From eaf25b457ddc5e1d3293996f9c1cd91a430118a8 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 13 Jul 2018 15:25:25 -0400 Subject: [PATCH 0310/1694] #211 Increase Code Coverage #211 Increase Code Coverage --- addenda12_test.go | 4 +- addenda16_test.go | 8 +-- batchSHR_test.go | 2 - iatBatch_test.go | 70 +++++++++++++++++++++++- reader.go | 3 +- reader_test.go | 109 +++++++++++++++++++++++++++++-------- test/data/20110729A.ach | 2 - test/data/20110805A.ach | 2 - test/data/20180713-IAT.ach | 30 ++++++++++ writer_test.go | 26 +++++---- 10 files changed, 205 insertions(+), 51 deletions(-) create mode 100644 test/data/20180713-IAT.ach diff --git a/addenda12_test.go b/addenda12_test.go index 1be3b1112..f073ae721 100644 --- a/addenda12_test.go +++ b/addenda12_test.go @@ -12,7 +12,7 @@ import ( func mockAddenda12() *Addenda12 { addenda12 := NewAddenda12() addenda12.OriginatorCityStateProvince = "JacobsTown*PA\\" - addenda12.OriginatorCountryPostalCode = "US19305\\" + addenda12.OriginatorCountryPostalCode = "US*19305\\" addenda12.EntryDetailSequenceNumber = 00000001 return addenda12 } @@ -299,7 +299,7 @@ func testAddenda12String(t testing.TB) { // Backslash logic var line = "712" + "JacobsTown*PA\\ " + - "US19305\\ " + + "US*19305\\ " + " " + "0000001" diff --git a/addenda16_test.go b/addenda16_test.go index 547b8ff88..0dd375852 100644 --- a/addenda16_test.go +++ b/addenda16_test.go @@ -11,8 +11,8 @@ import ( // mockAddenda16 creates a mock Addenda16 record func mockAddenda16() *Addenda16 { addenda16 := NewAddenda16() - addenda16.ReceiverCityStateProvince = "LetterTown*CO\\" - addenda16.ReceiverCountryPostalCode = "US80014\\" + addenda16.ReceiverCityStateProvince = "LetterTown*AB\\" + addenda16.ReceiverCountryPostalCode = "CA*80014\\" addenda16.EntryDetailSequenceNumber = 00000001 return addenda16 } @@ -300,8 +300,8 @@ func testAddenda16String(t testing.TB) { addenda16 := NewAddenda16() // Backslash logic var line = "716" + - "LetterTown*CO\\ " + - "US80014\\ " + + "LetterTown*AB\\ " + + "CA*80014\\ " + " " + "0000001" diff --git a/batchSHR_test.go b/batchSHR_test.go index 52649899e..5eb13e1d4 100644 --- a/batchSHR_test.go +++ b/batchSHR_test.go @@ -380,8 +380,6 @@ func BenchmarkBatchSHRInvalidAddenda(b *testing.B) { } } -// ToDo: Using a FieldError may need to add a BatchError and use *BatchError - // testBatchSHRInvalidBuild validates an invalid batch build func testBatchSHRInvalidBuild(t testing.TB) { mockBatch := mockBatchSHR() diff --git a/iatBatch_test.go b/iatBatch_test.go index 2f0a087ab..a42773039 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -750,8 +750,14 @@ func BenchmarkIATBatchAddendaRecordIndicator(b *testing.B) { func testIATBatchInvalidTraceNumberODFI(t testing.TB) { mockBatch := mockIATBatch() mockBatch.GetEntries()[0].SetTraceNumber("9928272", 1) - if err := mockBatch.build(); err != nil { - t.Errorf("%T: %s", err, err) + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ODFIIdentificationField" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } } } @@ -795,3 +801,63 @@ func BenchmarkIATBatchControl(b *testing.B) { testIATBatchControl(b) } } + +// testIATBatchEntryCountEquality validates IATBatch EntryAddendaCount +func testIATBatchEntryCountEquality(t testing.TB) { + mockBatch := mockIATBatch() + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + mockBatch.GetControl().EntryAddendaCount = 1 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "EntryAddendaCount" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchEntryCountEquality tests validating IATBatch EntryAddendaCount +func TestIATBatchEntryCountEquality(t *testing.T) { + testIATBatchEntryCountEquality(t) +} + +// BenchmarkIATBatchEntryCountEquality benchmarks validating IATBatch EntryAddendaCount +func BenchmarkIATBatchEntryCountEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchEntryCountEquality(b) + } +} + +// testIATBatchisEntryHash validates IATBatch EntryHash +func testIATBatchisEntryHash(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetControl().EntryHash = 1 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "EntryHash" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +//TestIATBatchisEntryHash tests validating IATBatch EntryHash +func TestIATBatchisEntryHash(t *testing.T) { + testIATBatchisEntryHash(t) +} + +//BenchmarkIATBatchisEntryHash benchmarks validating IATBatch EntryHash +func BenchmarkIATBatchisEntryHash(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchisEntryHash(b) + } +} diff --git a/reader.go b/reader.go index 56efa2cad..d42fa9ede 100644 --- a/reader.go +++ b/reader.go @@ -197,7 +197,8 @@ func (r *Reader) parseBH() error { // parseEd parses determines whether to parse an IATEntryDetail or EntryDetail func (r *Reader) parseED() error { - // ToDo: Review if this can be true for domestic files. + // ToDo: Review if this can be true for domestic files. Also this field may be + // ToDo: used for IAT Corrections so consider using another field // IATIndicator field if r.line[16:29] == " " { if err := r.parseIATEntryDetail(); err != nil { diff --git a/reader_test.go b/reader_test.go index 1c9d86444..ef53df5f9 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1008,24 +1008,29 @@ func testACHFileRead(t testing.TB) { defer f.Close() r := NewReader(f) _, err = r.Read() - if p, ok := err.(*ParseError); ok { - if e, ok := p.Err.(*BatchError); ok { - if e.FieldName != "entries" { - t.Errorf("%T: %s", e, e) + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", e, e) + } } + } else { + t.Errorf("%T: %s", err, err) } - } else { - t.Errorf("%T: %s", err, err) } - err = r.File.Validate() + err2 := r.File.Validate() - if e, ok := err.(*FileError); ok { - if e.FieldName != "BatchCount" { - t.Errorf("%T: %s", e, e) + if err2 != nil { + if e, ok := err2.(*FileError); ok { + if e.FieldName != "BatchCount" { + t.Errorf("%T: %s", e, e) + } + } else { + t.Errorf("%T: %s", err, err) } - } else { - t.Errorf("%T: %s", err, err) } } @@ -1051,24 +1056,29 @@ func testACHFileRead2(t testing.TB) { defer f.Close() r := NewReader(f) _, err = r.Read() - if p, ok := err.(*ParseError); ok { - if e, ok := p.Err.(*BatchError); ok { - if e.FieldName != "entries" { - t.Errorf("%T: %s", e, e) + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", e, e) + } } + } else { + t.Errorf("%T: %s", err, err) } - } else { - t.Errorf("%T: %s", err, err) } - err = r.File.Validate() + err2 := r.File.Validate() - if e, ok := err.(*FileError); ok { - if e.FieldName != "BatchCount" { - t.Errorf("%T: %s", e, e) + if err2 != nil { + if e, ok := err2.(*FileError); ok { + if e.FieldName != "BatchCount" { + t.Errorf("%T: %s", e, e) + } + } else { + t.Errorf("%T: %s", err, err) } - } else { - t.Errorf("%T: %s", err, err) } } @@ -1084,3 +1094,54 @@ func BenchmarkACHFileRead2(b *testing.B) { testACHFileRead2(b) } } + +// testACHFileRead3 validates reading a file with IAT entries +// that does not error. +func testACHFileRead3(t testing.TB) { + f, err := os.Open("./test/data/20180713-IAT.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } + + err2 := r.File.Validate() + + if err2 != nil { + if e, ok := err2.(*FileError); ok { + if e.FieldName != "BatchCount" { + t.Errorf("%T: %s", e, e) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHFileRead3 tests validating reading a file with IAT entries that +// does not error. +func TestACHFileRead3(t *testing.T) { + testACHFileRead3(t) +} + +// BenchmarkACHFileRead3 benchmarks validating reading a file with IAT entries +// that does not error. +func BenchmarkACHFileRead3(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileRead3(b) + } +} diff --git a/test/data/20110729A.ach b/test/data/20110729A.ach index 9e4932baf..8ed5b6a9d 100644 --- a/test/data/20110729A.ach +++ b/test/data/20110729A.ach @@ -166,8 +166,6 @@ 622021200025998412345 0000001600OA380 ANDREA BUTLER 0098765434200017 622021200025998412345 0000001200OA383 BRANDON ARMSTRONG 0098765434200018 822000001800381600360000000000000000000172001234567898 098765430000003 -5220TEST COMPANY 11234567898PPDTEST SALES110801110801 1042000010000002 -822000000000000000000000000000000000000000001234567898 042000010000002 5225 FV3 CA1234567898IATTEST BUYS USDCAD110802 1098765430000004 627091050234007 0012200000998412345 1098765430000001 710WEB DIEGO MAY 0000001 diff --git a/test/data/20110805A.ach b/test/data/20110805A.ach index 523f29e9e..c95ccb39d 100644 --- a/test/data/20110805A.ach +++ b/test/data/20110805A.ach @@ -26,8 +26,6 @@ 627021200025998412345 0000149000A297 PAYTON MCCOY 0042000010000024 627021200025998412345 0000217000A298 DESTINY COLEMAN 0042000010000025 822500002500530000500000046100000000000000000123456789 042000010000001 -5220EXAMPLE COMPANY 0123456789PPDREFUND 110808110808 1042000010000002 -822000000000000000000000000000000000000000000123456789 042000010000002 5220EXAMPLE COMPANY 0123456789PPDVERIFY 110808110808 1042000010000003 622021200025998412345 0000000008A251 NATHAN NELSON 0042000010000001 622021200025998412345 0000000010A252 NATHAN NELSON 0042000010000002 diff --git a/test/data/20180713-IAT.ach b/test/data/20180713-IAT.ach new file mode 100644 index 000000000..4c6c10ba0 --- /dev/null +++ b/test/data/20180713-IAT.ach @@ -0,0 +1,30 @@ +101 987654321 1234567891807130000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000002000000000000000 231380100000002 +9000002000003000000160024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/writer_test.go b/writer_test.go index cc8ac30e0..510333fdc 100644 --- a/writer_test.go +++ b/writer_test.go @@ -121,18 +121,20 @@ func testIATWrite(t testing.TB) { iatBatch.Create() file.AddIATBatch(iatBatch) - /* iatBatch2 := IATBatch{} - iatBatch2.SetHeader(mockIATBatchHeaderFF()) - iatBatch2.AddEntry(mockIATEntryDetail()) - iatBatch2.Entries[0].Addenda10 = mockAddenda10() - iatBatch2.Entries[0].Addenda11 = mockAddenda11() - iatBatch2.Entries[0].Addenda12 = mockAddenda12() - iatBatch2.Entries[0].Addenda13 = mockAddenda13() - iatBatch2.Entries[0].Addenda14 = mockAddenda14() - iatBatch2.Entries[0].Addenda15 = mockAddenda15() - iatBatch2.Entries[0].Addenda16 = mockAddenda16() - iatBatch2.Create() - file.AddIATBatch(iatBatch2)*/ + iatBatch2 := IATBatch{} + iatBatch2.SetHeader(mockIATBatchHeaderFF()) + iatBatch2.AddEntry(mockIATEntryDetail()) + iatBatch2.GetEntries()[0].TransactionCode = 27 + iatBatch2.GetEntries()[0].Amount = 2000 + iatBatch2.Entries[0].Addenda10 = mockAddenda10() + iatBatch2.Entries[0].Addenda11 = mockAddenda11() + iatBatch2.Entries[0].Addenda12 = mockAddenda12() + iatBatch2.Entries[0].Addenda13 = mockAddenda13() + iatBatch2.Entries[0].Addenda14 = mockAddenda14() + iatBatch2.Entries[0].Addenda15 = mockAddenda15() + iatBatch2.Entries[0].Addenda16 = mockAddenda16() + iatBatch2.Create() + file.AddIATBatch(iatBatch2) if err := file.Create(); err != nil { t.Errorf("%T: %s", err, err) From 3e4c6de5882063ed997aa730e9f79c3387b0c6ae Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 13 Jul 2018 16:52:10 -0400 Subject: [PATCH 0311/1694] #211 reader_test updates #211 reader_test updates --- reader_test.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/reader_test.go b/reader_test.go index ef53df5f9..900be4ddd 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1095,8 +1095,7 @@ func BenchmarkACHFileRead2(b *testing.B) { } } -// testACHFileRead3 validates reading a file with IAT entries -// that does not error. +// testACHFileRead3 validates reading a file with IAT entries only func testACHFileRead3(t testing.TB) { f, err := os.Open("./test/data/20180713-IAT.ach") if err != nil { @@ -1131,14 +1130,12 @@ func testACHFileRead3(t testing.TB) { } } -// TestACHFileRead3 tests validating reading a file with IAT entries that -// does not error. +// TestACHFileRead3 tests validating reading a file with IAT entries that only func TestACHFileRead3(t *testing.T) { testACHFileRead3(t) } -// BenchmarkACHFileRead3 benchmarks validating reading a file with IAT entries -// that does not error. +// BenchmarkACHFileRead3 benchmarks validating reading a file with IAT entries only func BenchmarkACHFileRead3(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From 6804d1b23dae3580d7c30b801afb6458c369cfdb Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 13 Jul 2018 17:27:09 -0400 Subject: [PATCH 0312/1694] #211 iatBatch_test Add additional code coverage --- iatBatch_test.go | 93 ++++++++++++++++++++++++++++++++++++++++++ iatEntryDetail_test.go | 13 ++++++ 2 files changed, 106 insertions(+) diff --git a/iatBatch_test.go b/iatBatch_test.go index a42773039..105f1679d 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -27,6 +27,32 @@ func mockIATBatch() IATBatch { return mockBatch } +// mockIATBatchManyEntries +func mockIATBatchManyEntries() IATBatch { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.AddEntry(mockIATEntryDetail2()) + mockBatch.Entries[1].Addenda10 = mockAddenda10() + mockBatch.Entries[1].Addenda11 = mockAddenda11() + mockBatch.Entries[1].Addenda12 = mockAddenda12() + mockBatch.Entries[1].Addenda13 = mockAddenda13() + mockBatch.Entries[1].Addenda14 = mockAddenda14() + mockBatch.Entries[1].Addenda15 = mockAddenda15() + mockBatch.Entries[1].Addenda16 = mockAddenda16() + if err := mockBatch.build(); err != nil { + log.Fatal(err) + } + return mockBatch +} + // TestMockIATBatch validates mockIATBatch func TestMockIATBatch(t *testing.T) { iatBatch := mockIATBatch() @@ -861,3 +887,70 @@ func BenchmarkIATBatchisEntryHash(b *testing.B) { testIATBatchisEntryHash(b) } } + +// testIATBatchIsSequenceAscending validates sequence ascending +func testIATBatchIsSequenceAscending(t testing.TB) { + mockBatch := mockIATBatch() + e2 := mockIATEntryDetail() + e2.TraceNumber = 1 + mockBatch.AddEntry(e2) + mockBatch.Entries[1].Addenda10 = mockAddenda10() + mockBatch.Entries[1].Addenda11 = mockAddenda11() + mockBatch.Entries[1].Addenda12 = mockAddenda12() + mockBatch.Entries[1].Addenda13 = mockAddenda13() + mockBatch.Entries[1].Addenda14 = mockAddenda14() + mockBatch.Entries[1].Addenda15 = mockAddenda15() + mockBatch.Entries[1].Addenda16 = mockAddenda16() + mockBatch.GetControl().EntryAddendaCount = 16 + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchIsSequenceAscending tests validating sequence ascending +func TestIATBatchIsSequenceAscending(t *testing.T) { + testIATBatchIsSequenceAscending(t) +} + +// BenchmarkIATBatchIsSequenceAscending tests validating sequence ascending +func BenchmarkIATBatchIsSequenceAscending(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchIsSequenceAscending(b) + } +} + +// testIATBatchIsCategory validates category +func testIATBatchIsCategory(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Category = CategoryReturn + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Category" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchIsCategory tests validating category +func TestIATBatchIsCategory(t *testing.T) { + testIATBatchIsCategory(t) +} + +// BenchmarkIATBatchIsCategory tests validating category +func BenchmarkIATBatchIsCategory(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchIsCategory(b) + } +} diff --git a/iatEntryDetail_test.go b/iatEntryDetail_test.go index 122dbd056..0b4e89141 100644 --- a/iatEntryDetail_test.go +++ b/iatEntryDetail_test.go @@ -22,6 +22,19 @@ func mockIATEntryDetail() *IATEntryDetail { return entry } +// mockIATEntryDetail2 creates an EntryDetail +func mockIATEntryDetail2() *IATEntryDetail { + entry := NewIATEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("121042882") + entry.AddendaRecords = 007 + entry.DFIAccountNumber = "123456789" + entry.Amount = 200000 // 2000.00 + entry.SetTraceNumber(mockIATBatchHeaderFF().ODFIIdentification, 2) + entry.Category = CategoryForward + return entry +} + // testMockIATEntryDetail validates an IATEntryDetail record func testMockIATEntryDetail(t testing.TB) { entry := mockIATEntryDetail() From ac4331e506bd998b1b323be2679e53af3d96ab85 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 16 Jul 2018 11:19:22 -0400 Subject: [PATCH 0313/1694] #211 Addenda17 #211 Addenda17 --- addenda17_test.go | 10 +++++++ iatBatch.go | 32 +++++++++-------------- iatEntryDetail.go | 26 +++++++++++++----- reader.go | 4 +-- reader_test.go | 48 ++++++++++++++++++++++++++++++++++ test/data/20180716-IAT-A17.ach | 30 +++++++++++++++++++++ writer.go | 9 +++++-- writer_test.go | 17 +++++++----- 8 files changed, 140 insertions(+), 36 deletions(-) create mode 100644 test/data/20180716-IAT-A17.ach diff --git a/addenda17_test.go b/addenda17_test.go index ea8fa2c87..0942d8aa4 100644 --- a/addenda17_test.go +++ b/addenda17_test.go @@ -11,12 +11,22 @@ import ( // mockAddenda17 creates a mock Addenda17 record func mockAddenda17() *Addenda17 { addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "This is an international payment" addenda17.SequenceNumber = 1 addenda17.EntryDetailSequenceNumber = 0000001 return addenda17 } +func mockAddenda17B() *Addenda17 { + addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "Transfer of money from one country to another" + addenda17.SequenceNumber = 2 + addenda17.EntryDetailSequenceNumber = 0000002 + + return addenda17 +} + // TestMockAddenda17 validates mockAddenda17 func TestMockAddenda17(t *testing.T) { addenda17 := mockAddenda17() diff --git a/iatBatch.go b/iatBatch.go index 8123d63a0..13e4e8340 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -114,15 +114,8 @@ func (batch *IATBatch) build() error { entryCount := 0 seq := 1 for i, entry := range batch.Entries { - entryCount = entryCount + 1 + 7 - //ToDo: Add Addenda17 and Addenda18 maximum of 2 addenda17 and 5 addenda18 - /* if entry.Addenda17 != nil { - entryCount = entryCount + 1 - }*/ - - /*if entry.Addenda18 != nil { - entryCount = entryCount + 1 - } */ + entryCount = entryCount + 1 + 7 + len(entry.Addendum) + //ToDo: Add Addenda17 and Addenda18 maximum of 2 addenda17 and 5 addenda18 // Verifies the required addenda* properties for an IAT entry detail are defined if err := batch.addendaFieldInclusion(entry); err != nil { @@ -153,10 +146,17 @@ func (batch *IATBatch) build() error { entry.Addenda15.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) entry.Addenda16.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) - //ToDo: Add Addenda17 and Addenda 18 logic for SequenceNUmber and EntryDetailSequenceNumber + // Addenda17 and Addenda18 SequenceNumber and EntryDetailSequenceNumber + seq++ + addendaSeq := 1 + for x := range entry.Addendum { + if a, ok := batch.Entries[i].Addendum[x].(*Addenda17); ok { + a.SequenceNumber = addendaSeq + a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) + } + addendaSeq++ + } - /* entry.Addenda17.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) - entry.Addenda18.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])*/ } // build a BatchControl record @@ -255,13 +255,7 @@ func (batch *IATBatch) isFieldInclusion() error { func (batch *IATBatch) isBatchEntryCount() error { entryCount := 0 for _, entry := range batch.Entries { - entryCount = entryCount + 1 + 7 - - //ToDo: Add logic for Addenda17 and Addenda18 - - if entry.Addenda17 != nil { - entryCount = entryCount + 1 - } + entryCount = entryCount + 1 + 7 + len(entry.Addendum) } if entryCount != batch.Control.EntryAddendaCount { msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.Control.EntryAddendaCount) diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 1d3dd4368..b53d16e09 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -92,14 +92,12 @@ type IATEntryDetail struct { // The Addenda15 record identifies key information related to the Receiver. Addenda15 *Addenda15 `json:"addenda15,omitempty"` // Addenda16 - Addenda16 *Addenda16 `json:"addenda16,omitempty"` - // Addenda16 is mandatory for IAT entries - // - // The Addenda16 record identifies key information related to the Receiver. - Addenda17 *Addenda17 `json:"addenda17,omitempty"` - // Addenda17 is optional for IAT entries // - // The Addenda17 record identifies payment-related data. A maximum of two of these Addenda Records + // Addenda16 record identifies additional key information related to the Receiver. + Addenda16 *Addenda16 `json:"addenda16,omitempty"` + // Addendum a list of Addenda for the Entry Detail + Addendum []Addendumer `json:"addendum,omitempty"` + // Category defines if the entry is a Forward, Return, or NOC Category string `json:"category,omitempty"` // validator is composed for data validation validator @@ -289,3 +287,17 @@ func (ed *IATEntryDetail) SecondaryOFACSreeningIndicatorField() string { func (ed *IATEntryDetail) TraceNumberField() string { return ed.numericField(ed.TraceNumber, 15) } + +// AddIATAddenda appends an Addendumer to the IATEntryDetail +// Currently this is used to add Addenda17 and Addenda18 IAT Addenda records +func (ed *IATEntryDetail) AddIATAddenda(addenda Addendumer) []Addendumer { + ed.AddendaRecordIndicator = 1 + // checks to make sure that we only have either or, not both + switch addenda.(type) { + // Addenda17 + default: + ed.Category = CategoryForward + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum + } +} diff --git a/reader.go b/reader.go index d42fa9ede..a5d74b3bb 100644 --- a/reader.go +++ b/reader.go @@ -417,7 +417,7 @@ func (r *Reader) parseIATEntryDetail() error { return nil } -// parseAddendaRecord takes the input record string and create an Addenda Type appended to the last EntryDetail +// parseIATAddenda takes the input record string and create an Addenda Type appended to the last EntryDetail func (r *Reader) parseIATAddenda() error { r.recordName = "Addenda" @@ -489,7 +489,7 @@ func (r *Reader) parseIATAddenda() error { if err := addenda17.Validate(); err != nil { return r.error(err) } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda17 = addenda17 + r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda17) } } else { msg := fmt.Sprint(msgIATBatchAddendaIndicator) diff --git a/reader_test.go b/reader_test.go index 900be4ddd..be2214dfc 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1142,3 +1142,51 @@ func BenchmarkACHFileRead3(b *testing.B) { testACHFileRead3(b) } } + +// testACHIATAddenda17 validates reading a file with IAT and Addenda 17 entries +func testACHIATAddenda17(t testing.TB) { + f, err := os.Open("./test/data/20180716-IAT-A17.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } + + err2 := r.File.Validate() + + if err2 != nil { + if e, ok := err2.(*FileError); ok { + if e.FieldName != "BatchCount" { + t.Errorf("%T: %s", e, e) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHIATAddenda17 tests validating reading a file with IAT and Addenda17 entries that +func TestACHIATAddenda17(t *testing.T) { + testACHIATAddenda17(t) +} + +// BenchmarkACHIATAddenda17 benchmarks validating reading a file with IAT and Addenda17 entries +func BenchmarkACHIATAddenda17(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHIATAddenda17(b) + } +} diff --git a/test/data/20180716-IAT-A17.ach b/test/data/20180716-IAT-A17.ach new file mode 100644 index 000000000..1f978f69d --- /dev/null +++ b/test/data/20180716-IAT-A17.ach @@ -0,0 +1,30 @@ +101 987654321 1234567891807160000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +717Transfer of money from one country to another 00020000001 +82200000100012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +82200000090012104288000000002000000000000000 231380100000002 +9000002000003000000190024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/writer.go b/writer.go index 9c4404d33..fb24e1859 100644 --- a/writer.go +++ b/writer.go @@ -134,8 +134,13 @@ func (w *Writer) writeIATBatch(file *File) error { return err } w.lineNum++ - - // ToDo: 17 and 18 + // IAT Addenda17 and IAT Addenda18 records + for _, IATaddenda := range entry.Addendum { + if _, err := w.w.WriteString(IATaddenda.String() + "\n"); err != nil { + return err + } + w.lineNum++ + } } if _, err := w.w.WriteString(iatBatch.GetControl().String() + "\n"); err != nil { return err diff --git a/writer_test.go b/writer_test.go index 510333fdc..86771ff58 100644 --- a/writer_test.go +++ b/writer_test.go @@ -6,6 +6,8 @@ package ach import ( "bytes" + "log" + "os" "strings" "testing" ) @@ -118,6 +120,8 @@ func testIATWrite(t testing.TB) { iatBatch.Entries[0].Addenda14 = mockAddenda14() iatBatch.Entries[0].Addenda15 = mockAddenda15() iatBatch.Entries[0].Addenda16 = mockAddenda16() + iatBatch.Entries[0].AddIATAddenda(mockAddenda17()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda17B()) iatBatch.Create() file.AddIATBatch(iatBatch) @@ -133,6 +137,7 @@ func testIATWrite(t testing.TB) { iatBatch2.Entries[0].Addenda14 = mockAddenda14() iatBatch2.Entries[0].Addenda15 = mockAddenda15() iatBatch2.Entries[0].Addenda16 = mockAddenda16() + iatBatch2.Entries[0].AddIATAddenda(mockAddenda17()) iatBatch2.Create() file.AddIATBatch(iatBatch2) @@ -159,12 +164,12 @@ func testIATWrite(t testing.TB) { t.Errorf("%T: %s", err, err) } - /* // Write IAT records to standard output. Anything io.Writer - w := NewWriter(os.Stdout) - if err := w.Write(file); err != nil { - log.Fatalf("Unexpected error: %s\n", err) - } - w.Flush()*/ + // Write IAT records to standard output. Anything io.Writer + w := NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() } // TestIATWrite tests writing a IAT ACH file From 23b77acdd7d40c1006fe23abcb55dead8f7352be Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 16 Jul 2018 22:03:16 -0400 Subject: [PATCH 0314/1694] #211 Addenda18 Support #211 Addenda18 Support --- .travis.yml | 2 +- addenda10.go | 1 - addenda13.go | 2 +- addenda14.go | 2 +- addenda16.go | 4 +- addenda18.go | 208 ++++++++++++++++++ addenda18_test.go | 325 +++++++++++++++++++++++++++++ iatBatch.go | 46 +++- iatEntryDetail.go | 7 +- reader.go | 131 +++++++----- reader_test.go | 50 ++++- test/data/20180716-IAT-A17-A18.ach | 40 ++++ validators.go | 2 +- writer_test.go | 25 ++- 14 files changed, 759 insertions(+), 86 deletions(-) create mode 100644 addenda18.go create mode 100644 addenda18_test.go create mode 100644 test/data/20180716-IAT-A17-A18.ach diff --git a/.travis.yml b/.travis.yml index 7b1d31368..163aba0b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ script: - test -z $(gofmt -s -l $GO_FILES) - go vet $GO_FILES - misspell -error -locale US . -- gocyclo -over 20 $GO_FILES +- gocyclo -over 19 $GO_FILES - golint -set_exit_status $GO_FILES after_success: - goveralls -repotoken $COVERALLS_TOKEN diff --git a/addenda10.go b/addenda10.go index 27d85fdc0..84632c222 100644 --- a/addenda10.go +++ b/addenda10.go @@ -67,7 +67,6 @@ func (addenda10 *Addenda10) Parse(record string) { // 07-24 Payment Amount For inbound IAT payments this field should contain the USD amount or may be blank. addenda10.ForeignPaymentAmount = addenda10.parseNumField(record[06:24]) // 25-46 Insert blanks or zeros - //addenda10.ForeignTraceNumber = addenda10.parseStringField(record[24:46]) addenda10.ForeignTraceNumber = record[24:46] // 47-81 Receiving Company Name/Individual Name addenda10.Name = record[46:81] diff --git a/addenda13.go b/addenda13.go index 3cc20d77f..8599c9818 100644 --- a/addenda13.go +++ b/addenda13.go @@ -187,7 +187,7 @@ func (addenda13 *Addenda13) ODFIIdentificationField() string { // ODFIBranchCountryCodeField gets the ODFIBranchCountryCode field left padded func (addenda13 *Addenda13) ODFIBranchCountryCodeField() string { - return addenda13.alphaField(addenda13.ODFIBranchCountryCode, 2) + " " + return addenda13.alphaField(addenda13.ODFIBranchCountryCode, 3) } // reservedField gets reserved - blank space diff --git a/addenda14.go b/addenda14.go index 008fab489..43e378fcc 100644 --- a/addenda14.go +++ b/addenda14.go @@ -183,7 +183,7 @@ func (addenda14 *Addenda14) RDFIIdentificationField() string { // RDFIBranchCountryCodeField gets the RDFIBranchCountryCode field left padded func (addenda14 *Addenda14) RDFIBranchCountryCodeField() string { - return addenda14.alphaField(addenda14.RDFIBranchCountryCode, 2) + " " + return addenda14.alphaField(addenda14.RDFIBranchCountryCode, 3) } // reservedField gets reserved - blank space diff --git a/addenda16.go b/addenda16.go index 00c86c6e1..c8ffacd73 100644 --- a/addenda16.go +++ b/addenda16.go @@ -57,9 +57,9 @@ func (addenda16 *Addenda16) Parse(record string) { addenda16.recordType = "7" // 2-3 Always 16 addenda16.typeCode = record[1:3] - // 4-38 + // 4-38 ReceiverCityStateProvince addenda16.ReceiverCityStateProvince = record[3:38] - // 39-73 + // 39-73 ReceiverCountryPostalCode addenda16.ReceiverCountryPostalCode = record[38:73] // 74-87 reserved - Leave blank addenda16.reserved = " " diff --git a/addenda18.go b/addenda18.go new file mode 100644 index 000000000..b0c510b89 --- /dev/null +++ b/addenda18.go @@ -0,0 +1,208 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" +) + +// Addenda18 is an addenda which provides business transaction information for Addenda Type +// Code 18 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard. +// +// Addenda18 is optional for IAT entries +// +// The Addenda18 record identifies information on each Foreign Correspondent Bank involved in the +// processing of the IAT entry. If no Foreign Correspondent Bank is involved,t he record should not be +// included. A maximum of five of these Addenda Records may be included with each IAT entry. +type Addenda18 struct { + // ID is a client defined string used as a reference to this record. + ID string `json:"id"` + // RecordType defines the type of record in the block. entryAddenda18 Pos 7 + recordType string + // TypeCode Addenda18 types code '18' + typeCode string + // ForeignCorrespondentBankName contains the name of the Foreign Correspondent Bank + ForeignCorrespondentBankName string `json:"foreignCorrespondentBankName"` + // Foreign Correspondent Bank Identification Number Qualifier contains a 2-digit code that + // identifies the numbering scheme used in the Foreign Correspondent Bank Identification Number + // field. Code values for this field are: + // “01” = National Clearing System + // “02” = BIC Code + // “03” = IBAN Code + ForeignCorrespondentBankIDNumberQualifier string `json:"foreignCorrespondentBankIDNumberQualifier"` + // Foreign Correspondent Bank Identification Number contains the bank ID number of the Foreign + // Correspondent Bank + ForeignCorrespondentBankIDNumber string `json:"foreignCorrespondentBankIDNumber"` + // Foreign Correspondent Bank Branch Country Code contains the two-character code, as approved by + // the International Organization for Standardization (ISO), to identify the country in which the + // branch of the Foreign Correspondent Bank is located. Values can be found on the International + // Organization for Standardization website: www.iso.org + ForeignCorrespondentBankBranchCountryCode string `json:"foreignCorrespondentBankBranchCountryCode"` + // reserved - Leave blank + reserved string + // SequenceNumber is consecutively assigned to each Addenda18 Record following + // an Entry Detail Record. The first addenda18 sequence number must always + // be a "1". + SequenceNumber int `json:"sequenceNumber,omitempty"` + // EntryDetailSequenceNumber contains the ascending sequence number section of the Entry + // Detail or Corporate Entry Detail Record's trace number This number is + // the same as the last seven digits of the trace number of the related + // Entry Detail Record or Corporate Entry Detail Record. + EntryDetailSequenceNumber int `json:"entryDetailSequenceNumber,omitempty"` + // validator is composed for data validation + validator + // converters is composed for ACH to GoLang Converters + converters +} + +// NewAddenda18 returns a new Addenda18 with default values for none exported fields +func NewAddenda18() *Addenda18 { + addenda18 := new(Addenda18) + addenda18.recordType = "7" + addenda18.typeCode = "18" + return addenda18 +} + +// Parse takes the input record string and parses the Addenda18 values +func (addenda18 *Addenda18) Parse(record string) { + // 1-1 Always "7" + addenda18.recordType = "7" + // 2-3 Always 18 + addenda18.typeCode = record[1:3] + // 4-83 Based on the information entered (04-38) 35 alphanumeric + addenda18.ForeignCorrespondentBankName = record[3:38] + // 39-40 Based on the information entered (39-40) 2 alphanumeric + // “01” = National Clearing System + // “02” = BIC Code + // “03” = IBAN Code + addenda18.ForeignCorrespondentBankIDNumberQualifier = record[38:40] + // 41-74 Based on the information entered (41-74) 34 alphanumeric + addenda18.ForeignCorrespondentBankIDNumber = record[40:74] + // 75-77 Based on the information entered (75-77) 3 alphanumeric + addenda18.ForeignCorrespondentBankBranchCountryCode = record[74:77] + // 78-83 - Blank space + addenda18.reserved = " " + // 84-87 SequenceNumber is consecutively assigned to each Addenda18 Record following + // an Entry Detail Record + addenda18.SequenceNumber = addenda18.parseNumField(record[83:87]) + // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record + addenda18.EntryDetailSequenceNumber = addenda18.parseNumField(record[87:94]) +} + +// String writes the Addenda18 struct to a 94 character string. +func (addenda18 *Addenda18) String() string { + return fmt.Sprintf("%v%v%v%v%v%v%v%v%v", + addenda18.recordType, + addenda18.typeCode, + addenda18.ForeignCorrespondentBankNameField(), + addenda18.ForeignCorrespondentBankIDNumberQualifierField(), + addenda18.ForeignCorrespondentBankIDNumberField(), + addenda18.ForeignCorrespondentBankBranchCountryCodeField(), + addenda18.reservedField(), + addenda18.SequenceNumberField(), + addenda18.EntryDetailSequenceNumberField()) +} + +// Validate performs NACHA format rule checks on the record and returns an error if not Validated +// The first error encountered is returned and stops that parsing. +func (addenda18 *Addenda18) Validate() error { + if err := addenda18.fieldInclusion(); err != nil { + return err + } + if addenda18.recordType != "7" { + msg := fmt.Sprintf(msgRecordType, 7) + return &FieldError{FieldName: "recordType", Value: addenda18.recordType, Msg: msg} + } + if err := addenda18.isTypeCode(addenda18.typeCode); err != nil { + return &FieldError{FieldName: "TypeCode", Value: addenda18.typeCode, Msg: err.Error()} + } + // Type Code must be 18 + if addenda18.typeCode != "18" { + return &FieldError{FieldName: "TypeCode", Value: addenda18.typeCode, Msg: msgAddendaTypeCode} + } + if err := addenda18.isAlphanumeric(addenda18.ForeignCorrespondentBankName); err != nil { + return &FieldError{FieldName: "ForeignCorrespondentBankName", Value: addenda18.ForeignCorrespondentBankName, Msg: err.Error()} + } + if err := addenda18.isAlphanumeric(addenda18.ForeignCorrespondentBankIDNumberQualifier); err != nil { + return &FieldError{FieldName: "ForeignCorrespondentBankIDNumberQualifier", Value: addenda18.ForeignCorrespondentBankIDNumberQualifier, Msg: err.Error()} + } + if err := addenda18.isAlphanumeric(addenda18.ForeignCorrespondentBankIDNumber); err != nil { + return &FieldError{FieldName: "ForeignCorrespondentBankIDNumber", Value: addenda18.ForeignCorrespondentBankIDNumber, Msg: err.Error()} + } + if err := addenda18.isAlphanumeric(addenda18.ForeignCorrespondentBankBranchCountryCode); err != nil { + return &FieldError{FieldName: "ForeignCorrespondentBankBranchCountryCode", Value: addenda18.ForeignCorrespondentBankBranchCountryCode, Msg: err.Error()} + } + return nil +} + +// fieldInclusion validate mandatory fields are not default values. If fields are +// invalid the ACH transfer will be returned. +func (addenda18 *Addenda18) fieldInclusion() error { + if addenda18.recordType == "" { + return &FieldError{FieldName: "recordType", Value: addenda18.recordType, Msg: msgFieldInclusion} + } + if addenda18.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda18.typeCode, Msg: msgFieldInclusion} + } + if addenda18.ForeignCorrespondentBankName == "" { + return &FieldError{FieldName: "ForeignCorrespondentBankName", Value: addenda18.ForeignCorrespondentBankName, Msg: msgFieldInclusion} + } + if addenda18.ForeignCorrespondentBankIDNumberQualifier == "" { + return &FieldError{FieldName: "ForeignCorrespondentBankIDNumberQualifier", Value: addenda18.ForeignCorrespondentBankIDNumberQualifier, Msg: msgFieldInclusion} + } + if addenda18.ForeignCorrespondentBankIDNumber == "" { + return &FieldError{FieldName: "ForeignCorrespondentBankIDNumber", Value: addenda18.ForeignCorrespondentBankIDNumber, Msg: msgFieldInclusion} + } + if addenda18.ForeignCorrespondentBankBranchCountryCode == "" { + return &FieldError{FieldName: "ForeignCorrespondentBankBranchCountryCode", Value: addenda18.ForeignCorrespondentBankBranchCountryCode, Msg: msgFieldInclusion} + } + if addenda18.SequenceNumber == 0 { + return &FieldError{FieldName: "SequenceNumber", Value: addenda18.SequenceNumberField(), Msg: msgFieldInclusion} + } + if addenda18.EntryDetailSequenceNumber == 0 { + return &FieldError{FieldName: "EntryDetailSequenceNumber", Value: addenda18.EntryDetailSequenceNumberField(), Msg: msgFieldInclusion} + } + return nil +} + +// ForeignCorrespondentBankNameField returns a zero padded ForeignCorrespondentBankName string +func (addenda18 *Addenda18) ForeignCorrespondentBankNameField() string { + return addenda18.alphaField(addenda18.ForeignCorrespondentBankName, 35) +} + +// ForeignCorrespondentBankIDNumberQualifierField returns a zero padded ForeignCorrespondentBankIDNumberQualifier string +func (addenda18 *Addenda18) ForeignCorrespondentBankIDNumberQualifierField() string { + return addenda18.alphaField(addenda18.ForeignCorrespondentBankIDNumberQualifier, 2) +} + +// ForeignCorrespondentBankIDNumberField returns a zero padded ForeignCorrespondentBankIDNumber string +func (addenda18 *Addenda18) ForeignCorrespondentBankIDNumberField() string { + return addenda18.alphaField(addenda18.ForeignCorrespondentBankIDNumber, 34) +} + +// ForeignCorrespondentBankBranchCountryCodeField returns a zero padded ForeignCorrespondentBankBranchCountryCode string +func (addenda18 *Addenda18) ForeignCorrespondentBankBranchCountryCodeField() string { + return addenda18.alphaField(addenda18.ForeignCorrespondentBankBranchCountryCode, 3) +} + +// SequenceNumberField returns a zero padded SequenceNumber string +func (addenda18 *Addenda18) SequenceNumberField() string { + return addenda18.numericField(addenda18.SequenceNumber, 4) +} + +// reservedField gets reserved - blank space +func (addenda18 *Addenda18) reservedField() string { + return addenda18.alphaField(addenda18.reserved, 6) +} + +// EntryDetailSequenceNumberField returns a zero padded EntryDetailSequenceNumber string +func (addenda18 *Addenda18) EntryDetailSequenceNumberField() string { + return addenda18.numericField(addenda18.EntryDetailSequenceNumber, 7) +} + +// TypeCode Defines the specific explanation and format for the addenda18 information +func (addenda18 *Addenda18) TypeCode() string { + return addenda18.typeCode +} diff --git a/addenda18_test.go b/addenda18_test.go new file mode 100644 index 000000000..aa29a2cc3 --- /dev/null +++ b/addenda18_test.go @@ -0,0 +1,325 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "testing" +) + +// mockAddenda18 creates a mock Addenda18 record +func mockAddenda18() *Addenda18 { + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of Germany" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "987987987654654" + addenda18.ForeignCorrespondentBankBranchCountryCode = "DE" + addenda18.SequenceNumber = 1 + addenda18.EntryDetailSequenceNumber = 0000001 + return addenda18 +} + +func mockAddenda18B() *Addenda18 { + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of Spain" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "987987987123123" + addenda18.ForeignCorrespondentBankBranchCountryCode = "ES" + addenda18.SequenceNumber = 2 + addenda18.EntryDetailSequenceNumber = 0000002 + return addenda18 +} + +func mockAddenda18C() *Addenda18 { + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of France" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "456456456987987" + addenda18.ForeignCorrespondentBankBranchCountryCode = "FR" + addenda18.SequenceNumber = 2 + addenda18.EntryDetailSequenceNumber = 0000003 + return addenda18 +} + +func mockAddenda18D() *Addenda18 { + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of Turkey" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "12312345678910" + addenda18.ForeignCorrespondentBankBranchCountryCode = "TR" + addenda18.SequenceNumber = 2 + addenda18.EntryDetailSequenceNumber = 0000004 + return addenda18 +} + +func mockAddenda18E() *Addenda18 { + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of United Kingdom" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "1234567890123456789012345678901234" + addenda18.ForeignCorrespondentBankBranchCountryCode = "GB" + addenda18.SequenceNumber = 2 + addenda18.EntryDetailSequenceNumber = 0000005 + return addenda18 +} + + +// TestMockAddenda18 validates mockAddenda18 +func TestMockAddenda18(t *testing.T) { + addenda18 := mockAddenda18() + if err := addenda18.Validate(); err != nil { + t.Error("mockAddenda18 does not validate and will break other tests") + } + if addenda18.ForeignCorrespondentBankName != "Bank of Germany" { + t.Error("ForeignCorrespondentBankName dependent default value has changed") + } + if addenda18.ForeignCorrespondentBankIDNumberQualifier != "01" { + t.Error("ForeignCorrespondentBankIDNumberQualifier dependent default value has changed") + } + if addenda18.ForeignCorrespondentBankIDNumber != "987987987654654" { + t.Error("ForeignCorrespondentBankIDNumber dependent default value has changed") + } + if addenda18.ForeignCorrespondentBankBranchCountryCode != "DE" { + t.Error("ForeignCorrespondentBankBranchCountryCode dependent default value has changed") + } + if addenda18.EntryDetailSequenceNumber != 0000001 { + t.Error("EntryDetailSequenceNumber dependent default value has changed") + } +} + +// ToDo: Add parse logic + +// testAddenda18String validates that a known parsed file can be return to a string of the same value +func testAddenda18String(t testing.TB) { + addenda18 := NewAddenda18() + var line = "718Bank of United Kingdom 011234567890123456789012345678901234GB 00010000001" + addenda18.Parse(line) + + if addenda18.String() != line { + t.Errorf("Strings do not match") + } +} + +// TestAddenda18 String tests validating that a known parsed file can be return to a string of the same value +func TestAddenda18String(t *testing.T) { + testAddenda18String(t) +} + +// BenchmarkAddenda18 String benchmarks validating that a known parsed file can be return to a string of the same value +func BenchmarkAddenda18String(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18String(b) + } +} + +func TestValidateAddenda18RecordType(t *testing.T) { + addenda18 := mockAddenda18() + addenda18.recordType = "63" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda18TypeCodeFieldInclusion(t *testing.T) { + addenda18 := mockAddenda18() + addenda18.typeCode = "" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda18FieldInclusion(t *testing.T) { + addenda18 := mockAddenda18() + addenda18.EntryDetailSequenceNumber = 0 + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "EntryDetailSequenceNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +func TestAddenda18FieldInclusionRecordType(t *testing.T) { + addenda18 := mockAddenda18() + addenda18.recordType = "" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.Msg != msgFieldInclusion { + t.Errorf("%T: %s", err, err) + } + } + } +} + +//testAddenda18ForeignCorrespondentBankNameAlphaNumeric validates ForeignCorrespondentBankName is alphanumeric +func testAddenda18ForeignCorrespondentBankNameAlphaNumeric(t testing.TB) { + addenda18 := mockAddenda18() + addenda18.ForeignCorrespondentBankName = "®©" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignCorrespondentBankName" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda18ForeignCorrespondentBankNameAlphaNumeric tests validating ForeignCorrespondentBankName is alphanumeric +func TestAddenda18ForeignCorrespondentBankNameAlphaNumeric(t *testing.T) { + testAddenda18ForeignCorrespondentBankNameAlphaNumeric(t) + +} + +// BenchmarkAddenda18ForeignCorrespondentBankNameAlphaNumeric benchmarks ForeignCorrespondentBankName is alphanumeric +func BenchmarkAddenda18ForeignCorrespondentBankNameAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18ForeignCorrespondentBankNameAlphaNumeric(b) + } +} + +//testAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric validates ForeignCorrespondentBankIDNumberQualifier is alphanumeric +func testAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric(t testing.TB) { + addenda18 := mockAddenda18() + addenda18.ForeignCorrespondentBankIDNumberQualifier = "®©" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignCorrespondentBankIDNumberQualifier" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric tests validating ForeignCorrespondentBankIDNumberQualifier is alphanumeric +func TestAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric(t *testing.T) { + testAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric(t) +} + +// BenchmarkAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric benchmarks ForeignCorrespondentBankIDNumberQualifier is alphanumeric +func BenchmarkAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18ForeignCorrespondentBankIDQualifierAlphaNumeric(b) + } +} + +//testAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric validates ForeignCorrespondentBankBranchCountryCode is alphanumeric +func testAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric(t testing.TB) { + addenda18 := mockAddenda18() + addenda18.ForeignCorrespondentBankBranchCountryCode = "®©" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignCorrespondentBankBranchCountryCode" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda18ForeignCorrespondentBankBranchCountryCodeNumeric tests validating ForeignCorrespondentBankBranchCountryCode is alphanumeric +func TestAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric(t *testing.T) { + testAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric(t) +} + +// BenchmarkAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric benchmarks ForeignCorrespondentBankBranchCountryCode is alphanumeric +func BenchmarkAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric(b) + } +} + + +//testAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric validates ForeignCorrespondentBankIDNumber is alphanumeric +func testAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric(t testing.TB) { + addenda18 := mockAddenda18() + addenda18.ForeignCorrespondentBankIDNumber = "®©" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "ForeignCorrespondentBankIDNumber" { + t.Errorf("%T: %s", err, err) + } + } + } +} + +// TestAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric tests validating ForeignCorrespondentBankIDNumber is alphanumeric +func TestAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric(t *testing.T) { + testAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric(t) +} + +// BenchmarkAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric benchmarks ForeignCorrespondentBankIDNumber is alphanumeric +func BenchmarkAddendaForeignCorrespondentBankIDNumberAlphaNumeric(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric(b) + } +} + +// testAddenda18ValidTypeCode validates Addenda18 TypeCode +func testAddenda18ValidTypeCode(t testing.TB) { + addenda18 := mockAddenda18() + addenda18.typeCode = "65" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda18ValidTypeCode tests validating Addenda18 TypeCode +func TestAddenda18ValidTypeCode(t *testing.T) { + testAddenda18ValidTypeCode(t) +} + +// BenchmarkAddenda18ValidTypeCode benchmarks validating Addenda18 TypeCode +func BenchmarkAddenda18ValidTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18ValidTypeCode(b) + } +} + +// testAddenda18TypeCode18 TypeCode is 18 if typeCode is a valid TypeCode +func testAddenda18TypeCode18(t testing.TB) { + addenda18 := mockAddenda18() + addenda18.typeCode = "05" + if err := addenda18.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda18TypeCode18 tests TypeCode is 18 if typeCode is a valid TypeCode +func TestAddenda18TypeCode18(t *testing.T) { + testAddenda18TypeCode18(t) +} + +// BenchmarkAddenda18TypeCode18 benchmarks TypeCode is 18 if typeCode is a valid TypeCode +func BenchmarkAddenda18TypeCode18(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18TypeCode18(b) + } +} diff --git a/iatBatch.go b/iatBatch.go index 13e4e8340..8f890a0ab 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -12,6 +12,11 @@ import ( var ( msgIATBatchAddendaRequired = "is required for an IAT detail entry" msgIATBatchAddendaIndicator = "is invalid for addenda record(s) found" + // There can be up to 2 optional Addenda17 records and up to 5 optional Addenda18 records + msgBatchIATAddendum = "found and 7 Addendum is the maximum for SEC code IAT" + msgBatchIATAddenda17 = "found and 2 Addenda17 is the maximum for SEC code IAT" + msgBatchIATAddenda18 = "found and 5 Addenda18 is the maximum for SEC code IAT" + msgBatchIATInvalidAddendumer = "invalid Addendumer for SEC Code IAT" ) // IATBatch holds the Batch Header and Batch Control and all Entry Records for an IAT batch @@ -90,7 +95,6 @@ func (batch *IATBatch) verify() error { if err := batch.isTraceNumberODFI(); err != nil { return err } - // TODO this is specific to batch SEC types and should be called by that validator if err := batch.isAddendaSequence(); err != nil { return err } @@ -115,7 +119,6 @@ func (batch *IATBatch) build() error { seq := 1 for i, entry := range batch.Entries { entryCount = entryCount + 1 + 7 + len(entry.Addendum) - //ToDo: Add Addenda17 and Addenda18 maximum of 2 addenda17 and 5 addenda18 // Verifies the required addenda* properties for an IAT entry detail are defined if err := batch.addendaFieldInclusion(entry); err != nil { @@ -154,9 +157,12 @@ func (batch *IATBatch) build() error { a.SequenceNumber = addendaSeq a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) } + if a, ok := batch.Entries[i].Addendum[x].(*Addenda18); ok { + a.SequenceNumber = addendaSeq + a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) + } addendaSeq++ } - } // build a BatchControl record @@ -204,7 +210,7 @@ func (batch *IATBatch) AddEntry(entry *IATEntryDetail) { } // Category returns IATBatch Category -// ToDo: Verify this process is the same as a non IAT Batch + func (batch *IATBatch) Category() string { return batch.category } @@ -222,7 +228,6 @@ 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 @@ -245,6 +250,7 @@ func (batch *IATBatch) isFieldInclusion() error { if err := entry.Addenda16.Validate(); err != nil { return err } + } return batch.Control.Validate() } @@ -453,12 +459,34 @@ func (batch *IATBatch) Validate() error { } // Add configuration based validation for this type. - // IATBatch must have the following mandatory addenda per entry detail: - // Addenda10,Addenda11,Addenda12,Addenda13,Addenda14,Addenda15,Addenda16 + for _, entry := range batch.Entries { - // ToDo: IATBatch can have a maximum of 2 optional Addenda17 records - // ToDo: IAtBatch can have a maximum of 5 optional Addenda18 records + // 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} + } + addenda17Count := 0 + addenda18Count := 0 + for _, IATAddenda := range entry.Addendum { + + if (IATAddenda.TypeCode() != "17") && (IATAddenda.TypeCode() != "18") { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATInvalidAddendumer} + } + if IATAddenda.TypeCode() == "17" { + addenda17Count = addenda17Count + 1 + } + if IATAddenda.TypeCode() == "18" { + addenda17Count = addenda18Count + 1 + } + } + if addenda17Count > 2 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATAddenda17} + } + if addenda18Count > 5 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATAddenda18} + } + } // Add type specific validation. // ... return nil diff --git a/iatEntryDetail.go b/iatEntryDetail.go index b53d16e09..66ee7cdd5 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -91,11 +91,13 @@ type IATEntryDetail struct { // // The Addenda15 record identifies key information related to the Receiver. Addenda15 *Addenda15 `json:"addenda15,omitempty"` - // Addenda16 + // Addenda16 is mandatory for IAt entries // // Addenda16 record identifies additional key information related to the Receiver. Addenda16 *Addenda16 `json:"addenda16,omitempty"` - // Addendum a list of Addenda for the Entry Detail + // Addendum a list of Addenda for the Entry Detail. For IAT the addendumer is currently being used + // for the optional Addenda17 and Addenda18 records. + // ToDo: Consider reverting Addenda* explicit properties back to being addendumer Addendum []Addendumer `json:"addendum,omitempty"` // Category defines if the entry is a Forward, Return, or NOC Category string `json:"category,omitempty"` @@ -188,7 +190,6 @@ func (ed *IATEntryDetail) Validate() error { if err != nil { return &FieldError{FieldName: "CheckDigit", Value: ed.CheckDigit, Msg: err.Error()} } - if calculated != edCheckDigit { msg := fmt.Sprintf(msgValidCheckDigit, calculated) return &FieldError{FieldName: "RDFIIdentification", Value: ed.CheckDigit, Msg: msg} diff --git a/reader.go b/reader.go index a5d74b3bb..90713267a 100644 --- a/reader.go +++ b/reader.go @@ -432,64 +432,9 @@ func (r *Reader) parseIATAddenda() error { entry := r.IATCurrentBatch.GetEntries()[entryIndex] if entry.AddendaRecordIndicator == 1 { - switch r.line[1:3] { - case "10": - addenda10 := NewAddenda10() - addenda10.Parse(r.line) - if err := addenda10.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda10 = addenda10 - case "11": - addenda11 := NewAddenda11() - addenda11.Parse(r.line) - if err := addenda11.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda11 = addenda11 - case "12": - addenda12 := NewAddenda12() - addenda12.Parse(r.line) - if err := addenda12.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda12 = addenda12 - case "13": - - addenda13 := NewAddenda13() - addenda13.Parse(r.line) - if err := addenda13.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda13 = addenda13 - case "14": - addenda14 := NewAddenda14() - addenda14.Parse(r.line) - if err := addenda14.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda14 = addenda14 - case "15": - addenda15 := NewAddenda15() - addenda15.Parse(r.line) - if err := addenda15.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda15 = addenda15 - case "16": - addenda16 := NewAddenda16() - addenda16.Parse(r.line) - if err := addenda16.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda16 = addenda16 - case "17": - addenda17 := NewAddenda17() - addenda17.Parse(r.line) - if err := addenda17.Validate(); err != nil { - return r.error(err) - } - r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda17) + err := r.switchIATAddenda(entryIndex) + if err != nil { + return r.error(err) } } else { msg := fmt.Sprint(msgIATBatchAddendaIndicator) @@ -498,3 +443,73 @@ func (r *Reader) parseIATAddenda() error { return nil } + +func (r *Reader) switchIATAddenda(entryIndex int) error { + switch r.line[1:3] { + case "10": + addenda10 := NewAddenda10() + addenda10.Parse(r.line) + if err := addenda10.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda10 = addenda10 + case "11": + addenda11 := NewAddenda11() + addenda11.Parse(r.line) + if err := addenda11.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda11 = addenda11 + case "12": + addenda12 := NewAddenda12() + addenda12.Parse(r.line) + if err := addenda12.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda12 = addenda12 + case "13": + + addenda13 := NewAddenda13() + addenda13.Parse(r.line) + if err := addenda13.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda13 = addenda13 + case "14": + addenda14 := NewAddenda14() + addenda14.Parse(r.line) + if err := addenda14.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda14 = addenda14 + case "15": + addenda15 := NewAddenda15() + addenda15.Parse(r.line) + if err := addenda15.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda15 = addenda15 + case "16": + addenda16 := NewAddenda16() + addenda16.Parse(r.line) + if err := addenda16.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].Addenda16 = addenda16 + case "17": + addenda17 := NewAddenda17() + addenda17.Parse(r.line) + if err := addenda17.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda17) + case "18": + addenda18 := NewAddenda18() + addenda18.Parse(r.line) + if err := addenda18.Validate(); err != nil { + return r.error(err) + } + r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda18) + } + return nil +} diff --git a/reader_test.go b/reader_test.go index be2214dfc..fb756b5cf 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1143,7 +1143,7 @@ func BenchmarkACHFileRead3(b *testing.B) { } } -// testACHIATAddenda17 validates reading a file with IAT and Addenda 17 entries +// testACHIATAddenda17 validates reading a file with IAT and Addenda17 entries func testACHIATAddenda17(t testing.TB) { f, err := os.Open("./test/data/20180716-IAT-A17.ach") if err != nil { @@ -1190,3 +1190,51 @@ func BenchmarkACHIATAddenda17(b *testing.B) { testACHIATAddenda17(b) } } + +// testACHIATAddenda1718 validates reading a file with IAT and Addenda17 and Addenda18 entries +func testACHIATAddenda1718(t testing.TB) { + f, err := os.Open("./test/data/20180716-IAT-A17-A18.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } + + err2 := r.File.Validate() + + if err2 != nil { + if e, ok := err2.(*FileError); ok { + if e.FieldName != "BatchCount" { + t.Errorf("%T: %s", e, e) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHIATAddenda1718 tests validating reading a file with IAT and Addenda17 and Addenda18 entries +func TestACHIATAddenda1718(t *testing.T) { + testACHIATAddenda1718(t) +} + +// BenchmarkACHIATAddenda17 benchmarks validating reading a file with IAT Addenda17 and Addenda18 entries +func BenchmarkACHIATAddenda1718(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHIATAddenda1718(b) + } +} diff --git a/test/data/20180716-IAT-A17-A18.ach b/test/data/20180716-IAT-A17-A18.ach new file mode 100644 index 000000000..53240e527 --- /dev/null +++ b/test/data/20180716-IAT-A17-A18.ach @@ -0,0 +1,40 @@ +101 987654321 1234567891807160000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +717Transfer of money from one country to another 00020000001 +718Bank of Germany 01987987987654654 DE 00030000001 +718Bank of Spain 01987987987123123 ES 00040000001 +718Bank of France 01456456456987987 FR 00050000001 +718Bank of Turkey 0112312345678910 TR 00060000001 +718Bank of United Kingdom 011234567890123456789012345678901234GB 00070000001 +82200000150012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +717Transfer of money from one country to another 00020000001 +718Bank of Germany 01987987987654654 DE 00030000001 +718Bank of Spain 01987987987123123 ES 00040000001 +718Bank of France 01456456456987987 FR 00050000001 +718Bank of Turkey 0112312345678910 TR 00060000001 +718Bank of United Kingdom 011234567890123456789012345678901234GB 00070000001 +82200000150012104288000000002000000000000000 231380100000002 +9000002000004000000300024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/validators.go b/validators.go index b2d3abfda..5ff0e8505 100644 --- a/validators.go +++ b/validators.go @@ -247,7 +247,7 @@ func (v *validator) isTypeCode(code string) error { // Return, Dishonored Return and Contested Dishonored Return Entries "99", // IAT forward Entries and IAT Returns - "10", "11", "12", "13", "14", "15", "16", "17", + "10", "11", "12", "13", "14", "15", "16", "17", "18", // ACK, ATX, CCD, CIE, CTX, DNE, ENR, PPD, TRX and WEB Entries "05": return nil diff --git a/writer_test.go b/writer_test.go index 86771ff58..11073d99a 100644 --- a/writer_test.go +++ b/writer_test.go @@ -6,8 +6,6 @@ package ach import ( "bytes" - "log" - "os" "strings" "testing" ) @@ -122,6 +120,11 @@ func testIATWrite(t testing.TB) { iatBatch.Entries[0].Addenda16 = mockAddenda16() iatBatch.Entries[0].AddIATAddenda(mockAddenda17()) iatBatch.Entries[0].AddIATAddenda(mockAddenda17B()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18B()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18C()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18D()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18E()) iatBatch.Create() file.AddIATBatch(iatBatch) @@ -138,6 +141,12 @@ func testIATWrite(t testing.TB) { iatBatch2.Entries[0].Addenda15 = mockAddenda15() iatBatch2.Entries[0].Addenda16 = mockAddenda16() iatBatch2.Entries[0].AddIATAddenda(mockAddenda17()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda17B()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18B()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18C()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18D()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18E()) iatBatch2.Create() file.AddIATBatch(iatBatch2) @@ -164,12 +173,12 @@ func testIATWrite(t testing.TB) { t.Errorf("%T: %s", err, err) } - // Write IAT records to standard output. Anything io.Writer - w := NewWriter(os.Stdout) - if err := w.Write(file); err != nil { - log.Fatalf("Unexpected error: %s\n", err) - } - w.Flush() + /* // Write IAT records to standard output. Anything io.Writer + w := NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush()*/ } // TestIATWrite tests writing a IAT ACH file From b210a0a279aa501d675ddc36466227eaca707af9 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 16 Jul 2018 22:08:15 -0400 Subject: [PATCH 0315/1694] #211 gofmt and govet #211 gofmt and govet --- addenda18_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/addenda18_test.go b/addenda18_test.go index aa29a2cc3..ab53d141a 100644 --- a/addenda18_test.go +++ b/addenda18_test.go @@ -64,7 +64,6 @@ func mockAddenda18E() *Addenda18 { return addenda18 } - // TestMockAddenda18 validates mockAddenda18 func TestMockAddenda18(t *testing.T) { addenda18 := mockAddenda18() @@ -241,7 +240,6 @@ func BenchmarkAddenda18ForeignCorrespondentBankBranchCountryCodeAlphaNumeric(b * } } - //testAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric validates ForeignCorrespondentBankIDNumber is alphanumeric func testAddenda18ForeignCorrespondentBankIDNumberAlphaNumeric(t testing.TB) { addenda18 := mockAddenda18() From e144a8935fd304a85aa0ec895330d3b961d87fcc Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 16 Jul 2018 22:11:32 -0400 Subject: [PATCH 0316/1694] #211 golint #211 golint --- iatBatch.go | 1 - 1 file changed, 1 deletion(-) diff --git a/iatBatch.go b/iatBatch.go index 8f890a0ab..6284730e3 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -210,7 +210,6 @@ func (batch *IATBatch) AddEntry(entry *IATEntryDetail) { } // Category returns IATBatch Category - func (batch *IATBatch) Category() string { return batch.category } From 84ea4cc151bc6427bee9cf2c2a4fbf22884a97fa Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 09:34:08 -0400 Subject: [PATCH 0317/1694] # 211 SequenceNumber adjustment # 211 SequenceNumber adjustment --- test/data/20180716-IAT-A17-A18.ach | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/data/20180716-IAT-A17-A18.ach b/test/data/20180716-IAT-A17-A18.ach index 53240e527..99059a3be 100644 --- a/test/data/20180716-IAT-A17-A18.ach +++ b/test/data/20180716-IAT-A17-A18.ach @@ -10,11 +10,11 @@ 716LetterTown*AB\ CA*80014\ 0000001 717This is an international payment 00010000001 717Transfer of money from one country to another 00020000001 -718Bank of Germany 01987987987654654 DE 00030000001 -718Bank of Spain 01987987987123123 ES 00040000001 -718Bank of France 01456456456987987 FR 00050000001 -718Bank of Turkey 0112312345678910 TR 00060000001 -718Bank of United Kingdom 011234567890123456789012345678901234GB 00070000001 +718Bank of Germany 01987987987654654 DE 00010000001 +718Bank of Spain 01987987987123123 ES 00020000001 +718Bank of France 01456456456987987 FR 00030000001 +718Bank of Turkey 0112312345678910 TR 00040000001 +718Bank of United Kingdom 011234567890123456789012345678901234GB 00050000001 82200000150012104288000000000000000000100000 231380100000001 5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 6271210428820007 0000002000123456789 1231380100000001 @@ -27,11 +27,11 @@ 716LetterTown*AB\ CA*80014\ 0000001 717This is an international payment 00010000001 717Transfer of money from one country to another 00020000001 -718Bank of Germany 01987987987654654 DE 00030000001 -718Bank of Spain 01987987987123123 ES 00040000001 -718Bank of France 01456456456987987 FR 00050000001 -718Bank of Turkey 0112312345678910 TR 00060000001 -718Bank of United Kingdom 011234567890123456789012345678901234GB 00070000001 +718Bank of Germany 01987987987654654 DE 00010000001 +718Bank of Spain 01987987987123123 ES 00020000001 +718Bank of France 01456456456987987 FR 00030000001 +718Bank of Turkey 0112312345678910 TR 00040000001 +718Bank of United Kingdom 011234567890123456789012345678901234GB 00050000001 82200000150012104288000000002000000000000000 231380100000002 9000002000004000000300024208576000000002000000000100000 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 From 45a3257ffc625f8c7c2c47f4817755c6dbb0dbc6 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 09:41:26 -0400 Subject: [PATCH 0318/1694] #211 Adennda17 and Addenda18 SequenceNumber and EntryDetailSequenceNumber --- iatBatch.go | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/iatBatch.go b/iatBatch.go index 6284730e3..9412292bd 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -139,7 +139,6 @@ func (batch *IATBatch) build() error { if currentTraceNumberODFI != batchHeaderODFI { batch.Entries[i].SetTraceNumber(batch.Header.ODFIIdentification, seq) } - seq++ entry.Addenda10.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) entry.Addenda11.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) @@ -151,17 +150,20 @@ func (batch *IATBatch) build() error { // Addenda17 and Addenda18 SequenceNumber and EntryDetailSequenceNumber seq++ - addendaSeq := 1 + addenda17Seq := 1 + addenda18Seq := 1 for x := range entry.Addendum { if a, ok := batch.Entries[i].Addendum[x].(*Addenda17); ok { - a.SequenceNumber = addendaSeq + a.SequenceNumber = addenda17Seq a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) + addenda17Seq++ } + if a, ok := batch.Entries[i].Addendum[x].(*Addenda18); ok { - a.SequenceNumber = addendaSeq + a.SequenceNumber = addenda18Seq a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) + addenda18Seq++ } - addendaSeq++ } } @@ -386,7 +388,39 @@ func (batch *IATBatch) isAddendaSequence() error { msg := fmt.Sprintf(msgBatchAddendaTraceNumber, entry.Addenda16.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} } - //ToDo: Add Addenda17 and Addenda 18 logic for SequenceNUmber and EntryDetailSequenceNumber + + lastAddenda17Seq := -1 + lastAddenda18Seq := -1 + // check if sequence is ascending + for _, IATAddenda := range entry.Addendum { + // sequences don't exist in NOC or Return addenda + if a, ok := IATAddenda.(*Addenda17); ok { + + if a.SequenceNumber < lastAddenda17Seq { + msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastAddenda17Seq) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} + } + lastAddenda17Seq = a.SequenceNumber + // check that we are in the correct Entry Detail + if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) { + msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + } + } + if a, ok := IATAddenda.(*Addenda18); ok { + + if a.SequenceNumber < lastAddenda18Seq { + msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastAddenda18Seq) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg} + } + lastAddenda18Seq = a.SequenceNumber + // check that we are in the correct Entry Detail + if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) { + msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} + } + } + } } return nil } @@ -476,7 +510,7 @@ func (batch *IATBatch) Validate() error { addenda17Count = addenda17Count + 1 } if IATAddenda.TypeCode() == "18" { - addenda17Count = addenda18Count + 1 + addenda18Count = addenda18Count + 1 } } if addenda17Count > 2 { From 2f84e470387acae314bbbd61eb9a65de7f3bff7f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 09:59:49 -0400 Subject: [PATCH 0319/1694] #211 Add call to Validate for Addendumer #211 Add call to Validate for Addendumer --- iatBatch.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/iatBatch.go b/iatBatch.go index 9412292bd..175122e98 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -135,11 +135,12 @@ func (batch *IATBatch) build() error { return err } - // Add a sequenced TraceNumber if one is not already set. Have to keep original trance number Return and NOC entries + // Add a sequenced TraceNumber if one is not already set. if currentTraceNumberODFI != batchHeaderODFI { 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:]) @@ -148,7 +149,7 @@ func (batch *IATBatch) build() error { entry.Addenda15.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) entry.Addenda16.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) - // Addenda17 and Addenda18 SequenceNumber and EntryDetailSequenceNumber + // Set TraceNumber for Addendumer Addenda17 and Addenda18 SequenceNumber and EntryDetailSequenceNumber seq++ addenda17Seq := 1 addenda18Seq := 1 @@ -158,7 +159,6 @@ func (batch *IATBatch) build() error { a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) addenda17Seq++ } - if a, ok := batch.Entries[i].Addendum[x].(*Addenda18); ok { a.SequenceNumber = addenda18Seq a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) @@ -166,7 +166,6 @@ func (batch *IATBatch) build() error { } } } - // build a BatchControl record bc := NewBatchControl() bc.ServiceClassCode = batch.Header.ServiceClassCode @@ -251,6 +250,12 @@ func (batch *IATBatch) isFieldInclusion() error { if err := entry.Addenda16.Validate(); err != nil { return err } + // Verifies addendumer Addenda17 and Addenda18 records are valid + for _, IATAddenda := range entry.Addendum { + if err := IATAddenda.Validate(); err != nil { + return err + } + } } return batch.Control.Validate() @@ -346,7 +351,6 @@ func (batch *IATBatch) isTraceNumberODFI() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ODFIIdentificationField", Msg: msg} } } - return nil } @@ -389,11 +393,10 @@ func (batch *IATBatch) isAddendaSequence() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg} } + // check if sequence is ascending for addendumer - Addenda17 and Addenda18 lastAddenda17Seq := -1 lastAddenda18Seq := -1 - // check if sequence is ascending for _, IATAddenda := range entry.Addendum { - // sequences don't exist in NOC or Return addenda if a, ok := IATAddenda.(*Addenda17); ok { if a.SequenceNumber < lastAddenda17Seq { From 431abb6ea7b02130495c54706dde0e9c17c8a357 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 12:04:55 -0400 Subject: [PATCH 0320/1694] #211 iatBatch Code Coverage #211 iatBatch Code Coverage --- iatBatch_test.go | 308 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) diff --git a/iatBatch_test.go b/iatBatch_test.go index 105f1679d..2164ae36c 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -31,7 +31,9 @@ func mockIATBatch() IATBatch { func mockIATBatchManyEntries() IATBatch { mockBatch := IATBatch{} mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + mockBatch.Entries[0].Addenda10 = mockAddenda10() mockBatch.Entries[0].Addenda11 = mockAddenda11() mockBatch.Entries[0].Addenda12 = mockAddenda12() @@ -39,7 +41,16 @@ func mockIATBatchManyEntries() IATBatch { mockBatch.Entries[0].Addenda14 = mockAddenda14() mockBatch.Entries[0].Addenda15 = mockAddenda15() mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(mockAddenda17()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda17B()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18B()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18C()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18D()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18E()) + mockBatch.AddEntry(mockIATEntryDetail2()) + mockBatch.Entries[1].Addenda10 = mockAddenda10() mockBatch.Entries[1].Addenda11 = mockAddenda11() mockBatch.Entries[1].Addenda12 = mockAddenda12() @@ -47,12 +58,49 @@ func mockIATBatchManyEntries() IATBatch { mockBatch.Entries[1].Addenda14 = mockAddenda14() mockBatch.Entries[1].Addenda15 = mockAddenda15() mockBatch.Entries[1].Addenda16 = mockAddenda16() + mockBatch.Entries[1].AddIATAddenda(mockAddenda17()) + mockBatch.Entries[1].AddIATAddenda(mockAddenda17B()) + mockBatch.Entries[1].AddIATAddenda(mockAddenda18()) + mockBatch.Entries[1].AddIATAddenda(mockAddenda18B()) + mockBatch.Entries[1].AddIATAddenda(mockAddenda18C()) + mockBatch.Entries[1].AddIATAddenda(mockAddenda18D()) + mockBatch.Entries[1].AddIATAddenda(mockAddenda18E()) + if err := mockBatch.build(); err != nil { log.Fatal(err) } return mockBatch } +// mockIATBatch +func mockInvalidIATBatch() IATBatch { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(mockInvalidAddenda17()) + if err := mockBatch.build(); err != nil { + log.Fatal(err) + } + return mockBatch +} + +func mockInvalidAddenda17() *Addenda17 { + addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "Transfer of money from one country to another" + addenda17.typeCode = "02" + addenda17.SequenceNumber = 2 + addenda17.EntryDetailSequenceNumber = 0000002 + + return addenda17 +} + // TestMockIATBatch validates mockIATBatch func TestMockIATBatch(t *testing.T) { iatBatch := mockIATBatch() @@ -954,3 +1002,263 @@ func BenchmarkIATBatchIsCategory(b *testing.B) { testIATBatchIsCategory(b) } } + +// testIATBatchValidateEntry validates EntryDetail +func testIATBatchValidateEntry(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetEntries()[0].recordType = "5" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateEntry tests validating Entry +func TestIATBatchValidateEntry(t *testing.T) { + testIATBatchValidateEntry(t) +} + +// BenchmarkIATBatchValidateEntry tests validating Entry +func BenchmarkIATBatchValidateEntry(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateEntry(b) + } +} + +// testIATBatchValidateAddenda10 validates Addenda10 +func testIATBatchValidateAddenda10(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Addenda10.typeCode = "02" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda10 tests validating Addenda10 +func TestIATBatchValidateAddenda10(t *testing.T) { + testIATBatchValidateAddenda10(t) +} + +// BenchmarkIATBatchValidateAddenda10 tests validating Addenda10 +func BenchmarkIATBatchValidateAddenda10(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda10(b) + } +} + +// testIATBatchValidateAddenda11 validates Addenda11 +func testIATBatchValidateAddenda11(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Addenda11.typeCode = "02" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda11 tests validating Addenda11 +func TestIATBatchValidateAddenda11(t *testing.T) { + testIATBatchValidateAddenda11(t) +} + +// BenchmarkIATBatchValidateAddenda11 tests validating Addenda11 +func BenchmarkIATBatchValidateAddenda11(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda11(b) + } +} + +// testIATBatchValidateAddenda12 validates Addenda12 +func testIATBatchValidateAddenda12(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Addenda12.typeCode = "02" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda12 tests validating Addenda12 +func TestIATBatchValidateAddenda12(t *testing.T) { + testIATBatchValidateAddenda12(t) +} + +// BenchmarkIATBatchValidateAddenda12 tests validating Addenda12 +func BenchmarkIATBatchValidateAddenda12(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda12(b) + } +} + +// testIATBatchValidateAddenda13 validates Addenda13 +func testIATBatchValidateAddenda13(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Addenda13.typeCode = "02" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda13 tests validating Addenda13 +func TestIATBatchValidateAddenda13(t *testing.T) { + testIATBatchValidateAddenda13(t) +} + +// BenchmarkIATBatchValidateAddenda13 tests validating Addenda13 +func BenchmarkIATBatchValidateAddenda13(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda13(b) + } +} + +// testIATBatchValidateAddenda14 validates Addenda14 +func testIATBatchValidateAddenda14(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Addenda14.typeCode = "02" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda14 tests validating Addenda14 +func TestIATBatchValidateAddenda14(t *testing.T) { + testIATBatchValidateAddenda14(t) +} + +// BenchmarkIATBatchValidateAddenda14 tests validating Addenda14 +func BenchmarkIATBatchValidateAddenda14(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda14(b) + } +} + +// testIATBatchValidateAddenda15 validates Addenda15 +func testIATBatchValidateAddenda15(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Addenda15.typeCode = "02" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda15 tests validating Addenda15 +func TestIATBatchValidateAddenda15(t *testing.T) { + testIATBatchValidateAddenda15(t) +} + +// BenchmarkIATBatchValidateAddenda15 tests validating Addenda15 +func BenchmarkIATBatchValidateAddenda15(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda15(b) + } +} + +// testIATBatchValidateAddenda16 validates Addenda16 +func testIATBatchValidateAddenda16(t testing.TB) { + mockBatch := mockIATBatchManyEntries() + mockBatch.GetEntries()[1].Addenda16.typeCode = "02" + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda16 tests validating Addenda16 +func TestIATBatchValidateAddenda16(t *testing.T) { + testIATBatchValidateAddenda16(t) +} + +// BenchmarkIATBatchValidateAddenda16 tests validating Addenda16 +func BenchmarkIATBatchValidateAddenda16(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda16(b) + } +} + +// testIATBatchValidateAddenda17 validates Addenda17 +func testIATBatchValidateAddenda17(t testing.TB) { + mockBatch := mockInvalidIATBatch() + + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchValidateAddenda17 tests validating Addenda17 +func TestIATBatchValidateAddenda17(t *testing.T) { + testIATBatchValidateAddenda17(t) +} + +// BenchmarkIATBatchValidateAddenda17 tests validating Addenda17 +func BenchmarkIATBatchValidateAddenda17(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidateAddenda17(b) + } +} From e4a28714d5da061c38f15de472bce5b109b58136 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 15:38:56 -0400 Subject: [PATCH 0321/1694] #211 iatBatch Code Coverage #211 iatBatch Code Coverage --- addenda18_test.go | 17 ++++-- iatBatch.go | 11 ++-- iatBatch_test.go | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 7 deletions(-) diff --git a/addenda18_test.go b/addenda18_test.go index ab53d141a..3c7ed8447 100644 --- a/addenda18_test.go +++ b/addenda18_test.go @@ -37,7 +37,7 @@ func mockAddenda18C() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" addenda18.ForeignCorrespondentBankIDNumber = "456456456987987" addenda18.ForeignCorrespondentBankBranchCountryCode = "FR" - addenda18.SequenceNumber = 2 + addenda18.SequenceNumber = 3 addenda18.EntryDetailSequenceNumber = 0000003 return addenda18 } @@ -48,7 +48,7 @@ func mockAddenda18D() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" addenda18.ForeignCorrespondentBankIDNumber = "12312345678910" addenda18.ForeignCorrespondentBankBranchCountryCode = "TR" - addenda18.SequenceNumber = 2 + addenda18.SequenceNumber = 4 addenda18.EntryDetailSequenceNumber = 0000004 return addenda18 } @@ -59,11 +59,22 @@ func mockAddenda18E() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" addenda18.ForeignCorrespondentBankIDNumber = "1234567890123456789012345678901234" addenda18.ForeignCorrespondentBankBranchCountryCode = "GB" - addenda18.SequenceNumber = 2 + addenda18.SequenceNumber = 5 addenda18.EntryDetailSequenceNumber = 0000005 return addenda18 } +func mockAddenda18F() *Addenda18 { + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Antarctica" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "123456789012345678901" + addenda18.ForeignCorrespondentBankBranchCountryCode = "AQ" + addenda18.SequenceNumber = 6 + addenda18.EntryDetailSequenceNumber = 0000006 + return addenda18 +} + // TestMockAddenda18 validates mockAddenda18 func TestMockAddenda18(t *testing.T) { addenda18 := mockAddenda18() diff --git a/iatBatch.go b/iatBatch.go index 175122e98..7af9465be 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -13,9 +13,9 @@ var ( msgIATBatchAddendaRequired = "is required for an IAT detail entry" msgIATBatchAddendaIndicator = "is invalid for addenda record(s) found" // There can be up to 2 optional Addenda17 records and up to 5 optional Addenda18 records - msgBatchIATAddendum = "found and 7 Addendum is the maximum for SEC code IAT" - msgBatchIATAddenda17 = "found and 2 Addenda17 is the maximum for SEC code IAT" - msgBatchIATAddenda18 = "found and 5 Addenda18 is the maximum for SEC code IAT" + msgBatchIATAddendum = "7 Addendum is the maximum for SEC code IAT" + msgBatchIATAddenda17 = "2 Addenda17 is the maximum for SEC code IAT" + msgBatchIATAddenda18 = "5 Addenda18 is the maximum for SEC code IAT" msgBatchIATInvalidAddendumer = "invalid Addendumer for SEC Code IAT" ) @@ -159,6 +159,7 @@ func (batch *IATBatch) build() error { a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) addenda17Seq++ } + if a, ok := batch.Entries[i].Addendum[x].(*Addenda18); ok { a.SequenceNumber = addenda18Seq a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:]) @@ -166,6 +167,7 @@ func (batch *IATBatch) build() error { } } } + // build a BatchControl record bc := NewBatchControl() bc.ServiceClassCode = batch.Header.ServiceClassCode @@ -256,7 +258,6 @@ func (batch *IATBatch) isFieldInclusion() error { return err } } - } return batch.Control.Validate() } @@ -351,6 +352,7 @@ func (batch *IATBatch) isTraceNumberODFI() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ODFIIdentificationField", Msg: msg} } } + return nil } @@ -396,6 +398,7 @@ func (batch *IATBatch) isAddendaSequence() error { // check if sequence is ascending for addendumer - Addenda17 and Addenda18 lastAddenda17Seq := -1 lastAddenda18Seq := -1 + for _, IATAddenda := range entry.Addendum { if a, ok := IATAddenda.(*Addenda17); ok { diff --git a/iatBatch_test.go b/iatBatch_test.go index 2164ae36c..a2f9070ef 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -1003,6 +1003,32 @@ func BenchmarkIATBatchIsCategory(b *testing.B) { } } +//testIATBatchCategory tests IATBatch Category +func testIATBatchCategory(t testing.TB) { + mockBatch := mockIATBatch() + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + if mockBatch.Category() != CategoryForward { + t.Errorf("No returns and Category is %s", mockBatch.Category()) + } +} + +// TestIATBatchCategory tests IATBatch Category +func TestIATBatchCategory(t *testing.T) { + testIATBatchCategory(t) +} + +// BenchmarkIATBatchCategory benchmarks IATBatch Category +func BenchmarkIATBatchCategory(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchCategory(b) + } +} + // testIATBatchValidateEntry validates EntryDetail func testIATBatchValidateEntry(t testing.TB) { mockBatch := mockIATBatch() @@ -1262,3 +1288,112 @@ func BenchmarkIATBatchValidateAddenda17(b *testing.B) { testIATBatchValidateAddenda17(b) } } + +// testIATBatchCreateError validates IATBatch create error +func testIATBatchCreate(t testing.TB) { + file := NewFile().SetHeader(mockFileHeader()) + mockBatch := mockIATBatch() + mockBatch.GetHeader().recordType = "7" + + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + + file.AddIATBatch(mockBatch) +} + +// TestIATBatchCreate tests validating IATBatch create error +func TestIATBatchCreate(t *testing.T) { + testIATBatchCreate(t) +} + +// BenchmarkIATBatchCreate benchmarks validating IATBatch create error +func BenchmarkIATBatchCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchCreate(b) + } + +} + +// testIATBatchValidate validates IATBatch validate error +func testIATBatchValidate(t testing.TB) { + file := NewFile().SetHeader(mockFileHeader()) + mockBatch := mockIATBatch() + mockBatch.GetHeader().ServiceClassCode = 225 + + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + + file.AddIATBatch(mockBatch) +} + +// TestIATBatchValidate tests validating IATBatch validate error +func TestIATBatchValidate(t *testing.T) { + testIATBatchValidate(t) +} + +// BenchmarkIATBatchValidate benchmarks validating IATBatch validate error +func BenchmarkIATBatchValidate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchValidate(b) + } + +} + +// testIATBatchEntryAddendum validates IATBatch EntryAddendum error +func testIATBatchEntryAddendum(t testing.TB) { + file := NewFile().SetHeader(mockFileHeader()) + mockBatch := mockIATBatch() + mockBatch.Entries[0].AddIATAddenda(mockAddenda17()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda17B()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18B()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18C()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18D()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18E()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18F()) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + + file.AddIATBatch(mockBatch) +} + +// TestIATBatchEntryAddendum tests validating IATBatch EntryAddendum error +func TestIATBatchEntryAddendum(t *testing.T) { + testIATBatchEntryAddendum(t) +} + +// BenchmarkIATBatchEntryAddendum benchmarks validating IATBatch EntryAddendum error +func BenchmarkIATBatchEntryAddendum(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchEntryAddendum(b) + } +} From 78996792206e83927429212a610f5e00b3cd890e Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 15:43:20 -0400 Subject: [PATCH 0322/1694] #211 error with goveralls on github #211 error with goveralls on github --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 163aba0b0..35573104b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ env: global: secure: QPkcX77j8QEqTwOYyLGItqvxYwE6Na5WaSZWjmhp48OlxYatWRHxJBwcFYSn1OWD5FMn+3oW39fHknReIxtrnhXMaNvI7x3/0gy4zujD/xZ2xAg7NsQ+l5buvEFO8/LEwwo0fp4knItFcBv8xH/ziJBJyXvgfMtj7Is4Q/pB1p6pWDdVy1vtAj3zH02bcqh1yXXS3HvcD8UhTszfU017gVNXDN1ow0rp1L3ainr3btrVK9izUxZfKvb7PlWJO1ogah7xNr/dIOJLsx2SfKgzKp+3H28L2WegtbzON74Op4jXvRywCwqjmUt/nwJ/Y9anunMNHT136h+ye4ziG1i/VdbWq0Q4PopQ8yYqinujG7SjfQio+wNCV2cwc2r/WjNBjbH0N9/Pflogq3RHvgy/9VtPif1tY+RrZCSntohoEZbYpVcFQFE1xDyf6xq/uLxVeEcCU33gqq7cKEfpcUgyCITa+yCPfBdtgkLBJ8h7Sew1j08D1kTKUW6g3D1epmwlCh/Z16oHG5VwSnCLGDjJy8wm/hQk1i/g7qeP7g24CfNzffzlFBCy88HhjzmrhUpcaTyfVVDf4h8wK6Zu/J3dHjHXQYwfiQRqpMa+2DYyjGgZhniccuh4GWolGZauDQdmO9SD4Ugyt9PEMk02i32ax3A4XE/Q6VNOam+qszviX3Q= before_install: -- go get github.com/mattn/goveralls - go get -u github.com/client9/misspell/cmd/misspell - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo From 16dec66bd09b722a24a4131975e74594d3969960 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 17:27:26 -0400 Subject: [PATCH 0323/1694] #211 EntryDetailSequenceNumber #211 EntryDetailSequenceNumber --- addenda17_test.go | 2 +- addenda18_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addenda17_test.go b/addenda17_test.go index 0942d8aa4..5e4289856 100644 --- a/addenda17_test.go +++ b/addenda17_test.go @@ -22,7 +22,7 @@ func mockAddenda17B() *Addenda17 { addenda17 := NewAddenda17() addenda17.PaymentRelatedInformation = "Transfer of money from one country to another" addenda17.SequenceNumber = 2 - addenda17.EntryDetailSequenceNumber = 0000002 + addenda17.EntryDetailSequenceNumber = 0000001 return addenda17 } diff --git a/addenda18_test.go b/addenda18_test.go index 3c7ed8447..2dd65fdb9 100644 --- a/addenda18_test.go +++ b/addenda18_test.go @@ -27,7 +27,7 @@ func mockAddenda18B() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumber = "987987987123123" addenda18.ForeignCorrespondentBankBranchCountryCode = "ES" addenda18.SequenceNumber = 2 - addenda18.EntryDetailSequenceNumber = 0000002 + addenda18.EntryDetailSequenceNumber = 0000001 return addenda18 } @@ -38,7 +38,7 @@ func mockAddenda18C() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumber = "456456456987987" addenda18.ForeignCorrespondentBankBranchCountryCode = "FR" addenda18.SequenceNumber = 3 - addenda18.EntryDetailSequenceNumber = 0000003 + addenda18.EntryDetailSequenceNumber = 0000001 return addenda18 } @@ -49,7 +49,7 @@ func mockAddenda18D() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumber = "12312345678910" addenda18.ForeignCorrespondentBankBranchCountryCode = "TR" addenda18.SequenceNumber = 4 - addenda18.EntryDetailSequenceNumber = 0000004 + addenda18.EntryDetailSequenceNumber = 0000001 return addenda18 } @@ -60,7 +60,7 @@ func mockAddenda18E() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumber = "1234567890123456789012345678901234" addenda18.ForeignCorrespondentBankBranchCountryCode = "GB" addenda18.SequenceNumber = 5 - addenda18.EntryDetailSequenceNumber = 0000005 + addenda18.EntryDetailSequenceNumber = 0000001 return addenda18 } @@ -71,7 +71,7 @@ func mockAddenda18F() *Addenda18 { addenda18.ForeignCorrespondentBankIDNumber = "123456789012345678901" addenda18.ForeignCorrespondentBankBranchCountryCode = "AQ" addenda18.SequenceNumber = 6 - addenda18.EntryDetailSequenceNumber = 0000006 + addenda18.EntryDetailSequenceNumber = 0000001 return addenda18 } From a435bdcb991d0702a8dd804bc77b8f3c862150f1 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 20:47:23 -0400 Subject: [PATCH 0324/1694] #211 iatBatch Code Coverage #211 iatBatch Code Coverage --- iatBatch_test.go | 254 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) diff --git a/iatBatch_test.go b/iatBatch_test.go index a2f9070ef..007b649f9 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -1397,3 +1397,257 @@ func BenchmarkIATBatchEntryAddendum(b *testing.B) { testIATBatchEntryAddendum(b) } } + +// testIATBatchAddenda17EDSequenceNumber validates IATBatch Addenda17 Entry Detail Sequence Number error +func testIATBatchAddenda17EDSequenceNumber(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "This is an international payment" + addenda17.SequenceNumber = 1 + addenda17.EntryDetailSequenceNumber = 0000001 + addenda17B := NewAddenda17() + addenda17B.PaymentRelatedInformation = "Transfer of money from one country to another" + addenda17B.SequenceNumber = 2 + addenda17B.EntryDetailSequenceNumber = 0000001 + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(addenda17) + mockBatch.Entries[0].AddIATAddenda(addenda17B) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + addenda17B.SequenceNumber = 1 + addenda17B.EntryDetailSequenceNumber = 0000002 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +// TestIATBatchAddenda17EDSequenceNumber tests validating IATBatch Addenda17 Entry Detail Sequence Number error +func TestIATBatchAddenda17EDSequenceNumber(t *testing.T) { + testIATBatchAddenda17EDSequenceNumber(t) +} + +// BenchmarkIATBatchAddenda17EDSequenceNumber benchmarks validating IATBatch Addenda17 Entry Detail Sequence Number error +func BenchmarkIATBatchAddenda17EDSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda17EDSequenceNumber(b) + } +} + +// testIATBatchAddenda17Sequence validates IATBatch Addenda17 Sequence Number error +func testIATBatchAddenda17Sequence(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "This is an international payment" + addenda17.SequenceNumber = 2 + addenda17.EntryDetailSequenceNumber = 0000001 + addenda17B := NewAddenda17() + addenda17B.PaymentRelatedInformation = "Transfer of money from one country to another" + addenda17B.SequenceNumber = 1 + addenda17B.EntryDetailSequenceNumber = 0000001 + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(addenda17) + mockBatch.Entries[0].AddIATAddenda(addenda17B) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + addenda17B.SequenceNumber = -1 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "SequenceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +// TestIATBatchAddenda17Sequence tests validating IATBatch Addenda17 Sequence Number error +func TestIATBatchAddenda17Sequence(t *testing.T) { + testIATBatchAddenda17Sequence(t) +} + +// BenchmarkIATBatchAddenda17Sequence benchmarks validating IATBatch Addenda17 Sequence Number error +func BenchmarkIATBatchAddenda17Sequence(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda17Sequence(b) + } +} + +// testIATBatchAddenda18EDSequenceNumber validates IATBatch Addenda18 Entry Detail Sequence Number error +func testIATBatchAddenda18EDSequenceNumber(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "This is an international payment" + addenda17.SequenceNumber = 1 + addenda17.EntryDetailSequenceNumber = 0000001 + + addenda17B := NewAddenda17() + addenda17B.PaymentRelatedInformation = "Transfer of money from one country to another" + addenda17B.SequenceNumber = 2 + addenda17B.EntryDetailSequenceNumber = 0000001 + + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of Turkey" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "12312345678910" + addenda18.ForeignCorrespondentBankBranchCountryCode = "TR" + addenda18.SequenceNumber = 1 + addenda18.EntryDetailSequenceNumber = 0000001 + + addenda18B := NewAddenda18() + addenda18B.ForeignCorrespondentBankName = "Bank of United Kingdom" + addenda18B.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18B.ForeignCorrespondentBankIDNumber = "1234567890123456789012345678901234" + addenda18B.ForeignCorrespondentBankBranchCountryCode = "GB" + addenda18B.SequenceNumber = 2 + addenda18B.EntryDetailSequenceNumber = 0000001 + + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(addenda17) + mockBatch.Entries[0].AddIATAddenda(addenda17B) + mockBatch.Entries[0].AddIATAddenda(addenda18) + mockBatch.Entries[0].AddIATAddenda(addenda18B) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + addenda18B.SequenceNumber = 1 + addenda18B.EntryDetailSequenceNumber = 0000002 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "TraceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +// TestIATBatchAddenda18EDSequenceNumber tests validating IATBatch Addenda18 Entry Detail Sequence Number error +func TestIATBatchAddenda18EDSequenceNumber(t *testing.T) { + testIATBatchAddenda18EDSequenceNumber(t) +} + +// BenchmarkIATBatchAddenda18EDSequenceNumber benchmarks validating IATBatch Addenda18 Entry Detail Sequence Number error +func BenchmarkIATBatchAddenda18EDSequenceNumber(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda18EDSequenceNumber(b) + } +} + +// testIATBatchAddenda18Sequence validates IATBatch Addenda18 Sequence Number error +func testIATBatchAddenda18Sequence(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "This is an international payment" + addenda17.SequenceNumber = 1 + addenda17.EntryDetailSequenceNumber = 0000001 + + addenda17B := NewAddenda17() + addenda17B.PaymentRelatedInformation = "Transfer of money from one country to another" + addenda17B.SequenceNumber = 2 + addenda17B.EntryDetailSequenceNumber = 0000001 + + addenda18 := NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of Turkey" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "12312345678910" + addenda18.ForeignCorrespondentBankBranchCountryCode = "TR" + addenda18.SequenceNumber = 1 + addenda18.EntryDetailSequenceNumber = 0000001 + + addenda18B := NewAddenda18() + addenda18B.ForeignCorrespondentBankName = "Bank of United Kingdom" + addenda18B.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18B.ForeignCorrespondentBankIDNumber = "1234567890123456789012345678901234" + addenda18B.ForeignCorrespondentBankBranchCountryCode = "GB" + addenda18B.SequenceNumber = 2 + addenda18B.EntryDetailSequenceNumber = 0000001 + + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(addenda17) + mockBatch.Entries[0].AddIATAddenda(addenda17B) + mockBatch.Entries[0].AddIATAddenda(addenda18) + mockBatch.Entries[0].AddIATAddenda(addenda18B) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + addenda18B.SequenceNumber = -1 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "SequenceNumber" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +// TestIATBatchAddenda18Sequence tests validating IATBatch Addenda18 Sequence Number error +func TestIATBatchAddenda18Sequence(t *testing.T) { + testIATBatchAddenda18Sequence(t) +} + +// BenchmarkIATBatchAddenda18Sequence benchmarks validating IATBatch Addenda18 Sequence Number error +func BenchmarkIATBatchAddenda18Sequence(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda18Sequence(b) + } +} From ff7a1db08996bb367b83996c12299fe947c4d266 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 17 Jul 2018 20:55:53 -0400 Subject: [PATCH 0325/1694] #211 revert removal of goveralls #211 revert removal of goveralls --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 35573104b..163aba0b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ env: global: secure: QPkcX77j8QEqTwOYyLGItqvxYwE6Na5WaSZWjmhp48OlxYatWRHxJBwcFYSn1OWD5FMn+3oW39fHknReIxtrnhXMaNvI7x3/0gy4zujD/xZ2xAg7NsQ+l5buvEFO8/LEwwo0fp4knItFcBv8xH/ziJBJyXvgfMtj7Is4Q/pB1p6pWDdVy1vtAj3zH02bcqh1yXXS3HvcD8UhTszfU017gVNXDN1ow0rp1L3ainr3btrVK9izUxZfKvb7PlWJO1ogah7xNr/dIOJLsx2SfKgzKp+3H28L2WegtbzON74Op4jXvRywCwqjmUt/nwJ/Y9anunMNHT136h+ye4ziG1i/VdbWq0Q4PopQ8yYqinujG7SjfQio+wNCV2cwc2r/WjNBjbH0N9/Pflogq3RHvgy/9VtPif1tY+RrZCSntohoEZbYpVcFQFE1xDyf6xq/uLxVeEcCU33gqq7cKEfpcUgyCITa+yCPfBdtgkLBJ8h7Sew1j08D1kTKUW6g3D1epmwlCh/Z16oHG5VwSnCLGDjJy8wm/hQk1i/g7qeP7g24CfNzffzlFBCy88HhjzmrhUpcaTyfVVDf4h8wK6Zu/J3dHjHXQYwfiQRqpMa+2DYyjGgZhniccuh4GWolGZauDQdmO9SD4Ugyt9PEMk02i32ax3A4XE/Q6VNOam+qszviX3Q= before_install: +- go get github.com/mattn/goveralls - go get -u github.com/client9/misspell/cmd/misspell - go get -u golang.org/x/lint/golint - go get github.com/fzipp/gocyclo From baac7a632abfd2a0acccadcd5ff0912ef3cd0620 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 18 Jul 2018 10:04:41 -0400 Subject: [PATCH 0326/1694] #211 iatBatch code coverage #211 iatBatch code coverage --- iatBatch_test.go | 249 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 240 insertions(+), 9 deletions(-) diff --git a/iatBatch_test.go b/iatBatch_test.go index 007b649f9..e224df458 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -734,7 +734,7 @@ func BenchmarkIATBatchFieldInclusion(b *testing.B) { } -// testIATBatchBuildError validates IATBatch build error +// testIATBatchBuild validates IATBatch build error func testIATBatchBuild(t testing.TB) { mockBatch := IATBatch{} mockBatch.SetHeader(mockIATBatchHeaderFF()) @@ -1514,12 +1514,10 @@ func testIATBatchAddenda18EDSequenceNumber(t testing.TB) { addenda17.PaymentRelatedInformation = "This is an international payment" addenda17.SequenceNumber = 1 addenda17.EntryDetailSequenceNumber = 0000001 - addenda17B := NewAddenda17() addenda17B.PaymentRelatedInformation = "Transfer of money from one country to another" addenda17B.SequenceNumber = 2 addenda17B.EntryDetailSequenceNumber = 0000001 - addenda18 := NewAddenda18() addenda18.ForeignCorrespondentBankName = "Bank of Turkey" addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" @@ -1527,7 +1525,6 @@ func testIATBatchAddenda18EDSequenceNumber(t testing.TB) { addenda18.ForeignCorrespondentBankBranchCountryCode = "TR" addenda18.SequenceNumber = 1 addenda18.EntryDetailSequenceNumber = 0000001 - addenda18B := NewAddenda18() addenda18B.ForeignCorrespondentBankName = "Bank of United Kingdom" addenda18B.ForeignCorrespondentBankIDNumberQualifier = "01" @@ -1535,7 +1532,6 @@ func testIATBatchAddenda18EDSequenceNumber(t testing.TB) { addenda18B.ForeignCorrespondentBankBranchCountryCode = "GB" addenda18B.SequenceNumber = 2 addenda18B.EntryDetailSequenceNumber = 0000001 - mockBatch.Entries[0].Addenda10 = mockAddenda10() mockBatch.Entries[0].Addenda11 = mockAddenda11() mockBatch.Entries[0].Addenda12 = mockAddenda12() @@ -1588,12 +1584,10 @@ func testIATBatchAddenda18Sequence(t testing.TB) { addenda17.PaymentRelatedInformation = "This is an international payment" addenda17.SequenceNumber = 1 addenda17.EntryDetailSequenceNumber = 0000001 - addenda17B := NewAddenda17() addenda17B.PaymentRelatedInformation = "Transfer of money from one country to another" addenda17B.SequenceNumber = 2 addenda17B.EntryDetailSequenceNumber = 0000001 - addenda18 := NewAddenda18() addenda18.ForeignCorrespondentBankName = "Bank of Turkey" addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" @@ -1601,7 +1595,6 @@ func testIATBatchAddenda18Sequence(t testing.TB) { addenda18.ForeignCorrespondentBankBranchCountryCode = "TR" addenda18.SequenceNumber = 1 addenda18.EntryDetailSequenceNumber = 0000001 - addenda18B := NewAddenda18() addenda18B.ForeignCorrespondentBankName = "Bank of United Kingdom" addenda18B.ForeignCorrespondentBankIDNumberQualifier = "01" @@ -1609,7 +1602,6 @@ func testIATBatchAddenda18Sequence(t testing.TB) { addenda18B.ForeignCorrespondentBankBranchCountryCode = "GB" addenda18B.SequenceNumber = 2 addenda18B.EntryDetailSequenceNumber = 0000001 - mockBatch.Entries[0].Addenda10 = mockAddenda10() mockBatch.Entries[0].Addenda11 = mockAddenda11() mockBatch.Entries[0].Addenda12 = mockAddenda12() @@ -1651,3 +1643,242 @@ func BenchmarkIATBatchAddenda18Sequence(b *testing.B) { testIATBatchAddenda18Sequence(b) } } + +// testIATNoEntry validates error for no entries +func testIATNoEntry(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATNoEntry tests validating error for no entries +func TestIATNoEntry(t *testing.T) { + testIATNoEntry(t) +} + +// BenchmarkIATNoEntry benchmarks validating error for no entries +func BenchmarkIATNoEntry(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATNoEntry(b) + } +} + +// testIATBatchAddendumTypeCode validates IATBatch Addendum TypeCode +func testIATBatchAddendumTypeCode(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetEntries()[0].AddIATAddenda(mockAddenda12()) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchAddendumTypeCode tests validating IATBatch Addendum TypeCode +func TestIATBatchAddendumTypeCode(t *testing.T) { + testIATBatchAddendumTypeCode(t) +} + +// BenchmarkIATBatchAddendumTypeCode benchmarks validating IATBatch Addendum TypeCode +func BenchmarkIATBatchAddendumTypeCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddendumTypeCode(b) + } + +} + +// testIATBatchAddenda17Count validates IATBatch Addenda17 Count +func testIATBatchAddenda17Count(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + addenda17 := NewAddenda17() + addenda17.PaymentRelatedInformation = "This is an international payment" + addenda17.SequenceNumber = 1 + addenda17.EntryDetailSequenceNumber = 0000001 + addenda17B := NewAddenda17() + addenda17B.PaymentRelatedInformation = "Transfer of money from one country to another" + addenda17B.SequenceNumber = 2 + addenda17B.EntryDetailSequenceNumber = 0000001 + addenda17C := NewAddenda17() + addenda17C.PaymentRelatedInformation = "Send money Internationally" + addenda17C.SequenceNumber = 3 + addenda17C.EntryDetailSequenceNumber = 0000001 + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(addenda17) + mockBatch.Entries[0].AddIATAddenda(addenda17B) + mockBatch.Entries[0].AddIATAddenda(addenda17C) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +// TestIATBatchAddenda17Count tests validating IATBatch Addenda17 Count +func TestIATBatchAddenda17Count(t *testing.T) { + testIATBatchAddenda17Count(t) +} + +// BenchmarkIATBatchAddenda17Count benchmarks validating IATBatch Addenda17 Count +func BenchmarkIATBatchAddenda17Count(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda17Count(b) + } +} + +// testIATBatchAddenda18Count validates IATBatch Addenda18 Count +func testIATBatchAddenda18Count(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(mockAddenda17()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda17B()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18B()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18C()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18D()) + mockBatch.Entries[0].AddIATAddenda(mockAddenda18E()) + + addenda18F := NewAddenda18() + addenda18F.ForeignCorrespondentBankName = "Russian Federation" + addenda18F.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18F.ForeignCorrespondentBankIDNumber = "123123456789943874" + addenda18F.ForeignCorrespondentBankBranchCountryCode = "RU" + addenda18F.SequenceNumber = 6 + addenda18F.EntryDetailSequenceNumber = 0000001 + + mockBatch.Entries[0].AddIATAddenda(mockAddenda18F()) + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +// TestIATBatchAddenda18Count tests validating IATBatch Addenda18 Count +func TestIATBatchAddenda18Count(t *testing.T) { + testIATBatchAddenda18Count(t) +} + +// BenchmarkIATBatchAddenda18Count benchmarks validating IATBatch Addenda18 Count +func BenchmarkIATBatchAddenda18Count(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda18Count(b) + } +} + +// testIATBatchBuildAddendaError validates IATBatch build Addenda error +func testIATBatchBuildAddendaError(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + + if err := mockBatch.build(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addenda10" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchBuildAddendaError tests validating IATBatch build Addenda error +func TestIATBatchBuildAddendaError(t *testing.T) { + testIATBatchBuildAddendaError(t) +} + +// BenchmarkIATBatchBuildAddendaError benchmarks validating IATBatch build Addenda error +func BenchmarkIATBatchBuildAddendaError(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchBuildAddendaError(b) + } + +} + +// testIATBatchBHODFI validates IATBatchHeader ODFI error +func testIATBatchBHODFI(t testing.TB) { + mockBatch := mockIATBatch() + mockBatch.GetEntries()[0].SetTraceNumber("39387337", 1) + + if err := mockBatch.build(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestIATBatchBHODFI tests validating IATBatchHeader ODFI error +func TestIATBatchBHODFI(t *testing.T) { + testIATBatchBHODFI(t) +} + +// BenchmarkIATBatchBHODFI benchmarks validating IATBatchHeader ODFI error +func BenchmarkIATBatchBHODFI(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchBHODFI(b) + } + +} From 80348a2e22c207900f41aac9f44d9988a944e540 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 18 Jul 2018 10:36:03 -0400 Subject: [PATCH 0327/1694] #211 iatBatch #211 iatBatch comment code in isCategory regarding NOC --- iatBatch.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/iatBatch.go b/iatBatch.go index 7af9465be..22eccb13d 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -436,9 +436,10 @@ func (batch *IATBatch) isCategory() error { category := batch.GetEntries()[0].Category if len(batch.Entries) > 1 { for i := 1; i < len(batch.Entries); i++ { - if batch.Entries[i].Category == CategoryNOC { + // ToDo: Need to research requirements for Notice of change fo IAT + /*if batch.Entries[i].Category == CategoryNOC { continue - } + }*/ if batch.Entries[i].Category != category { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Category", Msg: msgBatchForwardReturn} } From d8044278cdc3e553e969f63f6daba2a7e2849341 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 18 Jul 2018 15:11:39 -0400 Subject: [PATCH 0328/1694] #211 IAT Addenda Record Parsing tests #211 IAT Addenda Record Parsing tests --- addenda10.go | 5 +++-- addenda10_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++++--- addenda11.go | 5 +++-- addenda11_test.go | 41 +++++++++++++++++++++++++++++++++++++-- addenda12.go | 5 +++-- addenda12_test.go | 39 +++++++++++++++++++++++++++++++++++++ addenda13.go | 5 +++-- addenda13_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++--- addenda14.go | 5 +++-- addenda14_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++-- addenda15.go | 3 ++- addenda15_test.go | 41 +++++++++++++++++++++++++++++++++++++-- addenda16.go | 5 +++-- addenda16_test.go | 39 +++++++++++++++++++++++++++++++++++++ addenda17_test.go | 38 ++++++++++++++++++++++++++++++++++-- addenda18.go | 7 ++++--- addenda18_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++-- iatBatch_test.go | 2 +- 18 files changed, 398 insertions(+), 33 deletions(-) diff --git a/addenda10.go b/addenda10.go index 84632c222..1d6cf7cec 100644 --- a/addenda10.go +++ b/addenda10.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda10 is an addenda which provides business transaction information for Addenda Type @@ -67,9 +68,9 @@ func (addenda10 *Addenda10) Parse(record string) { // 07-24 Payment Amount For inbound IAT payments this field should contain the USD amount or may be blank. addenda10.ForeignPaymentAmount = addenda10.parseNumField(record[06:24]) // 25-46 Insert blanks or zeros - addenda10.ForeignTraceNumber = record[24:46] + addenda10.ForeignTraceNumber = strings.TrimSpace(record[24:46]) // 47-81 Receiving Company Name/Individual Name - addenda10.Name = record[46:81] + addenda10.Name = strings.TrimSpace(record[46:81]) // 82-87 reserved - Leave blank addenda10.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record diff --git a/addenda10_test.go b/addenda10_test.go index d101307b2..e5bfe9df6 100644 --- a/addenda10_test.go +++ b/addenda10_test.go @@ -27,6 +27,51 @@ func TestMockAddenda10(t *testing.T) { } } +// testAddenda10Parse parses Addenda10 record +func testAddenda10Parse(t testing.TB) { + addenda10 := NewAddenda10() + line := "710ANN000000000000100000928383-23938 BEK Enterprises 0000001" + addenda10.Parse(line) + // walk the Addenda10 struct + if addenda10.recordType != "7" { + t.Errorf("expected %v got %v", "7", addenda10.recordType) + } + if addenda10.typeCode != "10" { + t.Errorf("expected %v got %v", "10", addenda10.typeCode) + } + if addenda10.TransactionTypeCode != "ANN" { + t.Errorf("expected %v got %v", "ANN", addenda10.TransactionTypeCode) + } + if addenda10.ForeignPaymentAmount != 100000 { + t.Errorf("expected: %v got: %v", 100000, addenda10.ForeignPaymentAmount) + } + if addenda10.ForeignTraceNumber != "928383-23938" { + t.Errorf("expected: %v got: %v", "928383-23938", addenda10.ForeignTraceNumber) + } + if addenda10.Name != "BEK Enterprises" { + t.Errorf("expected: %s got: %s", "BEK Enterprises", addenda10.Name) + } + if addenda10.reserved != " " { + t.Errorf("expected: %v got: %v", " ", addenda10.reserved) + } + if addenda10.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, addenda10.EntryDetailSequenceNumber) + } +} + +// TestAddenda10Parse tests parsing Addenda10 record +func TestAddenda10Parse(t *testing.T) { + testAddenda10Parse(t) +} + +// BenchmarkAddenda10Parse benchmarks parsing Addenda10 record +func BenchmarkAddenda10Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda10Parse(b) + } +} + // testAddenda10ValidRecordType validates Addenda10 recordType func testAddenda10ValidRecordType(t testing.TB) { addenda10 := mockAddenda10() @@ -351,12 +396,10 @@ func BenchmarkAddenda10FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } -// ToDo Add Parse test for individual fields - // TestAddenda10String validates that a known parsed Addenda10 record can be return to a string of the same value func testAddenda10String(t testing.TB) { addenda10 := NewAddenda10() - var line = "710ANN000000000000100000928383-23938 BEK Enterprises 0000001" + var line = "710ANN000000000000100000928383-23938 BEK Enterprises 0000001" addenda10.Parse(line) if addenda10.String() != line { diff --git a/addenda11.go b/addenda11.go index 98aec15ff..831ba1775 100644 --- a/addenda11.go +++ b/addenda11.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda11 is an addenda which provides business transaction information for Addenda Type @@ -54,9 +55,9 @@ func (addenda11 *Addenda11) Parse(record string) { // 2-3 Always 11 addenda11.typeCode = record[1:3] // 4-38 - addenda11.OriginatorName = record[3:38] + addenda11.OriginatorName = strings.TrimSpace(record[3:38]) // 39-73 - addenda11.OriginatorStreetAddress = record[38:73] + addenda11.OriginatorStreetAddress = strings.TrimSpace(record[38:73]) // 74-87 reserved - Leave blank addenda11.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record diff --git a/addenda11_test.go b/addenda11_test.go index 55867128d..283563545 100644 --- a/addenda11_test.go +++ b/addenda11_test.go @@ -25,6 +25,45 @@ func TestMockAddenda11(t *testing.T) { } } +// testAddenda11Parse parses Addenda11 record +func testAddenda11Parse(t testing.TB) { + Addenda11 := NewAddenda11() + line := "711BEK Solutions 15 West Place Street 0000001" + Addenda11.Parse(line) + // walk the Addenda11 struct + if Addenda11.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda11.recordType) + } + if Addenda11.typeCode != "11" { + t.Errorf("expected %v got %v", "11", Addenda11.typeCode) + } + if Addenda11.OriginatorName != "BEK Solutions" { + t.Errorf("expected %v got %v", "BEK Solutions", Addenda11.OriginatorName) + } + if Addenda11.OriginatorStreetAddress != "15 West Place Street" { + t.Errorf("expected: %v got: %v", "15 West Place Street", Addenda11.OriginatorStreetAddress) + } + if Addenda11.reserved != " " { + t.Errorf("expected: %v got: %v", " ", Addenda11.reserved) + } + if Addenda11.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda11.EntryDetailSequenceNumber) + } +} + +// TestAddenda11Parse tests parsing Addenda11 record +func TestAddenda11Parse(t *testing.T) { + testAddenda11Parse(t) +} + +// BenchmarkAddenda11Parse benchmarks parsing Addenda11 record +func BenchmarkAddenda11Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda11Parse(b) + } +} + // testAddenda11ValidRecordType validates Addenda11 recordType func testAddenda11ValidRecordType(t testing.TB) { addenda11 := mockAddenda11() @@ -293,8 +332,6 @@ func BenchmarkAddenda11FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } -// ToDo Add Parse test for individual fields - // TestAddenda11String validates that a known parsed Addenda11 record can be return to a string of the same value func testAddenda11String(t testing.TB) { addenda11 := NewAddenda11() diff --git a/addenda12.go b/addenda12.go index 90bd946a7..054cda81a 100644 --- a/addenda12.go +++ b/addenda12.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda12 is an addenda which provides business transaction information for Addenda Type @@ -59,9 +60,9 @@ func (addenda12 *Addenda12) Parse(record string) { // 2-3 Always 12 addenda12.typeCode = record[1:3] // 4-38 - addenda12.OriginatorCityStateProvince = record[3:38] + addenda12.OriginatorCityStateProvince = strings.TrimSpace(record[3:38]) // 39-73 - addenda12.OriginatorCountryPostalCode = record[38:73] + addenda12.OriginatorCountryPostalCode = strings.TrimSpace(record[38:73]) // 74-87 reserved - Leave blank addenda12.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record diff --git a/addenda12_test.go b/addenda12_test.go index f073ae721..4f8556e78 100644 --- a/addenda12_test.go +++ b/addenda12_test.go @@ -25,6 +25,45 @@ func TestMockAddenda12(t *testing.T) { } } +// testAddenda12Parse parses Addenda12 record +func testAddenda12Parse(t testing.TB) { + Addenda12 := NewAddenda12() + line := "712" + "JacobsTown*PA\\ " + "US*19305\\ " + "0000001" + Addenda12.Parse(line) + // walk the Addenda12 struct + if Addenda12.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda12.recordType) + } + if Addenda12.typeCode != "12" { + t.Errorf("expected %v got %v", "12", Addenda12.typeCode) + } + if Addenda12.OriginatorCityStateProvince != "JacobsTown*PA\\" { + t.Errorf("expected %v got %v", "JacobsTown*PA\\", Addenda12.OriginatorCityStateProvince) + } + if Addenda12.OriginatorCountryPostalCode != "US*19305\\" { + t.Errorf("expected: %v got: %v", "US*19305\\", Addenda12.OriginatorCountryPostalCode) + } + if Addenda12.reserved != " " { + t.Errorf("expected: %v got: %v", " ", Addenda12.reserved) + } + if Addenda12.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda12.EntryDetailSequenceNumber) + } +} + +// TestAddenda12Parse tests parsing Addenda12 record +func TestAddenda12Parse(t *testing.T) { + testAddenda12Parse(t) +} + +// BenchmarkAddenda12Parse benchmarks parsing Addenda12 record +func BenchmarkAddenda12Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda12Parse(b) + } +} + // testAddenda12ValidRecordType validates Addenda12 recordType func testAddenda12ValidRecordType(t testing.TB) { addenda12 := mockAddenda12() diff --git a/addenda13.go b/addenda13.go index 8599c9818..3a154f1a6 100644 --- a/addenda13.go +++ b/addenda13.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda13 is an addenda which provides business transaction information for Addenda Type @@ -75,13 +76,13 @@ func (addenda13 *Addenda13) Parse(record string) { // 2-3 Always 13 addenda13.typeCode = record[1:3] // 4-38 ODFIName - addenda13.ODFIName = record[3:38] + addenda13.ODFIName = strings.TrimSpace(record[3:38]) // 39-40 ODFIIDNumberQualifier addenda13.ODFIIDNumberQualifier = record[38:40] // 41-74 ODFIIdentification addenda13.ODFIIdentification = addenda13.parseStringField(record[40:74]) // 75-77 - addenda13.ODFIBranchCountryCode = record[74:77] + addenda13.ODFIBranchCountryCode = strings.TrimSpace(record[74:77]) // 78-87 reserved - Leave blank addenda13.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record diff --git a/addenda13_test.go b/addenda13_test.go index 7acced8c0..f454d391d 100644 --- a/addenda13_test.go +++ b/addenda13_test.go @@ -19,6 +19,51 @@ func mockAddenda13() *Addenda13 { return addenda13 } +// testAddenda13Parse parses Addenda13 record +func testAddenda13Parse(t testing.TB) { + Addenda13 := NewAddenda13() + line := "713Wells Fargo 01121042882 US 0000001" + Addenda13.Parse(line) + // walk the Addenda13 struct + if Addenda13.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda13.recordType) + } + if Addenda13.typeCode != "13" { + t.Errorf("expected %v got %v", "13", Addenda13.typeCode) + } + if Addenda13.ODFIName != "Wells Fargo" { + t.Errorf("expected %v got %v", "Wells Fargo", Addenda13.ODFIName) + } + if Addenda13.ODFIIDNumberQualifier != "01" { + t.Errorf("expected: %v got: %v", "01", Addenda13.ODFIIDNumberQualifier) + } + if Addenda13.ODFIIdentification != "121042882" { + t.Errorf("expected: %v got: %v", "121042882", Addenda13.ODFIIdentification) + } + if Addenda13.ODFIBranchCountryCode != "US" { + t.Errorf("expected: %s got: %s", "US", Addenda13.ODFIBranchCountryCode) + } + if Addenda13.reserved != " " { + t.Errorf("expected: %v got: %v", " ", Addenda13.reserved) + } + if Addenda13.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda13.EntryDetailSequenceNumber) + } +} + +// TestAddenda13Parse tests parsing Addenda13 record +func TestAddenda13Parse(t *testing.T) { + testAddenda13Parse(t) +} + +// BenchmarkAddenda13Parse benchmarks parsing Addenda13 record +func BenchmarkAddenda13Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda13Parse(b) + } +} + // TestMockAddenda13 validates mockAddenda13 func TestMockAddenda13(t *testing.T) { addenda13 := mockAddenda13() @@ -399,13 +444,10 @@ func BenchmarkAddenda13FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } -// ToDo Add Parse test for individual fields - // TestAddenda13String validates that a known parsed Addenda13 record can be return to a string of the same value func testAddenda13String(t testing.TB) { addenda13 := NewAddenda13() var line = "713Wells Fargo 121042882 US 0000001" - addenda13.Parse(line) if addenda13.String() != line { diff --git a/addenda14.go b/addenda14.go index 43e378fcc..1c9ff163f 100644 --- a/addenda14.go +++ b/addenda14.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda14 is an addenda which provides business transaction information for Addenda Type @@ -71,13 +72,13 @@ func (addenda14 *Addenda14) Parse(record string) { // 2-3 Always 14 addenda14.typeCode = record[1:3] // 4-38 RDFIName - addenda14.RDFIName = record[3:38] + addenda14.RDFIName = strings.TrimSpace(record[3:38]) // 39-40 RDFIIDNumberQualifier addenda14.RDFIIDNumberQualifier = record[38:40] // 41-74 RDFIIdentification addenda14.RDFIIdentification = addenda14.parseStringField(record[40:74]) // 75-77 - addenda14.RDFIBranchCountryCode = record[74:77] + addenda14.RDFIBranchCountryCode = strings.TrimSpace(record[74:77]) // 78-87 reserved - Leave blank addenda14.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record diff --git a/addenda14_test.go b/addenda14_test.go index 511fde002..9954290ba 100644 --- a/addenda14_test.go +++ b/addenda14_test.go @@ -27,6 +27,51 @@ func TestMockAddenda14(t *testing.T) { } } +// testAddenda14Parse parses Addenda14 record +func testAddenda14Parse(t testing.TB) { + Addenda14 := NewAddenda14() + line := "714Citadel Bank 01231380104 US 0000001" + Addenda14.Parse(line) + // walk the Addenda14 struct + if Addenda14.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda14.recordType) + } + if Addenda14.typeCode != "14" { + t.Errorf("expected %v got %v", "14", Addenda14.typeCode) + } + if Addenda14.RDFIName != "Citadel Bank" { + t.Errorf("expected %v got %v", "Citadel Bank", Addenda14.RDFIName) + } + if Addenda14.RDFIIDNumberQualifier != "01" { + t.Errorf("expected: %v got: %v", "01", Addenda14.RDFIIDNumberQualifier) + } + if Addenda14.RDFIIdentification != "231380104" { + t.Errorf("expected: %v got: %v", "928383-23938", Addenda14.RDFIIdentification) + } + if Addenda14.RDFIBranchCountryCode != "US" { + t.Errorf("expected: %s got: %s", "US", Addenda14.RDFIBranchCountryCode) + } + if Addenda14.reserved != " " { + t.Errorf("expected: %v got: %v", " ", Addenda14.reserved) + } + if Addenda14.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda14.EntryDetailSequenceNumber) + } +} + +// TestAddenda14Parse tests parsing Addenda14 record +func TestAddenda14Parse(t *testing.T) { + testAddenda14Parse(t) +} + +// BenchmarkAddenda14Parse benchmarks parsing Addenda14 record +func BenchmarkAddenda14Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda14Parse(b) + } +} + // testAddenda14ValidRecordType validates Addenda14 recordType func testAddenda14ValidRecordType(t testing.TB) { addenda14 := mockAddenda14() @@ -399,8 +444,6 @@ func BenchmarkAddenda14FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } -// ToDo Add Parse test for individual fields - // TestAddenda14String validates that a known parsed Addenda14 record can be return to a string of the same value func testAddenda14String(t testing.TB) { addenda14 := NewAddenda14() diff --git a/addenda15.go b/addenda15.go index f32ac2419..0cdd8665f 100644 --- a/addenda15.go +++ b/addenda15.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda15 is an addenda which provides business transaction information for Addenda Type @@ -57,7 +58,7 @@ func (addenda15 *Addenda15) Parse(record string) { // 4-18 addenda15.ReceiverIDNumber = addenda15.parseStringField(record[3:18]) // 19-53 - addenda15.ReceiverStreetAddress = record[18:53] + addenda15.ReceiverStreetAddress = strings.TrimSpace(record[18:53]) // 54-87 reserved - Leave blank addenda15.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record diff --git a/addenda15_test.go b/addenda15_test.go index 07a44c10c..e384885df 100644 --- a/addenda15_test.go +++ b/addenda15_test.go @@ -25,6 +25,45 @@ func TestMockAddenda15(t *testing.T) { } } +// testAddenda15Parse parses Addenda15 record +func testAddenda15Parse(t testing.TB) { + Addenda15 := NewAddenda15() + line := "7159874654932139872121 Front Street 0000001" + Addenda15.Parse(line) + // walk the Addenda15 struct + if Addenda15.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda15.recordType) + } + if Addenda15.typeCode != "15" { + t.Errorf("expected %v got %v", "15", Addenda15.typeCode) + } + if Addenda15.ReceiverIDNumber != "987465493213987" { + t.Errorf("expected %v got %v", "987465493213987", Addenda15.ReceiverIDNumber) + } + if Addenda15.ReceiverStreetAddress != "2121 Front Street" { + t.Errorf("expected: %v got: %v", "2121 Front Street", Addenda15.ReceiverStreetAddress) + } + if Addenda15.reserved != " " { + t.Errorf("expected: %v got: %v", " ", Addenda15.reserved) + } + if Addenda15.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda15.EntryDetailSequenceNumber) + } +} + +// TestAddenda15Parse tests parsing Addenda15 record +func TestAddenda15Parse(t *testing.T) { + testAddenda15Parse(t) +} + +// BenchmarkAddenda15Parse benchmarks parsing Addenda15 record +func BenchmarkAddenda15Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda15Parse(b) + } +} + // testAddenda15ValidRecordType validates Addenda15 recordType func testAddenda15ValidRecordType(t testing.TB) { addenda15 := mockAddenda15() @@ -267,8 +306,6 @@ func BenchmarkAddenda15FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } -// ToDo Add Parse test for individual fields - // TestAddenda15String validates that a known parsed Addenda15 record can be return to a string of the same value func testAddenda15String(t testing.TB) { addenda15 := NewAddenda15() diff --git a/addenda16.go b/addenda16.go index c8ffacd73..2c7c165e7 100644 --- a/addenda16.go +++ b/addenda16.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda16 is an addenda which provides business transaction information for Addenda Type @@ -58,9 +59,9 @@ func (addenda16 *Addenda16) Parse(record string) { // 2-3 Always 16 addenda16.typeCode = record[1:3] // 4-38 ReceiverCityStateProvince - addenda16.ReceiverCityStateProvince = record[3:38] + addenda16.ReceiverCityStateProvince = strings.TrimSpace(record[3:38]) // 39-73 ReceiverCountryPostalCode - addenda16.ReceiverCountryPostalCode = record[38:73] + addenda16.ReceiverCountryPostalCode = strings.TrimSpace(record[38:73]) // 74-87 reserved - Leave blank addenda16.reserved = " " // 88-94 Contains the last seven digits of the number entered in the Trace Number field in the corresponding Entry Detail Record diff --git a/addenda16_test.go b/addenda16_test.go index 0dd375852..9fbbf84b9 100644 --- a/addenda16_test.go +++ b/addenda16_test.go @@ -25,6 +25,45 @@ func TestMockAddenda16(t *testing.T) { } } +// testAddenda16Parse parses Addenda16 record +func testAddenda16Parse(t testing.TB) { + Addenda16 := NewAddenda16() + line := "716LetterTown*AB\\ CA*80014\\ 0000001" + Addenda16.Parse(line) + // walk the Addenda16 struct + if Addenda16.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda16.recordType) + } + if Addenda16.typeCode != "16" { + t.Errorf("expected %v got %v", "16", Addenda16.typeCode) + } + if Addenda16.ReceiverCityStateProvince != "LetterTown*AB\\" { + t.Errorf("expected %v got %v", "LetterTown*AB\\", Addenda16.ReceiverCityStateProvince) + } + if Addenda16.ReceiverCountryPostalCode != "CA*80014\\" { + t.Errorf("expected: %v got: %v", "CA*80014\\", Addenda16.ReceiverCountryPostalCode) + } + if Addenda16.reserved != " " { + t.Errorf("expected: %v got: %v", " ", Addenda16.reserved) + } + if Addenda16.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda16.EntryDetailSequenceNumber) + } +} + +// TestAddenda16Parse tests parsing Addenda16 record +func TestAddenda16Parse(t *testing.T) { + testAddenda16Parse(t) +} + +// BenchmarkAddenda16Parse benchmarks parsing Addenda16 record +func BenchmarkAddenda16Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda16Parse(b) + } +} + // testAddenda16ValidRecordType validates Addenda16 recordType func testAddenda16ValidRecordType(t testing.TB) { addenda16 := mockAddenda16() diff --git a/addenda17_test.go b/addenda17_test.go index 5e4289856..de7002e37 100644 --- a/addenda17_test.go +++ b/addenda17_test.go @@ -27,6 +27,42 @@ func mockAddenda17B() *Addenda17 { return addenda17 } +// testAddenda17Parse parses Addenda17 record +func testAddenda17Parse(t testing.TB) { + Addenda17 := NewAddenda17() + line := "717This is an international payment 00010000001" + Addenda17.Parse(line) + // walk the Addenda17 struct + if Addenda17.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda17.recordType) + } + if Addenda17.typeCode != "17" { + t.Errorf("expected %v got %v", "17", Addenda17.typeCode) + } + if Addenda17.PaymentRelatedInformation != "This is an international payment" { + t.Errorf("expected %v got %v", "This is an international payment", Addenda17.PaymentRelatedInformation) + } + if Addenda17.SequenceNumber != 1 { + t.Errorf("expected: %v got: %v", 1, Addenda17.SequenceNumber) + } + if Addenda17.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda17.EntryDetailSequenceNumber) + } +} + +// TestAddenda17Parse tests parsing Addenda17 record +func TestAddenda17Parse(t *testing.T) { + testAddenda17Parse(t) +} + +// BenchmarkAddenda17Parse benchmarks parsing Addenda17 record +func BenchmarkAddenda17Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda17Parse(b) + } +} + // TestMockAddenda17 validates mockAddenda17 func TestMockAddenda17(t *testing.T) { addenda17 := mockAddenda17() @@ -38,8 +74,6 @@ func TestMockAddenda17(t *testing.T) { } } -// ToDo: Add parse logic - // testAddenda17String validates that a known parsed file can be return to a string of the same value func testAddenda17String(t testing.TB) { addenda17 := NewAddenda17() diff --git a/addenda18.go b/addenda18.go index b0c510b89..bfa597dac 100644 --- a/addenda18.go +++ b/addenda18.go @@ -6,6 +6,7 @@ package ach import ( "fmt" + "strings" ) // Addenda18 is an addenda which provides business transaction information for Addenda Type @@ -72,16 +73,16 @@ func (addenda18 *Addenda18) Parse(record string) { // 2-3 Always 18 addenda18.typeCode = record[1:3] // 4-83 Based on the information entered (04-38) 35 alphanumeric - addenda18.ForeignCorrespondentBankName = record[3:38] + addenda18.ForeignCorrespondentBankName = strings.TrimSpace(record[3:38]) // 39-40 Based on the information entered (39-40) 2 alphanumeric // “01” = National Clearing System // “02” = BIC Code // “03” = IBAN Code addenda18.ForeignCorrespondentBankIDNumberQualifier = record[38:40] // 41-74 Based on the information entered (41-74) 34 alphanumeric - addenda18.ForeignCorrespondentBankIDNumber = record[40:74] + addenda18.ForeignCorrespondentBankIDNumber = strings.TrimSpace(record[40:74]) // 75-77 Based on the information entered (75-77) 3 alphanumeric - addenda18.ForeignCorrespondentBankBranchCountryCode = record[74:77] + addenda18.ForeignCorrespondentBankBranchCountryCode = strings.TrimSpace(record[74:77]) // 78-83 - Blank space addenda18.reserved = " " // 84-87 SequenceNumber is consecutively assigned to each Addenda18 Record following diff --git a/addenda18_test.go b/addenda18_test.go index 2dd65fdb9..e1e59f3b9 100644 --- a/addenda18_test.go +++ b/addenda18_test.go @@ -66,7 +66,7 @@ func mockAddenda18E() *Addenda18 { func mockAddenda18F() *Addenda18 { addenda18 := NewAddenda18() - addenda18.ForeignCorrespondentBankName = "Antarctica" + addenda18.ForeignCorrespondentBankName = "Bank of Antarctica" addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" addenda18.ForeignCorrespondentBankIDNumber = "123456789012345678901" addenda18.ForeignCorrespondentBankBranchCountryCode = "AQ" @@ -98,7 +98,50 @@ func TestMockAddenda18(t *testing.T) { } } -// ToDo: Add parse logic +// testAddenda18Parse parses Addenda18 record +func testAddenda18Parse(t testing.TB) { + Addenda18 := NewAddenda18() + line := "718Bank of Germany 01987987987654654 DE 00010000001" + Addenda18.Parse(line) + // walk the Addenda18 struct + if Addenda18.recordType != "7" { + t.Errorf("expected %v got %v", "7", Addenda18.recordType) + } + if Addenda18.typeCode != "18" { + t.Errorf("expected %v got %v", "18", Addenda18.typeCode) + } + if Addenda18.ForeignCorrespondentBankName != "Bank of Germany" { + t.Errorf("expected %v got %v", "Bank of Germany", Addenda18.ForeignCorrespondentBankName) + } + if Addenda18.ForeignCorrespondentBankIDNumberQualifier != "01" { + t.Errorf("expected: %v got: %v", "01", Addenda18.ForeignCorrespondentBankIDNumberQualifier) + } + if Addenda18.ForeignCorrespondentBankIDNumber != "987987987654654" { + t.Errorf("expected: %v got: %v", "987987987654654", Addenda18.ForeignCorrespondentBankIDNumber) + } + if Addenda18.ForeignCorrespondentBankBranchCountryCode != "DE" { + t.Errorf("expected: %s got: %s", "DE", Addenda18.ForeignCorrespondentBankBranchCountryCode) + } + if Addenda18.reserved != " " { + t.Errorf("expected: %v got: %v", " ", Addenda18.reserved) + } + if Addenda18.EntryDetailSequenceNumber != 0000001 { + t.Errorf("expected: %v got: %v", 0000001, Addenda18.EntryDetailSequenceNumber) + } +} + +// TestAddenda18Parse tests parsing Addenda18 record +func TestAddenda18Parse(t *testing.T) { + testAddenda18Parse(t) +} + +// BenchmarkAddenda18Parse benchmarks parsing Addenda18 record +func BenchmarkAddenda18Parse(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda18Parse(b) + } +} // testAddenda18String validates that a known parsed file can be return to a string of the same value func testAddenda18String(t testing.TB) { diff --git a/iatBatch_test.go b/iatBatch_test.go index e224df458..3aaa03c69 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -1784,7 +1784,7 @@ func testIATBatchAddenda18Count(t testing.TB) { mockBatch.Entries[0].AddIATAddenda(mockAddenda18E()) addenda18F := NewAddenda18() - addenda18F.ForeignCorrespondentBankName = "Russian Federation" + addenda18F.ForeignCorrespondentBankName = "Russian Federation Bank" addenda18F.ForeignCorrespondentBankIDNumberQualifier = "01" addenda18F.ForeignCorrespondentBankIDNumber = "123123456789943874" addenda18F.ForeignCorrespondentBankBranchCountryCode = "RU" From 18ec18d6ecde58904d7b39a4a66a628955bf06d3 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 19 Jul 2018 09:46:19 -0400 Subject: [PATCH 0329/1694] #211 reader_test #211 reader_test --- reader.go | 18 ++--- reader_test.go | 108 +++++++++++++++++++++++++++ test/data/IAT-InvalidAddenda10.ach | 30 ++++++++ test/data/IAT-InvalidBatchHeader.ach | 30 ++++++++ test/data/IAT-InvalidEntryDetail.ach | 30 ++++++++ 5 files changed, 207 insertions(+), 9 deletions(-) create mode 100644 test/data/IAT-InvalidAddenda10.ach create mode 100644 test/data/IAT-InvalidBatchHeader.ach create mode 100644 test/data/IAT-InvalidEntryDetail.ach diff --git a/reader.go b/reader.go index 90713267a..3af43a330 100644 --- a/reader.go +++ b/reader.go @@ -450,21 +450,21 @@ func (r *Reader) switchIATAddenda(entryIndex int) error { addenda10 := NewAddenda10() addenda10.Parse(r.line) if err := addenda10.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].Addenda10 = addenda10 case "11": addenda11 := NewAddenda11() addenda11.Parse(r.line) if err := addenda11.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].Addenda11 = addenda11 case "12": addenda12 := NewAddenda12() addenda12.Parse(r.line) if err := addenda12.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].Addenda12 = addenda12 case "13": @@ -472,42 +472,42 @@ func (r *Reader) switchIATAddenda(entryIndex int) error { addenda13 := NewAddenda13() addenda13.Parse(r.line) if err := addenda13.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].Addenda13 = addenda13 case "14": addenda14 := NewAddenda14() addenda14.Parse(r.line) if err := addenda14.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].Addenda14 = addenda14 case "15": addenda15 := NewAddenda15() addenda15.Parse(r.line) if err := addenda15.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].Addenda15 = addenda15 case "16": addenda16 := NewAddenda16() addenda16.Parse(r.line) if err := addenda16.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].Addenda16 = addenda16 case "17": addenda17 := NewAddenda17() addenda17.Parse(r.line) if err := addenda17.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda17) case "18": addenda18 := NewAddenda18() addenda18.Parse(r.line) if err := addenda18.Validate(); err != nil { - return r.error(err) + return err } r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda18) } diff --git a/reader_test.go b/reader_test.go index fb756b5cf..8ea0b7265 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1238,3 +1238,111 @@ func BenchmarkACHIATAddenda1718(b *testing.B) { testACHIATAddenda1718(b) } } + +// testACHFileIATBatchHeader validates error when reading an invalid IATBatchHeader +func testACHFileIATBatchHeader(t testing.TB) { + f, err := os.Open("./test/data/IAT-InvalidBatchHeader.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHFileIATBatchHeader tests validating error when reading an invalid IATBatchHeader +func TestACHFileIATBatchHeader(t *testing.T) { + testACHFileIATBatchHeader(t) +} + +// BenchmarkACHFileIATBatchHeader benchmarks validating error when reading an invalid IATBatchHeader +func BenchmarkACHFileIATBatchHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileIATBatchHeader(b) + } +} + +// testACHFileIATEntryDetail validates error when reading an invalid IATEntryDetail +func testACHFileIATEntryDetail(t testing.TB) { + f, err := os.Open("./test/data/IAT-InvalidEntryDetail.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.FieldName != "TransactionCode" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHFileIATEntryDetail tests validating error when reading an invalid IATEntryDetail +func TestACHFileIATEntryDetail(t *testing.T) { + testACHFileIATEntryDetail(t) +} + +// BenchmarkACHFileIATEntryDetail benchmarks validating error when reading an invalid IATEntryDetail +func BenchmarkACHFileIATEntryDetail(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileIATEntryDetail(b) + } +} + +// testACHFileIATAddenda10 validates error when reading an invalid IATAddenda10 +func testACHFileIATAddenda10(t testing.TB) { + f, err := os.Open("./test/data/IAT-InvalidAddenda10.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FieldError); ok { + if e.FieldName != "TransactionTypeCode" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHFileIATAddenda10 tests validating error when reading an invalid IATAddenda10 +func TestACHFileIATAddenda10(t *testing.T) { + testACHFileIATAddenda10(t) +} + +// BenchmarkACHFileIATAddenda10 benchmarks validating error when reading an invalid IATAddenda10 +func BenchmarkACHFileIATAddenda10(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileIATAddenda10(b) + } +} diff --git a/test/data/IAT-InvalidAddenda10.ach b/test/data/IAT-InvalidAddenda10.ach new file mode 100644 index 000000000..914f2e9dc --- /dev/null +++ b/test/data/IAT-InvalidAddenda10.ach @@ -0,0 +1,30 @@ +101 987654321 1234567891807130000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +710BIL000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000002000000000000000 231380100000002 +9000002000003000000160024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/data/IAT-InvalidBatchHeader.ach b/test/data/IAT-InvalidBatchHeader.ach new file mode 100644 index 000000000..7b6707817 --- /dev/null +++ b/test/data/IAT-InvalidBatchHeader.ach @@ -0,0 +1,30 @@ +101 987654321 1234567891807130000A094101Federal Reserve Bank My Bank Name +5100 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000002000000000000000 231380100000002 +9000002000003000000160024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/data/IAT-InvalidEntryDetail.ach b/test/data/IAT-InvalidEntryDetail.ach new file mode 100644 index 000000000..0584a0b40 --- /dev/null +++ b/test/data/IAT-InvalidEntryDetail.ach @@ -0,0 +1,30 @@ +101 987654321 1234567891807160000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6901210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +717Transfer of money from one country to another 00020000001 +82200000100012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +82200000090012104288000000002000000000000000 231380100000002 +9000002000003000000190024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file From e21af84dca05dcd27a54a0cdd8ac5b479eb6b9cb Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 19 Jul 2018 09:55:37 -0400 Subject: [PATCH 0330/1694] #211 IAT BatchControl Test #211 IAT BatchControl Test --- reader_test.go | 37 +++++++++++++++++++++++++++ test/data/IAT-InvalidBatchControl.ach | 30 ++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 test/data/IAT-InvalidBatchControl.ach diff --git a/reader_test.go b/reader_test.go index 8ea0b7265..cc4d3e82b 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1346,3 +1346,40 @@ func BenchmarkACHFileIATAddenda10(b *testing.B) { testACHFileIATAddenda10(b) } } + + +// testACHFileIATBC validates error when reading an invalid IAT Batch Control +func testACHFileIATBC(t testing.TB) { + f, err := os.Open("./test/data/IAT-InvalidBatchControl.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*BatchError); ok { + if e.FieldName != "ODFIIdentification" { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHFileIATBC tests validating error when reading an invalid IAT Batch Control +func TestACHFileIATBC(t *testing.T) { + testACHFileIATBC(t) +} + +// BenchmarkACHFileIATBC benchmarks validating error when reading an invalid IAT Batch Control +func BenchmarkACHFileIATBC(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileIATBC(b) + } +} \ No newline at end of file diff --git a/test/data/IAT-InvalidBatchControl.ach b/test/data/IAT-InvalidBatchControl.ach new file mode 100644 index 000000000..9949f7e3e --- /dev/null +++ b/test/data/IAT-InvalidBatchControl.ach @@ -0,0 +1,30 @@ +101 987654321 1234567891807130000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000000000000000100000 231000000000000 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000002000000000000000 231380100000002 +9000002000003000000160024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file From 81c34759d36b9fac097987b99eef69781869419d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 19 Jul 2018 10:44:53 -0400 Subject: [PATCH 0331/1694] #211 reader_test #211 reader_test --- reader_test.go | 39 ++++++++++++++++++++++++++++++-- test/data/IAT-BatchHeaderErr.ach | 31 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 test/data/IAT-BatchHeaderErr.ach diff --git a/reader_test.go b/reader_test.go index cc4d3e82b..c0253152b 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1347,7 +1347,6 @@ func BenchmarkACHFileIATAddenda10(b *testing.B) { } } - // testACHFileIATBC validates error when reading an invalid IAT Batch Control func testACHFileIATBC(t testing.TB) { f, err := os.Open("./test/data/IAT-InvalidBatchControl.ach") @@ -1382,4 +1381,40 @@ func BenchmarkACHFileIATBC(b *testing.B) { for i := 0; i < b.N; i++ { testACHFileIATBC(b) } -} \ No newline at end of file +} + +// testACHFileIATBH validates error when reading an invalid IAT Batch Header +func testACHFileIATBH(t testing.TB) { + f, err := os.Open("./test/data/IAT-BatchHeaderErr.ach") + if err != nil { + t.Errorf("%T: %s", err, err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Read() + + if err != nil { + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FileError); ok { + if e.Msg != msgFileBatchInside { + t.Errorf("%T: %s", e, e) + } + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestACHFileIATBC tests validating error when reading an invalid IAT Batch Header +func TestACHFileIATBH(t *testing.T) { + testACHFileIATBH(t) +} + +// BenchmarkACHFileIATBH benchmarks validating error when reading an invalid IAT Batch Header +func BenchmarkACHFileIATBH(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testACHFileIATBH(b) + } +} diff --git a/test/data/IAT-BatchHeaderErr.ach b/test/data/IAT-BatchHeaderErr.ach new file mode 100644 index 000000000..873bcedeb --- /dev/null +++ b/test/data/IAT-BatchHeaderErr.ach @@ -0,0 +1,31 @@ +101 987654321 1234567891807130000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +82200000080012104288000000002000000000000000 231380100000002 +9000002000003000000160024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file From 78c11247bd1f82006867f24e0acd98487c1fae85 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 19 Jul 2018 11:19:58 -0400 Subject: [PATCH 0332/1694] #211 Removed unnecessary ToDo #211 Removed unnecessary ToDo --- addenda16.go | 1 - addenda16_test.go | 2 -- batchCIE_test.go | 2 -- batchPOS_test.go | 2 -- iatEntryDetail_test.go | 1 - test/data/IAT-InvalidBatchControl.ach | 2 +- 6 files changed, 1 insertion(+), 9 deletions(-) diff --git a/addenda16.go b/addenda16.go index 2c7c165e7..a53728213 100644 --- a/addenda16.go +++ b/addenda16.go @@ -74,7 +74,6 @@ func (addenda16 *Addenda16) String() string { addenda16.recordType, addenda16.typeCode, addenda16.ReceiverCityStateProvinceField(), - // ToDo: Validator for backslash addenda16.ReceiverCountryPostalCodeField(), addenda16.reservedField(), addenda16.EntryDetailSequenceNumberField()) diff --git a/addenda16_test.go b/addenda16_test.go index 9fbbf84b9..41c5abce1 100644 --- a/addenda16_test.go +++ b/addenda16_test.go @@ -332,8 +332,6 @@ func BenchmarkAddenda16FieldInclusionEntryDetailSequenceNumber(b *testing.B) { } } -// ToDo Add Parse test for individual fields - // TestAddenda16String validates that a known parsed Addenda16 record can be return to a string of the same value func testAddenda16String(t testing.TB) { addenda16 := NewAddenda16() diff --git a/batchCIE_test.go b/batchCIE_test.go index e4c8f2c3f..60f6bfd90 100644 --- a/batchCIE_test.go +++ b/batchCIE_test.go @@ -377,8 +377,6 @@ func BenchmarkBatchCIEInvalidAddenda(b *testing.B) { } } -// ToDo: Using a FieldError may need to add a BatchError and use *BatchError - // testBatchCIEInvalidBuild validates an invalid batch build func testBatchCIEInvalidBuild(t testing.TB) { mockBatch := mockBatchCIE() diff --git a/batchPOS_test.go b/batchPOS_test.go index 06428f26c..5ce7bd8bc 100644 --- a/batchPOS_test.go +++ b/batchPOS_test.go @@ -378,8 +378,6 @@ func BenchmarkBatchPOSInvalidAddenda(b *testing.B) { } } -// ToDo: Using a FieldError may need to add a BatchError and use *BatchError - // testBatchPOSInvalidBuild validates an invalid batch build func testBatchPOSInvalidBuild(t testing.TB) { mockBatch := mockBatchPOS() diff --git a/iatEntryDetail_test.go b/iatEntryDetail_test.go index 0b4e89141..b6ceed1b8 100644 --- a/iatEntryDetail_test.go +++ b/iatEntryDetail_test.go @@ -47,7 +47,6 @@ func testMockIATEntryDetail(t testing.TB) { if entry.RDFIIdentification != "12104288" { t.Error("RDFIIdentification dependent default value has changed") } - // ToDo: Add checkDigit test if entry.AddendaRecords != 7 { t.Error("AddendaRecords default dependent value has changed") } diff --git a/test/data/IAT-InvalidBatchControl.ach b/test/data/IAT-InvalidBatchControl.ach index 9949f7e3e..88616111c 100644 --- a/test/data/IAT-InvalidBatchControl.ach +++ b/test/data/IAT-InvalidBatchControl.ach @@ -27,4 +27,4 @@ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 From 3bdda8480008226e12794fb353c6635ed6d4611f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 19 Jul 2018 11:35:31 -0400 Subject: [PATCH 0333/1694] 211 writer_test 211 writer_test --- README.md | 6 ++++ writer_test.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/README.md b/README.md index 32bbcbcaf..a88553acb 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ ACH is under active development but already in production for multiple companies * Addenda Type Code 12 (IAT) * Addenda Type Code 98 (NOC) * Addenda Type Code 99 (Return) + * IAT ## Project Roadmap * Additional SEC codes will be added based on library users needs. Please open an issue with a valid test file. @@ -261,6 +262,11 @@ Which will generate a well formed ACH flat file. 82200000020010200101000000000000000000000799123456789 234567890000002 9000002000001000000040020400202000000017500000000000799 ``` + +### Create an IAT file + + + # Getting Help channel | info diff --git a/writer_test.go b/writer_test.go index 11073d99a..389e067f8 100644 --- a/writer_test.go +++ b/writer_test.go @@ -193,3 +193,101 @@ func BenchmarkIATWrite(b *testing.B) { testIATWrite(b) } } + +// testPPDIATWrite writes an ACH file which writing an ACH file which contains PPD and IAT entries +func testPPDIATWrite(t testing.TB) { + file := NewFile().SetHeader(mockFileHeader()) + + entry := mockEntryDetail() + entry.AddAddenda(mockAddenda05()) + batch := NewBatchPPD(mockBatchPPDHeader()) + batch.SetHeader(mockBatchHeader()) + batch.AddEntry(entry) + batch.Create() + file.AddBatch(batch) + + iatBatch := IATBatch{} + iatBatch.SetHeader(mockIATBatchHeaderFF()) + iatBatch.AddEntry(mockIATEntryDetail()) + iatBatch.Entries[0].Addenda10 = mockAddenda10() + iatBatch.Entries[0].Addenda11 = mockAddenda11() + iatBatch.Entries[0].Addenda12 = mockAddenda12() + iatBatch.Entries[0].Addenda13 = mockAddenda13() + iatBatch.Entries[0].Addenda14 = mockAddenda14() + iatBatch.Entries[0].Addenda15 = mockAddenda15() + iatBatch.Entries[0].Addenda16 = mockAddenda16() + iatBatch.Entries[0].AddIATAddenda(mockAddenda17()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda17B()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18B()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18C()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18D()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18E()) + iatBatch.Create() + file.AddIATBatch(iatBatch) + + iatBatch2 := IATBatch{} + iatBatch2.SetHeader(mockIATBatchHeaderFF()) + iatBatch2.AddEntry(mockIATEntryDetail()) + iatBatch2.GetEntries()[0].TransactionCode = 27 + iatBatch2.GetEntries()[0].Amount = 2000 + iatBatch2.Entries[0].Addenda10 = mockAddenda10() + iatBatch2.Entries[0].Addenda11 = mockAddenda11() + iatBatch2.Entries[0].Addenda12 = mockAddenda12() + iatBatch2.Entries[0].Addenda13 = mockAddenda13() + iatBatch2.Entries[0].Addenda14 = mockAddenda14() + iatBatch2.Entries[0].Addenda15 = mockAddenda15() + iatBatch2.Entries[0].Addenda16 = mockAddenda16() + iatBatch2.Entries[0].AddIATAddenda(mockAddenda17()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda17B()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18B()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18C()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18D()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18E()) + iatBatch2.Create() + file.AddIATBatch(iatBatch2) + + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + b := &bytes.Buffer{} + f := NewWriter(b) + + if err := f.Write(file); err != nil { + t.Errorf("%T: %s", err, err) + } + + r := NewReader(strings.NewReader(b.String())) + _, err := r.Read() + if err != nil { + t.Errorf("%T: %s", err, err) + } + if err = r.File.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + /* // Write records to standard output. Anything io.Writer + w := NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush()*/ +} + +// TestPPDIATWrite tests writing a IAT ACH file +func TestPPDIATWrite(t *testing.T) { + testPPDIATWrite(t) +} + +// BenchmarkPPDIATWrite benchmarks validating writing an ACH file which contain PPD and IAT entries +func BenchmarkPPDIATWrite(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testPPDIATWrite(b) + } +} From 63ad758387ff3d0dcb4b353bb410cde5332bc692 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 20 Jul 2018 13:44:09 -0400 Subject: [PATCH 0334/1694] #211 typo #211 typo --- reader_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reader_test.go b/reader_test.go index c0253152b..4e51a9807 100644 --- a/reader_test.go +++ b/reader_test.go @@ -1406,7 +1406,7 @@ func testACHFileIATBH(t testing.TB) { } } -// TestACHFileIATBC tests validating error when reading an invalid IAT Batch Header +// TestACHFileIATBH tests validating error when reading an invalid IAT Batch Header func TestACHFileIATBH(t *testing.T) { testACHFileIATBH(t) } From 0b9f31ed191a82ce677c1636fae3061d57b155f0 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 20 Jul 2018 14:00:47 -0400 Subject: [PATCH 0335/1694] #211 README updates #211 README --- README.md | 122 +++++++++++++++++++++++++++++++++++++++++++++- addenda17_test.go | 1 - 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a88553acb..ec6e0a10f 100644 --- a/README.md +++ b/README.md @@ -263,9 +263,127 @@ Which will generate a well formed ACH flat file. 9000002000001000000040020400202000000017500000000000799 ``` -### Create an IAT file +### Create an IAT file - +```go + file := NewFile().SetHeader(mockFileHeader()) + iatBatch := IATBatch{} + iatBatch.SetHeader(mockIATBatchHeaderFF()) + iatBatch.AddEntry(mockIATEntryDetail()) + iatBatch.Entries[0].Addenda10 = mockAddenda10() + iatBatch.Entries[0].Addenda11 = mockAddenda11() + iatBatch.Entries[0].Addenda12 = mockAddenda12() + iatBatch.Entries[0].Addenda13 = mockAddenda13() + iatBatch.Entries[0].Addenda14 = mockAddenda14() + iatBatch.Entries[0].Addenda15 = mockAddenda15() + iatBatch.Entries[0].Addenda16 = mockAddenda16() + iatBatch.Entries[0].AddIATAddenda(mockAddenda17()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda17B()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18B()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18C()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18D()) + iatBatch.Entries[0].AddIATAddenda(mockAddenda18E()) + iatBatch.Create() + file.AddIATBatch(iatBatch) + + iatBatch2 := IATBatch{} + iatBatch2.SetHeader(mockIATBatchHeaderFF()) + iatBatch2.AddEntry(mockIATEntryDetail()) + iatBatch2.GetEntries()[0].TransactionCode = 27 + iatBatch2.GetEntries()[0].Amount = 2000 + iatBatch2.Entries[0].Addenda10 = mockAddenda10() + iatBatch2.Entries[0].Addenda11 = mockAddenda11() + iatBatch2.Entries[0].Addenda12 = mockAddenda12() + iatBatch2.Entries[0].Addenda13 = mockAddenda13() + iatBatch2.Entries[0].Addenda14 = mockAddenda14() + iatBatch2.Entries[0].Addenda15 = mockAddenda15() + iatBatch2.Entries[0].Addenda16 = mockAddenda16() + iatBatch2.Entries[0].AddIATAddenda(mockAddenda17()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda17B()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18B()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18C()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18D()) + iatBatch2.Entries[0].AddIATAddenda(mockAddenda18E()) + iatBatch2.Create() + file.AddIATBatch(iatBatch2) + + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + b := &bytes.Buffer{} + f := NewWriter(b) + + if err := f.Write(file); err != nil { + t.Errorf("%T: %s", err, err) + } + + r := NewReader(strings.NewReader(b.String())) + _, err := r.Read() + if err != nil { + t.Errorf("%T: %s", err, err) + } + if err = r.File.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + // Write IAT records to standard output. Anything io.Writer + w := NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +``` + +This will generate a well formed flat IAT ACH file + +```text +101 987654321 1234567891807200000A094101Federal Reserve Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6221210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +717Transfer of money from one country to another 00020000001 +718Bank of Germany 01987987987654654 DE 00010000001 +718Bank of Spain 01987987987123123 ES 00020000001 +718Bank of France 01456456456987987 FR 00030000001 +718Bank of Turkey 0112312345678910 TR 00040000001 +718Bank of United Kingdom 011234567890123456789012345678901234GB 00050000001 +82200000150012104288000000000000000000100000 231380100000001 +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000002 +6271210428820007 0000002000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 US 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +717Transfer of money from one country to another 00020000001 +718Bank of Germany 01987987987654654 DE 00010000001 +718Bank of Spain 01987987987123123 ES 00020000001 +718Bank of France 01456456456987987 FR 00030000001 +718Bank of Turkey 0112312345678910 TR 00040000001 +718Bank of United Kingdom 011234567890123456789012345678901234GB 00050000001 +82200000150012104288000000002000000000000000 231380100000002 +9000002000004000000300024208576000000002000000000100000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +``` # Getting Help diff --git a/addenda17_test.go b/addenda17_test.go index de7002e37..08de4c377 100644 --- a/addenda17_test.go +++ b/addenda17_test.go @@ -14,7 +14,6 @@ func mockAddenda17() *Addenda17 { addenda17.PaymentRelatedInformation = "This is an international payment" addenda17.SequenceNumber = 1 addenda17.EntryDetailSequenceNumber = 0000001 - return addenda17 } From 3e18c3deda2106098eeb46f55f72431dbc7e9bcc Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 24 Jul 2018 15:58:04 -0400 Subject: [PATCH 0336/1694] #211 IAt Writer Test #211 IAT Writer Test --- iatBatch.go | 4 +- iatEntryDetail_test.go | 4 +- reader.go | 2 +- test/ach-iat-write/main.go | 121 ++++++++++++++++++++++++++++++++ test/ach-iat-write/main_test.go | 7 ++ 5 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 test/ach-iat-write/main.go create mode 100644 test/ach-iat-write/main_test.go diff --git a/iatBatch.go b/iatBatch.go index 22eccb13d..7b7723ed5 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -41,8 +41,8 @@ type IATBatch struct { converters } -// IATNewBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported. -func IATNewBatch(bh *IATBatchHeader) IATBatch { +// NewIATBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported. +func NewIATBatch(bh *IATBatchHeader) IATBatch { iatBatch := IATBatch{} iatBatch.SetControl(NewBatchControl()) iatBatch.SetHeader(bh) diff --git a/iatEntryDetail_test.go b/iatEntryDetail_test.go index b6ceed1b8..117b2de8a 100644 --- a/iatEntryDetail_test.go +++ b/iatEntryDetail_test.go @@ -78,7 +78,7 @@ func BenchmarkIATMockEntryDetail(b *testing.B) { func testParseIATEntryDetail(t testing.TB) { var line = "6221210428820007 000010000012345678901234567890123456789012345 1231380100000001" r := NewReader(strings.NewReader(line)) - r.addIATCurrentBatch(IATNewBatch(mockIATBatchHeaderFF())) + r.addIATCurrentBatch(NewIATBatch(mockIATBatchHeaderFF())) r.IATCurrentBatch.SetHeader(mockIATBatchHeaderFF()) r.line = line if err := r.parseIATEntryDetail(); err != nil { @@ -134,7 +134,7 @@ func BenchmarkParseIATEntryDetail(b *testing.B) { func testIATEDString(t testing.TB) { var line = "6221210428820007 000010000012345678901234567890123456789012345 1231380100000001" r := NewReader(strings.NewReader(line)) - r.addIATCurrentBatch(IATNewBatch(mockIATBatchHeaderFF())) + r.addIATCurrentBatch(NewIATBatch(mockIATBatchHeaderFF())) r.IATCurrentBatch.SetHeader(mockIATBatchHeaderFF()) r.line = line if err := r.parseIATEntryDetail(); err != nil { diff --git a/reader.go b/reader.go index 3af43a330..6e12b2807 100644 --- a/reader.go +++ b/reader.go @@ -393,7 +393,7 @@ func (r *Reader) parseIATBatchHeader() error { } // Passing BatchHeader into NewBatchIAT creates a Batcher of IAT SEC code type. - iatBatch := IATNewBatch(bh) + iatBatch := NewIATBatch(bh) r.addIATCurrentBatch(iatBatch) diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go new file mode 100644 index 000000000..47c1a8d71 --- /dev/null +++ b/test/ach-iat-write/main.go @@ -0,0 +1,121 @@ +package main + +import ( + "log" + "os" + "time" + + "github.com/bkmoovio/ach" +) + +func main() { + // Example transfer to write an ACH PPD file to send/credit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + // Set originator bank ODFI and destination Operator for the financial institution + // this is the funding/receiving source of the transfer + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewIATBatchHeader() + bh.ServiceClassCode = 220 + bh.ForeignExchangeIndicator = "FF" + bh.ForeignExchangeReferenceIndicator = 3 + bh.ISODestinationCountryCode = "US" + bh.OriginatorIdentification = "123456789" + bh.StandardEntryClassCode = "IAT" + bh.CompanyEntryDescription = "TRADEPAYMT" + bh.ISOOriginatingCurrencyCode = "CAD" + bh.ISODestinationCurrencyCode = "USD" + bh.ODFIIdentification = "23138010" + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewIATEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("121042882") + entry.AddendaRecords = 007 + entry.DFIAccountNumber = "123456789" + entry.Amount = 100000 // 1000.00 + entry.SetTraceNumber("23138010", 1) + entry.Category = ach.CategoryForward + + //addenda + + addenda10 := ach.NewAddenda10() + addenda10.TransactionTypeCode = "ANN" + addenda10.ForeignPaymentAmount = 100000 + addenda10.ForeignTraceNumber = "928383-23938" + addenda10.Name = "BEK Enterprises" + addenda10.EntryDetailSequenceNumber = 00000001 + entry.Addenda10 = addenda10 + + addenda11 := ach.NewAddenda11() + addenda11.OriginatorName = "BEK Solutions" + addenda11.OriginatorStreetAddress = "15 West Place Street" + addenda11.EntryDetailSequenceNumber = 00000001 + entry.Addenda11 = addenda11 + + addenda12 := ach.NewAddenda12() + addenda12.OriginatorCityStateProvince = "JacobsTown*PA\\" + addenda12.OriginatorCountryPostalCode = "US*19305\\" + addenda12.EntryDetailSequenceNumber = 00000001 + entry.Addenda12 = addenda12 + + addenda13 := ach.NewAddenda13() + addenda13.ODFIName = "Wells Fargo" + addenda13.ODFIIDNumberQualifier = "01" + addenda13.ODFIIdentification = "121042882" + addenda13.ODFIBranchCountryCode = "US" + addenda13.EntryDetailSequenceNumber = 00000001 + entry.Addenda13 = addenda13 + + addenda14 := ach.NewAddenda14() + addenda14.RDFIName = "Citadel Bank" + addenda14.RDFIIDNumberQualifier = "01" + addenda14.RDFIIdentification = "231380104" + addenda14.RDFIBranchCountryCode = "US" + addenda14.EntryDetailSequenceNumber = 00000001 + entry.Addenda14 = addenda14 + + addenda15 := ach.NewAddenda15() + addenda15.ReceiverIDNumber = "987465493213987" + addenda15.ReceiverStreetAddress = "2121 Front Street" + addenda15.EntryDetailSequenceNumber = 00000001 + entry.Addenda15 = addenda15 + + addenda16 := ach.NewAddenda16() + addenda16.ReceiverCityStateProvince = "LetterTown*AB\\" + addenda16.ReceiverCountryPostalCode = "CA*80014\\" + addenda16.EntryDetailSequenceNumber = 00000001 + entry.Addenda16 = addenda16 + + + // build the batch + batch := ach.NewIATBatch(bh) + batch.AddEntry(entry) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddIATBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} + diff --git a/test/ach-iat-write/main_test.go b/test/ach-iat-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-iat-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From 9c592fb7ae6421e5f0053970f1b6e1079003b90b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 24 Jul 2018 15:58:53 -0400 Subject: [PATCH 0337/1694] #211 Writer Test #211 Writer Test --- test/ach-iat-write/main.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go index 47c1a8d71..3a5124020 100644 --- a/test/ach-iat-write/main.go +++ b/test/ach-iat-write/main.go @@ -95,7 +95,6 @@ func main() { addenda16.EntryDetailSequenceNumber = 00000001 entry.Addenda16 = addenda16 - // build the batch batch := ach.NewIATBatch(bh) batch.AddEntry(entry) @@ -118,4 +117,3 @@ func main() { } w.Flush() } - From 1aa53c9aa1141de173c7a7415448b2e923af59a9 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 26 Jul 2018 11:14:41 -0400 Subject: [PATCH 0338/1694] #211-Test-Package Read IAT File --- test/ach-iat-read/iat-ach.ach | 20 ++++++++++++++++ test/ach-iat-read/main.go | 43 ++++++++++++++++++++++++++++++++++ test/ach-iat-read/main_test.go | 7 ++++++ test/ach-iat-write/main.go | 19 +++++++++++++-- 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 test/ach-iat-read/iat-ach.ach create mode 100644 test/ach-iat-read/main.go create mode 100644 test/ach-iat-read/main_test.go diff --git a/test/ach-iat-read/iat-ach.ach b/test/ach-iat-read/iat-ach.ach new file mode 100644 index 000000000..b7e7d1632 --- /dev/null +++ b/test/ach-iat-read/iat-ach.ach @@ -0,0 +1,20 @@ +101 231380104 1210428821807260000A094101Bank My Bank Name +5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 +6271210428820007 0000100000123456789 1231380100000001 +710ANN000000000000100000928383-23938 BEK Enterprises 0000001 +711BEK Solutions 15 West Place Street 0000001 +712JacobsTown*PA\ US*19305\ 0000001 +713Wells Fargo 01121042882 US 0000001 +714Citadel Bank 01231380104 CA 0000001 +7159874654932139872121 Front Street 0000001 +716LetterTown*AB\ CA*80014\ 0000001 +717This is an international payment 00010000001 +718Bank of France 01456456456987987 FR 00010000001 +82200000100012104288000000100000000000000000 231380100000001 +9000001000002000000100012104288000000100000000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-iat-read/main.go b/test/ach-iat-read/main.go new file mode 100644 index 000000000..8dabd9608 --- /dev/null +++ b/test/ach-iat-read/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "github.com/moov-io/ach" + "log" + "os" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("iat-ach.ach") + if err != nil { + log.Fatal(err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.IATBatches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("Entry: %v \n", achFile.IATBatches[0].GetEntries()[0]) + fmt.Printf("Addenda Record Indicator: %v \n", achFile.IATBatches[0].GetEntries()[0].AddendaRecordIndicator) + fmt.Printf("Addenda10: %v \n", achFile.IATBatches[0].GetEntries()[0].Addenda10) + fmt.Printf("Addenda11: %v \n", achFile.IATBatches[0].GetEntries()[0].Addenda11) + fmt.Printf("Addenda12: %v \n", achFile.IATBatches[0].GetEntries()[0].Addenda12) + fmt.Printf("Addenda13: %v \n", achFile.IATBatches[0].GetEntries()[0].Addenda13) + fmt.Printf("Addenda14: %v \n", achFile.IATBatches[0].GetEntries()[0].Addenda14) + fmt.Printf("Addenda15: %v \n", achFile.IATBatches[0].GetEntries()[0].Addenda15) + fmt.Printf("Addenda16: %v \n", achFile.IATBatches[0].GetEntries()[0].Addenda16) + fmt.Printf("Addenda17: %v \n", achFile.IATBatches[0].GetEntries()[0].Addendum[0].String()) + fmt.Printf("Addenda18: %v \n", achFile.IATBatches[0].GetEntries()[0].Addendum[1].String()) +} diff --git a/test/ach-iat-read/main_test.go b/test/ach-iat-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-iat-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go index 3a5124020..050092e4a 100644 --- a/test/ach-iat-write/main.go +++ b/test/ach-iat-write/main.go @@ -37,7 +37,7 @@ func main() { // Identifies the receivers account information // can be multiple entry's per batch entry := ach.NewIATEntryDetail() - entry.TransactionCode = 22 + entry.TransactionCode = 27 entry.SetRDFI("121042882") entry.AddendaRecords = 007 entry.DFIAccountNumber = "123456789" @@ -79,7 +79,7 @@ func main() { addenda14.RDFIName = "Citadel Bank" addenda14.RDFIIDNumberQualifier = "01" addenda14.RDFIIdentification = "231380104" - addenda14.RDFIBranchCountryCode = "US" + addenda14.RDFIBranchCountryCode = "CA" addenda14.EntryDetailSequenceNumber = 00000001 entry.Addenda14 = addenda14 @@ -95,6 +95,21 @@ func main() { addenda16.EntryDetailSequenceNumber = 00000001 entry.Addenda16 = addenda16 + addenda17 := ach.NewAddenda17() + addenda17.PaymentRelatedInformation = "This is an international payment" + addenda17.SequenceNumber = 1 + addenda17.EntryDetailSequenceNumber = 0000001 + entry.AddIATAddenda(addenda17) + + addenda18 := ach.NewAddenda18() + addenda18.ForeignCorrespondentBankName = "Bank of France" + addenda18.ForeignCorrespondentBankIDNumberQualifier = "01" + addenda18.ForeignCorrespondentBankIDNumber = "456456456987987" + addenda18.ForeignCorrespondentBankBranchCountryCode = "FR" + addenda18.SequenceNumber = 3 + addenda18.EntryDetailSequenceNumber = 0000001 + entry.AddIATAddenda(addenda18) + // build the batch batch := ach.NewIATBatch(bh) batch.AddEntry(entry) From e8189353981f20721a5911d0e3747f1f5f91967c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 26 Jul 2018 11:27:58 -0400 Subject: [PATCH 0339/1694] #211 IAT write test #211 IAT write test --- test/ach-iat-write/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go index 050092e4a..90e672ac0 100644 --- a/test/ach-iat-write/main.go +++ b/test/ach-iat-write/main.go @@ -4,8 +4,8 @@ import ( "log" "os" "time" - - "github.com/bkmoovio/ach" + + "github.com/moov-io/ach" ) func main() { From 0a26b67a58ac9a7e290d9c9d433ac974b2b87e59 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 26 Jul 2018 11:45:24 -0400 Subject: [PATCH 0340/1694] #211 changed IATNewBatch to newIATBatch #211 changed IATNewBatch to NewIATBatch unitl the code is pushed --- test/ach-iat-write/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go index 90e672ac0..db2f0a4f7 100644 --- a/test/ach-iat-write/main.go +++ b/test/ach-iat-write/main.go @@ -4,7 +4,7 @@ import ( "log" "os" "time" - + "github.com/moov-io/ach" ) @@ -111,7 +111,7 @@ func main() { entry.AddIATAddenda(addenda18) // build the batch - batch := ach.NewIATBatch(bh) + batch := ach.IATNewBatch(bh) batch.AddEntry(entry) if err := batch.Create(); err != nil { log.Fatalf("Unexpected error building batch: %s\n", err) From ed486f632a56f4b3b47cd253e0129b08a24194a7 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 26 Jul 2018 11:54:06 -0400 Subject: [PATCH 0341/1694] #211 IAT Write test #211 IAT Write test --- test/ach-iat-read/iat-ach.ach | 6 +++--- test/ach-iat-write/main.go | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/test/ach-iat-read/iat-ach.ach b/test/ach-iat-read/iat-ach.ach index b7e7d1632..838ccda73 100644 --- a/test/ach-iat-read/iat-ach.ach +++ b/test/ach-iat-read/iat-ach.ach @@ -1,11 +1,11 @@ -101 231380104 1210428821807260000A094101Bank My Bank Name +101 121042882 2313801041807260000A094101Bank My Bank Name 5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 6271210428820007 0000100000123456789 1231380100000001 710ANN000000000000100000928383-23938 BEK Enterprises 0000001 711BEK Solutions 15 West Place Street 0000001 712JacobsTown*PA\ US*19305\ 0000001 -713Wells Fargo 01121042882 US 0000001 -714Citadel Bank 01231380104 CA 0000001 +713Wells Fargo 01231380104 US 0000001 +714Citadel Bank 01121042882 CA 0000001 7159874654932139872121 Front Street 0000001 716LetterTown*AB\ CA*80014\ 0000001 717This is an international payment 00010000001 diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go index db2f0a4f7..962e7bfa3 100644 --- a/test/ach-iat-write/main.go +++ b/test/ach-iat-write/main.go @@ -1,22 +1,21 @@ package main import ( + "time" + "github.com/bkmoovio/ach" "log" "os" - "time" - - "github.com/moov-io/ach" ) func main() { - // Example transfer to write an ACH PPD file to send/credit a external institutions account + // Example transfer to write an ACH IAT file to debit a external institutions account // Important: All financial institutions are different and will require registration and exact field values. // Set originator bank ODFI and destination Operator for the financial institution // this is the funding/receiving source of the transfer fh := ach.NewFileHeader() - fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent - fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.ImmediateDestination = "121042882" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "231380104" // Routing Number of the ACH Operator or sending point that is sending the file fh.FileCreationDate = time.Now() // Today's Date fh.ImmediateDestinationName = "Bank" fh.ImmediateOriginName = "My Bank Name" @@ -70,7 +69,7 @@ func main() { addenda13 := ach.NewAddenda13() addenda13.ODFIName = "Wells Fargo" addenda13.ODFIIDNumberQualifier = "01" - addenda13.ODFIIdentification = "121042882" + addenda13.ODFIIdentification = "231380104" addenda13.ODFIBranchCountryCode = "US" addenda13.EntryDetailSequenceNumber = 00000001 entry.Addenda13 = addenda13 @@ -78,7 +77,7 @@ func main() { addenda14 := ach.NewAddenda14() addenda14.RDFIName = "Citadel Bank" addenda14.RDFIIDNumberQualifier = "01" - addenda14.RDFIIdentification = "231380104" + addenda14.RDFIIdentification = "121042882" addenda14.RDFIBranchCountryCode = "CA" addenda14.EntryDetailSequenceNumber = 00000001 entry.Addenda14 = addenda14 From dff89f78fe246cdac278baf51e6bed24fef88d09 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 26 Jul 2018 11:57:03 -0400 Subject: [PATCH 0342/1694] #211 moov-io #211 moov-io --- test/ach-iat-write/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go index 962e7bfa3..a5463d1f4 100644 --- a/test/ach-iat-write/main.go +++ b/test/ach-iat-write/main.go @@ -2,7 +2,7 @@ package main import ( "time" - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "log" "os" ) From 15a67b21ff8c6c9999935cf8150a3f2599bb09b0 Mon Sep 17 00:00:00 2001 From: Brooke Kline Date: Thu, 26 Jul 2018 12:06:20 -0400 Subject: [PATCH 0343/1694] Update main.go --- test/ach-iat-write/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ach-iat-write/main.go b/test/ach-iat-write/main.go index a5463d1f4..906082012 100644 --- a/test/ach-iat-write/main.go +++ b/test/ach-iat-write/main.go @@ -110,7 +110,7 @@ func main() { entry.AddIATAddenda(addenda18) // build the batch - batch := ach.IATNewBatch(bh) + batch := ach.NewIATBatch(bh) batch.AddEntry(entry) if err := batch.Create(); err != nil { log.Fatalf("Unexpected error building batch: %s\n", err) From d91fe22b24dedc3cc073fda12d3814b79c30727f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 27 Jul 2018 14:24:52 -0400 Subject: [PATCH 0344/1694] #212 CTX Support #212 CTX Support --- README.md | 1 + addenda05_test.go | 1 + batch.go | 10 +- batchCTX.go | 94 ++++++++++ batchCTX_test.go | 446 ++++++++++++++++++++++++++++++++++++++++++++++ batch_test.go | 33 ++++ entryDetail.go | 29 ++- file.go | 3 +- iatEntryDetail.go | 2 +- 9 files changed, 610 insertions(+), 9 deletions(-) create mode 100644 batchCTX.go create mode 100644 batchCTX_test.go diff --git a/README.md b/README.md index ec6e0a10f..021d859f7 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ ACH is under active development but already in production for multiple companies * CCD (Corporate credit or debit) * CIE (Customer-Initiated Entry) * COR (Automated Notification of Change(NOC)) + * CTX (Corporate Trade Exchange) * POP (Point of Purchase) * POS (Point of Sale) * PPD (Prearranged payment and deposits) diff --git a/addenda05_test.go b/addenda05_test.go index 2b9cbd657..7e489c401 100644 --- a/addenda05_test.go +++ b/addenda05_test.go @@ -12,6 +12,7 @@ import ( func mockAddenda05() *Addenda05 { addenda05 := NewAddenda05() addenda05.SequenceNumber = 1 + addenda05.PaymentRelatedInformation = "This is an Addenda05" addenda05.EntryDetailSequenceNumber = 0000001 return addenda05 diff --git a/batch.go b/batch.go index 274b2255a..6e3b3bf55 100644 --- a/batch.go +++ b/batch.go @@ -37,10 +37,11 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { return NewBatchCIE(bh), nil case "COR": return NewBatchCOR(bh), nil + case "CTX": + return NewBatchCTX(bh), nil case "IAT": - //ToDo: Update message to tell user to use iatBatch.go - msg := fmt.Sprintf(msgFileNoneSEC, bh.StandardEntryClassCode) - return nil, &FileError{FieldName: "StandardEntryClassCode", Msg: msg} + msg := fmt.Sprintf(msgFileIATSEC, bh.StandardEntryClassCode) + return nil, &FileError{FieldName: "StandardEntryClassCode", Value: bh.StandardEntryClassCode, Msg: msg} case "POP": return NewBatchPOP(bh), nil case "POS": @@ -58,7 +59,7 @@ func NewBatch(bh *BatchHeader) (Batcher, error) { default: } msg := fmt.Sprintf(msgFileNoneSEC, bh.StandardEntryClassCode) - return nil, &FileError{FieldName: "StandardEntryClassCode", Msg: msg} + return nil, &FileError{FieldName: "StandardEntryClassCode", Value: bh.StandardEntryClassCode, Msg: msg} } // verify checks basic valid NACHA batch rules. Assumes properly parsed records. This does not mean it is a valid batch as validity is tied to each batch type @@ -140,6 +141,7 @@ func (batch *batch) build() error { seq := 1 for i, entry := range batch.Entries { entryCount = entryCount + 1 + len(entry.Addendum) + currentTraceNumberODFI, err := strconv.Atoi(entry.TraceNumberField()[:8]) if err != nil { return err diff --git a/batchCTX.go b/batchCTX.go new file mode 100644 index 000000000..4e5116971 --- /dev/null +++ b/batchCTX.go @@ -0,0 +1,94 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "fmt" + "strconv" +) + +// BatchCTX holds the BatchHeader and BatchControl and all EntryDetail for CTX Entries. +// +// The Corporate Trade Exchange (CTX) application provides the ability to collect and disburse +// funds and information between companies. Generally it is used by businesses paying one another +// for goods or services. These payments replace checks with an electronic process of debiting and +// crediting invoices between the financial institutions of participating companies. +type BatchCTX struct { + batch +} + +var ( + msgBatchCTXAddenda = "9999 is the maximum addenda records for SEC code CTX" + msgBatchCTXAddendaCount = "%v entry detail addenda records not equal to addendum %v" + msgBatchCTXAddendaType = "%T found where Addenda05 is required for SEC code CTX" +) + +// NewBatchCTX returns a *BatchCTX +func NewBatchCTX(bh *BatchHeader) *BatchCTX { + batch := new(BatchCTX) + batch.SetControl(NewBatchControl()) + batch.SetHeader(bh) + return batch +} + +// Validate checks valid NACHA batch rules. Assumes properly parsed records. +func (batch *BatchCTX) Validate() error { + // basic verification of the batch before we validate specific rules. + if err := batch.verify(); err != nil { + return err + } + // Add configuration based validation for this type. + + // Add type specific validation. + + if batch.Header.StandardEntryClassCode != "CTX" { + msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "CTX") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg} + } + + for _, entry := range batch.Entries { + + // Addenda validations - CTX Addenda must be Addenda05 + + // A maximum of 9999 addenda records for CTX entry details + if len(entry.Addendum) > 9999 { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchCTXAddenda} + } + + addendaRecords, _ := strconv.Atoi(entry.CTXAddendaRecordsField()) + if len(entry.Addendum) != addendaRecords { + msg := fmt.Sprintf(msgBatchCTXAddendaCount, addendaRecords, len(entry.Addendum)) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} + } + + if len(entry.Addendum) > 0 { + for i := range entry.Addendum { + addenda05, ok := entry.Addendum[i].(*Addenda05) + if !ok { + msg := fmt.Sprintf(msgBatchCTXAddendaType, entry.Addendum[i]) + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} + } + if err := addenda05.Validate(); err != nil { + // convert the field error in to a batch error for a consistent api + if e, ok := err.(*FieldError); ok { + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg} + } + } + } + } + } + return nil +} + +// Create takes Batch Header and Entries and builds a valid batch +func (batch *BatchCTX) Create() error { + // generates sequence numbers and batch control + if err := batch.build(); err != nil { + return err + } + // Additional steps specific to batch type + // ... + return batch.Validate() +} diff --git a/batchCTX_test.go b/batchCTX_test.go new file mode 100644 index 000000000..6bf7a1422 --- /dev/null +++ b/batchCTX_test.go @@ -0,0 +1,446 @@ +// Copyright 2018 The ACH Authors +// Use of this source code is governed by an Apache License +// license that can be found in the LICENSE file. + +package ach + +import ( + "log" + "testing" +) + +// mockBatchCTXHeader creates a BatchCTX BatchHeader +func mockBatchCTXHeader() *BatchHeader { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "CTX" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH CTX" + bh.ODFIIdentification = "12104288" + return bh +} + +// mockCTXEntryDetail creates a BatchCTX EntryDetail +func mockCTXEntryDetail() *EntryDetail { + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetCTXAddendaRecords(1) + entry.SetCTXReceivingCompany("Receiver Company") + entry.SetTraceNumber(mockBatchCTXHeader().ODFIIdentification, 1) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + return entry +} + +// mockBatchCTX creates a BatchCTX +func mockBatchCTX() *BatchCTX { + mockBatch := NewBatchCTX(mockBatchCTXHeader()) + mockBatch.AddEntry(mockCTXEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + if err := mockBatch.Create(); err != nil { + log.Fatal(err) + } + return mockBatch +} + +// testBatchCTXHeader creates a BatchCTX BatchHeader +func testBatchCTXHeader(t testing.TB) { + batch, _ := NewBatch(mockBatchCTXHeader()) + err, ok := batch.(*BatchCTX) + if !ok { + t.Errorf("Expecting BatchCTX got %T", err) + } +} + +// TestBatchCTXHeader tests validating BatchCTX BatchHeader +func TestBatchCTXHeader(t *testing.T) { + testBatchCTXHeader(t) +} + +// BenchmarkBatchCTXHeader benchmarks validating BatchCTX BatchHeader +func BenchmarkBatchCTXHeader(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXHeader(b) + } +} + +// testBatchCTXCreate validates BatchCTX create +func testBatchCTXCreate(t testing.TB) { + mockBatch := mockBatchCTX() + if err := mockBatch.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } +} + +// TestBatchCTXCreate tests validating BatchCTX create +func TestBatchCTXCreate(t *testing.T) { + testBatchCTXCreate(t) +} + +// BenchmarkBatchCTXCreate benchmarks validating BatchCTX create +func BenchmarkBatchCTXCreate(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXCreate(b) + } +} + +// testBatchCTXStandardEntryClassCode validates BatchCTX create for an invalid StandardEntryClassCode +func testBatchCTXStandardEntryClassCode(t testing.TB) { + mockBatch := mockBatchCTX() + mockBatch.Header.StandardEntryClassCode = "WEB" + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXStandardEntryClassCode tests validating BatchCTX create for an invalid StandardEntryClassCode +func TestBatchCTXStandardEntryClassCode(t *testing.T) { + testBatchCTXStandardEntryClassCode(t) +} + +// BenchmarkBatchCTXStandardEntryClassCode benchmarks validating BatchCTX create for an invalid StandardEntryClassCode +func BenchmarkBatchCTXStandardEntryClassCode(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXStandardEntryClassCode(b) + } +} + +// testBatchCTXServiceClassCodeEquality validates service class code equality +func testBatchCTXServiceClassCodeEquality(t testing.TB) { + mockBatch := mockBatchCTX() + mockBatch.GetControl().ServiceClassCode = 200 + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "ServiceClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXServiceClassCodeEquality tests validating service class code equality +func TestBatchCTXServiceClassCodeEquality(t *testing.T) { + testBatchCTXServiceClassCodeEquality(t) +} + +// BenchmarkBatchCTXServiceClassCodeEquality benchmarks validating service class code equality +func BenchmarkBatchCTXServiceClassCodeEquality(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXServiceClassCodeEquality(b) + } +} + +// testBatchCTXAddendaCount validates BatchCTX Addendum count of 2 +func testBatchCTXAddendaCount(t testing.TB) { + mockBatch := mockBatchCTX() + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + mockBatch.Create() + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXAddendaCount tests validating BatchCTX Addendum count of 2 +func TestBatchCTXAddendaCount(t *testing.T) { + testBatchCTXAddendaCount(t) +} + +// BenchmarkBatchCTXAddendaCount benchmarks validating BatchCTX Addendum count of 2 +func BenchmarkBatchCTXAddendaCount(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXAddendaCount(b) + } +} + +// testBatchCTXAddendaCountZero validates Addendum count of 0 +func testBatchCTXAddendaCountZero(t testing.TB) { + mockBatch := NewBatchCTX(mockBatchCTXHeader()) + mockBatch.AddEntry(mockCTXEntryDetail()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXAddendaCountZero tests validating Addendum count of 0 +func TestBatchCTXAddendaCountZero(t *testing.T) { + testBatchCTXAddendaCountZero(t) +} + +// BenchmarkBatchCTXAddendaCountZero benchmarks validating Addendum count of 0 +func BenchmarkBatchCTXAddendaCountZero(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXAddendaCountZero(b) + } +} + +// testBatchCTXInvalidAddendum validates Addendum must be Addenda05 +func testBatchCTXInvalidAddendum(t testing.TB) { + mockBatch := NewBatchCTX(mockBatchCTXHeader()) + mockBatch.AddEntry(mockCTXEntryDetail()) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda02()) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXInvalidAddendum tests validating Addendum must be Addenda05 +func TestBatchCTXInvalidAddendum(t *testing.T) { + testBatchCTXInvalidAddendum(t) +} + +// BenchmarkBatchCTXInvalidAddendum benchmarks validating Addendum must be Addenda05 +func BenchmarkBatchCTXInvalidAddendum(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXInvalidAddendum(b) + } +} + +// testBatchCTXInvalidAddenda validates Addendum must be Addenda05 with record type 7 +func testBatchCTXInvalidAddenda(t testing.TB) { + mockBatch := NewBatchCTX(mockBatchCTXHeader()) + mockBatch.AddEntry(mockCTXEntryDetail()) + addenda05 := mockAddenda05() + addenda05.recordType = "63" + mockBatch.GetEntries()[0].AddAddenda(addenda05) + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXInvalidAddenda tests validating Addendum must be Addenda05 with record type 7 +func TestBatchCTXInvalidAddenda(t *testing.T) { + testBatchCTXInvalidAddenda(t) +} + +// BenchmarkBatchCTXInvalidAddenda benchmarks validating Addendum must be Addenda05 with record type 7 +func BenchmarkBatchCTXInvalidAddenda(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXInvalidAddenda(b) + } +} + +// testBatchCTXInvalidBuild validates an invalid batch build +func testBatchCTXInvalidBuild(t testing.TB) { + mockBatch := mockBatchCTX() + mockBatch.GetHeader().recordType = "3" + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "recordType" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXInvalidBuild tests validating an invalid batch build +func TestBatchCTXInvalidBuild(t *testing.T) { + testBatchCTXInvalidBuild(t) +} + +// BenchmarkBatchCTXInvalidBuild benchmarks validating an invalid batch build +func BenchmarkBatchCTXInvalidBuild(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXInvalidBuild(b) + } +} + +// testBatchCTXAddenda10000 validates error for 10000 Addenda +func testBatchCTXAddenda10000(t testing.TB) { + + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "CTX" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH CTX" + bh.ODFIIdentification = "12104288" + + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetCTXAddendaRecords(9999) + entry.SetCTXReceivingCompany("Receiver Company") + entry.SetTraceNumber(mockBatchCTXHeader().ODFIIdentification, 1) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + + mockBatch := NewBatchCTX(bh) + mockBatch.AddEntry(entry) + + for i := 0; i < 10000; i++ { + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + } + + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXAddenda10000 tests validating error for 10000 Addenda +func TestBatchCTXAddenda10000(t *testing.T) { + testBatchCTXAddenda10000(t) +} + +// BenchmarkBatchCTXAddenda10000 benchmarks validating error for 10000 Addenda +func BenchmarkBatchCTXAddenda10000(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXAddenda10000(b) + } +} + +// testBatchCTXAddendaRecords validates error for AddendaRecords not equal to addendum +func testBatchCTXAddendaRecords(t testing.TB) { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "CTX" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH CTX" + bh.ODFIIdentification = "12104288" + + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetCTXAddendaRecords(500) + entry.SetCTXReceivingCompany("Receiver Company") + entry.SetTraceNumber(mockBatchCTXHeader().ODFIIdentification, 1) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + + mockBatch := NewBatchCTX(bh) + mockBatch.AddEntry(entry) + + for i := 0; i < 565; i++ { + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + } + + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXAddendaRecords tests validating error for AddendaRecords not equal to addendum +func TestBatchCTXAddendaRecords(t *testing.T) { + testBatchCTXAddendaRecords(t) +} + +// BenchmarkBatchAddendaRecords benchmarks validating error for AddendaRecords not equal to addendum +func BenchmarkBatchCTXAddendaRecords(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXAddendaRecords(b) + } +} + +// testBatchCTXReceivingCompany validates CTXReceivingCompany +func testBatchCTXReceivingCompany(t testing.TB) { + mockBatch := mockBatchCTX() + //mockBatch.GetEntries()[0].SetCTXReceivingCompany("Receiver") + + if mockBatch.GetEntries()[0].CTXReceivingCompanyField() != "Receiver Company" { + t.Errorf("expected %v got %v", "Receiver Company", mockBatch.GetEntries()[0].CTXReceivingCompanyField()) + } +} + +// TestBatchCTXReceivingCompany tests validating CTXReceivingCompany +func TestBatchCTXReceivingCompany(t *testing.T) { + testBatchCTXReceivingCompany(t) +} + +// BenchmarkBatchCTXReceivingCompany benchmarks validating CTXReceivingCompany +func BenchmarkBatchCTXReceivingCompany(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXReceivingCompany(b) + } +} + +// testBatchCTXReserved validates CTXReservedField +func testBatchCTXReserved(t testing.TB) { + mockBatch := mockBatchCTX() + + if mockBatch.GetEntries()[0].CTXReservedField() != " " { + t.Errorf("expected %v got %v", " ", mockBatch.GetEntries()[0].CTXReservedField()) + } +} + +// TestBatchCTXReserved tests validating CTXReservedField +func TestBatchCTXReserved(t *testing.T) { + testBatchCTXReserved(t) +} + +// BenchmarkBatchCTXReserved benchmarks validating CTXReservedField +func BenchmarkBatchCTXReserved(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXReserved(b) + } +} diff --git a/batch_test.go b/batch_test.go index 24ddeafd6..35be03fd0 100644 --- a/batch_test.go +++ b/batch_test.go @@ -395,6 +395,7 @@ func BenchmarkBatchAddendaTraceNumber(b *testing.B) { testBatchAddendaTraceNumber(b) } } + func testNewBatchDefault(t testing.TB) { _, err := NewBatch(mockBatchInvalidSECHeader()) @@ -597,3 +598,35 @@ func BenchmarkBatchControl(b *testing.B) { testBatchControl(b) } } + +func testIATBatch(t testing.TB) { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "IAT" + bh.CompanyName = "ACME Corporation" + bh.CompanyIdentification = "123456789" + bh.CompanyEntryDescription = "PAYROLL" + bh.EffectiveEntryDate = time.Now() + bh.ODFIIdentification = "123456789" + + _, err := NewBatch(bh) + + if e, ok := err.(*FileError); ok { + if e.FieldName != "StandardEntryClassCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } +} + +func TestIATBatch(t *testing.T) { + testIATBatch(t) +} + +func BenchmarkIATBatch(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatch(b) + } +} diff --git a/entryDetail.go b/entryDetail.go index 0594650b2..00225b265 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -300,21 +300,18 @@ func (ed *EntryDetail) SetPOPTerminalState(s string) { // POPCheckSerialNumberField is used in POP, characters 1-9 of underlying BatchPOP // CheckSerialNumber / IdentificationNumber func (ed *EntryDetail) POPCheckSerialNumberField() string { - //return ed.alphaField(ed.IdentificationNumber, 9) return ed.parseStringField(ed.IdentificationNumber[0:9]) } // POPTerminalCityField is used in POP, characters 10-13 of underlying BatchPOP // CheckSerialNumber / IdentificationNumber func (ed *EntryDetail) POPTerminalCityField() string { - //return ed.alphaField(ed.IdentificationNumber, 9) return ed.parseStringField(ed.IdentificationNumber[9:13]) } // POPTerminalStateField is used in POP, characters 14-15 of underlying BatchPOP // CheckSerialNumber / IdentificationNumber func (ed *EntryDetail) POPTerminalStateField() string { - //return ed.alphaField(ed.IdentificationNumber, 9) return ed.parseStringField(ed.IdentificationNumber[13:15]) } @@ -369,6 +366,32 @@ func (ed *EntryDetail) SetReceivingCompany(s string) { ed.IndividualName = s } +// SetCTXAddendaRecords setter for CTX AddendaRecords characters 1-4 of underlying IndividualName +func (ed *EntryDetail) SetCTXAddendaRecords(i int) { + ed.IndividualName = ed.numericField(i, 4) +} + +// SetCTXReceivingCompany setter for CTX ReceivingCompany characters 5-20 underlying IndividualName +// Position 21-22 of underlying Individual Name are reserved blank space for CTX " " +func (ed *EntryDetail) SetCTXReceivingCompany(s string) { + ed.IndividualName = ed.IndividualName + ed.alphaField(s, 16) + " " +} + +// CTXAddendaRecordsField is used in CTX files, characters 1-4 of underlying IndividualName field +func (ed *EntryDetail) CTXAddendaRecordsField() string { + return ed.parseStringField(ed.IndividualName[0:4]) +} + +// CTXReceivingCompanyField is used in CTX files, characters 5-20 of underlying IndividualName field +func (ed *EntryDetail) CTXReceivingCompanyField() string { + return ed.parseStringField(ed.IndividualName[4:20]) +} + +// CTXReservedField is used in CTX files, characters 21-22 of underlying IndividualName field +func (ed *EntryDetail) CTXReservedField() string { + return ed.IndividualName[20:22] +} + // DiscretionaryDataField returns a space padded string of DiscretionaryData func (ed *EntryDetail) DiscretionaryDataField() string { return ed.alphaField(ed.DiscretionaryData, 2) diff --git a/file.go b/file.go index 4ddb0786c..603b49d74 100644 --- a/file.go +++ b/file.go @@ -37,7 +37,8 @@ var ( msgFileControl = "none or more than one file control exists" msgFileHeader = "none or more than one file headers exists" msgUnknownRecordType = "%s is an unknown record type" - msgFileNoneSEC = "%v SEC(standard entry class) is not implemented" + msgFileNoneSEC = "%v Standard Entry Class Code is not implemented" + msgFileIATSEC = "%v Standard Entry Class Code should use iatBatch" ) // FileError is an error describing issues validating a file diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 66ee7cdd5..9255c2bbc 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -49,7 +49,7 @@ type IATEntryDetail struct { // SecondaryOFACSreeningIndicator - Leave blank SecondaryOFACSreeningIndicator string `json:"SecondaryOFACSreeningIndicator"` // AddendaRecordIndicator indicates the existence of an Addenda Record. - // A value of "1" indicates that one ore more addenda records follow, + // A value of "1" indicates that one or more addenda records follow, // and "0" means no such record is present. AddendaRecordIndicator int `json:"addendaRecordIndicator,omitempty"` // TraceNumber assigned by the ODFI in ascending sequence, is included in each From 93836da9c7b942f81df97235431ac011aa816b84 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 27 Jul 2018 15:53:59 -0400 Subject: [PATCH 0345/1694] #212 Code Coverage #212 Code Coverage --- batch_test.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/batch_test.go b/batch_test.go index 35be03fd0..bc7d80e47 100644 --- a/batch_test.go +++ b/batch_test.go @@ -165,7 +165,7 @@ func BenchmarkSavingsBatchisBatchAmount(b *testing.B) { } } -func testBatchisEntryHash(t testing.TB) { +func testBatchIsEntryHash(t testing.TB) { mockBatch := mockBatch() mockBatch.GetControl().EntryHash = 1 if err := mockBatch.verify(); err != nil { @@ -179,14 +179,14 @@ func testBatchisEntryHash(t testing.TB) { } } -func TestBatchisEntryHash(t *testing.T) { - testBatchisEntryHash(t) +func TestBatchIsEntryHash(t *testing.T) { + testBatchIsEntryHash(t) } -func BenchmarkBatchisEntryHash(b *testing.B) { +func BenchmarkBatchIsEntryHash(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testBatchisEntryHash(b) + testBatchIsEntryHash(b) } } @@ -558,6 +558,18 @@ func testBatchNoEntry(t testing.TB) { t.Errorf("%T: %s", err, err) } } + + // test verify + if err := mockBatch.verify(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "entries" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + } func TestBatchNoEntry(t *testing.T) { @@ -599,6 +611,7 @@ func BenchmarkBatchControl(b *testing.B) { } } +// testIATBatch validates an IAT batch returns an error for batch func testIATBatch(t testing.TB) { bh := NewBatchHeader() bh.ServiceClassCode = 220 @@ -620,13 +633,15 @@ func testIATBatch(t testing.TB) { } } +// TestIATBatch tests validating an IAT batch returns an error for batch func TestIATBatch(t *testing.T) { testIATBatch(t) } +// BenchmarkIATBatch benchmarks validating an IAT batch returns an error for batch func BenchmarkIATBatch(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { testIATBatch(b) } -} +} \ No newline at end of file From 63b9df22a441fb5a3c66598c588d5534501d4ca0 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 27 Jul 2018 16:09:53 -0400 Subject: [PATCH 0346/1694] #212 govet #212 govet --- batch_test.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/batch_test.go b/batch_test.go index bc7d80e47..4e2140865 100644 --- a/batch_test.go +++ b/batch_test.go @@ -396,6 +396,7 @@ func BenchmarkBatchAddendaTraceNumber(b *testing.B) { } } +// testNewBatchDefault validates error for NewBatch if invalid SEC Code func testNewBatchDefault(t testing.TB) { _, err := NewBatch(mockBatchInvalidSECHeader()) @@ -408,10 +409,13 @@ func testNewBatchDefault(t testing.TB) { } } +// TestNewBatchDefault test validating error for NewBatch if invalid SEC Code func TestNewBatchDefault(t *testing.T) { testNewBatchDefault(t) } +// BenchmarkNewBatchDefault benchmarks validating error for NewBatch if +// invalid SEC Code func BenchmarkNewBatchDefault(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -419,6 +423,7 @@ func BenchmarkNewBatchDefault(b *testing.B) { } } +// testBatchCategory validates Batch Category func testBatchCategory(t testing.TB) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch @@ -435,10 +440,12 @@ func testBatchCategory(t testing.TB) { } } +// TestBatchCategory tests validating Batch Category func TestBatchCategory(t *testing.T) { testBatchCategory(t) } +// BenchmarkBatchCategory benchmarks validating Batch Category func BenchmarkBatchCategory(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -446,6 +453,7 @@ func BenchmarkBatchCategory(b *testing.B) { } } +// testBatchCategoryForwardReturn validates Category based on EntryDetail func testBatchCategoryForwardReturn(t testing.TB) { mockBatch := mockBatch() // Add a Addenda Return to the mock batch @@ -467,9 +475,12 @@ func testBatchCategoryForwardReturn(t testing.TB) { } } +// TestBatchCategoryForwardReturn tests validating Category based on EntryDetail func TestBatchCategoryForwardReturn(t *testing.T) { testBatchCategoryForwardReturn(t) } + +// BenchmarkBatchCategoryForwardReturn benchmarks validating Category based on EntryDetail func BenchmarkBatchCategoryForwardReturn(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -547,6 +558,7 @@ func BenchmarkBatchInvalidTraceNumberODFI(b *testing.B) { } } +// testBatchNoEntry validates error for a batch with no entries func testBatchNoEntry(t testing.TB) { mockBatch := mockBatchNoEntry() if err := mockBatch.build(); err != nil { @@ -572,10 +584,12 @@ func testBatchNoEntry(t testing.TB) { } +// TestBatchNoEntry tests validating error for a batch with no entries func TestBatchNoEntry(t *testing.T) { testBatchNoEntry(t) } +// BenchmarkBatchNoEntry benchmarks validating error for a batch with no entries func BenchmarkBatchNoEntry(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -644,4 +658,4 @@ func BenchmarkIATBatch(b *testing.B) { for i := 0; i < b.N; i++ { testIATBatch(b) } -} \ No newline at end of file +} From eeba088f8573f60b989c86526b79b0157c055c6b Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 30 Jul 2018 10:12:56 -0400 Subject: [PATCH 0347/1694] #212 Code Coverage #212 Code Coverage --- batch_test.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/batch_test.go b/batch_test.go index 4e2140865..8b752e0fc 100644 --- a/batch_test.go +++ b/batch_test.go @@ -63,6 +63,7 @@ func mockBatchInvalidSECHeader() *BatchHeader { } // Test cases that apply to all batch types +// testBatchNumberMismatch validates BatchNumber mismatch func testBatchNumberMismatch(t testing.TB) { mockBatch := mockBatch() mockBatch.GetControl().BatchNumber = 2 @@ -77,9 +78,12 @@ func testBatchNumberMismatch(t testing.TB) { } } +// TestBatchNumberMismatch tests validating BatchNumber mismatch func TestBatchNumberMismatch(t *testing.T) { testBatchNumberMismatch(t) } + +// BenchmarkBatchNumberMismatch benchmarks validating BatchNumber mismatch func BenchmarkBatchNumberMismatch(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -87,7 +91,8 @@ func BenchmarkBatchNumberMismatch(b *testing.B) { } } -func testCreditBatchisBatchAmount(t testing.TB) { +// testCreditBatchIsBatchAmount validates Batch TotalCreditEntryDollarAmount +func testCreditBatchIsBatchAmount(t testing.TB) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) e1 := mockBatch.GetEntries()[0] @@ -114,19 +119,22 @@ func testCreditBatchisBatchAmount(t testing.TB) { } } -func TestCreditBatchisBatchAmount(t *testing.T) { - testCreditBatchisBatchAmount(t) +// TestCreditBatchIsBatchAmount test validating Batch TotalCreditEntryDollarAmount +func TestCreditBatchIsBatchAmount(t *testing.T) { + testCreditBatchIsBatchAmount(t) } -func BenchmarkCreditBatchisBatchAmount(b *testing.B) { +// BenchmarkCreditBatchIsBatchAmount benchmarks Batch TotalCreditEntryDollarAmount +func BenchmarkCreditBatchIsBatchAmount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testCreditBatchisBatchAmount(b) + testCreditBatchIsBatchAmount(b) } } -func testSavingsBatchisBatchAmount(t testing.TB) { +// testSavingsBatchIsBatchAmount validates Batch TotalDebitEntryDollarAmount +func testSavingsBatchIsBatchAmount(t testing.TB) { mockBatch := mockBatch() mockBatch.SetHeader(mockBatchHeader()) e1 := mockBatch.GetEntries()[0] @@ -154,14 +162,16 @@ func testSavingsBatchisBatchAmount(t testing.TB) { } } -func TestSavingsBatchisBatchAmount(t *testing.T) { - testSavingsBatchisBatchAmount(t) +// TestSavingsBatchIsBatchAmount tests validating Batch TotalDebitEntryDollarAmount +func TestSavingsBatchIsBatchAmount(t *testing.T) { + testSavingsBatchIsBatchAmount(t) } -func BenchmarkSavingsBatchisBatchAmount(b *testing.B) { +// BenchmarkSavingsBatchIsBatchAmount benchmarks validating Batch TotalDebitEntryDollarAmount +func BenchmarkSavingsBatchIsBatchAmount(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - testSavingsBatchisBatchAmount(b) + testSavingsBatchIsBatchAmount(b) } } From 94f76b40f1b9928852030667d96bd31cb213497c Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 30 Jul 2018 11:54:54 -0400 Subject: [PATCH 0348/1694] #212 Prenote validation #212 Prenote validation --- batchCOR_test.go | 3 +- batchCTX.go | 12 ++++++ batchCTX_test.go | 100 +++++++++++++++++++++++++++++++++++++++++++++++ batcher.go | 1 + 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/batchCOR_test.go b/batchCOR_test.go index 55cc226e9..0b754401b 100644 --- a/batchCOR_test.go +++ b/batchCOR_test.go @@ -5,6 +5,7 @@ package ach import ( + "log" "testing" ) @@ -42,7 +43,7 @@ func mockBatchCOR() *BatchCOR { mockBatch.AddEntry(mockCOREntryDetail()) mockBatch.GetEntries()[0].AddAddenda(mockAddenda98()) if err := mockBatch.Create(); err != nil { - panic(err) + log.Fatal(err) } return mockBatch } diff --git a/batchCTX.go b/batchCTX.go index 4e5116971..c5f0eaa70 100644 --- a/batchCTX.go +++ b/batchCTX.go @@ -57,6 +57,8 @@ func (batch *BatchCTX) Validate() error { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchCTXAddenda} } + // validate CTXAddendaRecord Field is equal to the actual number of Addenda records + // use 0 value if there is no Addenda records addendaRecords, _ := strconv.Atoi(entry.CTXAddendaRecordsField()) if len(entry.Addendum) != addendaRecords { msg := fmt.Sprintf(msgBatchCTXAddendaCount, addendaRecords, len(entry.Addendum)) @@ -64,6 +66,16 @@ func (batch *BatchCTX) Validate() error { } if len(entry.Addendum) > 0 { + + switch entry.TransactionCode { + // Prenote credit 23, 33, 43, 53 + // Prenote debit 28, 38, 48 + case 23, 28, 33, 38, 43, 48, 53: + msg := fmt.Sprintf(msgBatchTransactionCodeAddenda, entry.TransactionCode, "CTX") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} + default: + } + for i := range entry.Addendum { addenda05, ok := entry.Addendum[i].(*Addenda05) if !ok { diff --git a/batchCTX_test.go b/batchCTX_test.go index 6bf7a1422..659570637 100644 --- a/batchCTX_test.go +++ b/batchCTX_test.go @@ -444,3 +444,103 @@ func BenchmarkBatchCTXReserved(b *testing.B) { testBatchCTXReserved(b) } } + +// testBatchCTXZeroAddendaRecords validates zero addenda records +func testBatchCTXZeroAddendaRecords(t testing.TB) { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "CTX" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH CTX" + bh.ODFIIdentification = "12104288" + + entry := NewEntryDetail() + entry.TransactionCode = 22 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetCTXAddendaRecords(1) + entry.SetCTXReceivingCompany("Receiver Company") + entry.SetTraceNumber(mockBatchCTXHeader().ODFIIdentification, 1) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + + mockBatch := NewBatchCTX(bh) + mockBatch.AddEntry(entry) + + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXZeroAddendaRecords tests validating zero addenda records +func TestBatchCTXZeroAddendaRecords(t *testing.T) { + testBatchCTXZeroAddendaRecords(t) +} + +// BenchmarkBatchZeroAddendaRecords benchmarks validating zero addenda records +func BenchmarkBatchCTXZeroAddendaRecords(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXZeroAddendaRecords(b) + } +} + +// testBatchCTXPrenoteAddendaRecords validates prenote addenda records +func testBatchCTXPrenoteAddendaRecords(t testing.TB) { + bh := NewBatchHeader() + bh.ServiceClassCode = 220 + bh.StandardEntryClassCode = "CTX" + bh.CompanyName = "Payee Name" + bh.CompanyIdentification = "121042882" + bh.CompanyEntryDescription = "ACH CTX" + bh.ODFIIdentification = "12104288" + bh.OriginatorStatusCode = 2 + + entry := NewEntryDetail() + entry.TransactionCode = 23 + entry.SetRDFI("231380104") + entry.DFIAccountNumber = "744-5678-99" + entry.Amount = 25000 + entry.IdentificationNumber = "45689033" + entry.SetCTXAddendaRecords(1) + entry.SetCTXReceivingCompany("Receiver Company") + entry.SetTraceNumber(mockBatchCTXHeader().ODFIIdentification, 1) + entry.DiscretionaryData = "01" + entry.Category = CategoryForward + + mockBatch := NewBatchCTX(bh) + mockBatch.AddEntry(entry) + mockBatch.GetEntries()[0].AddAddenda(mockAddenda05()) + + if err := mockBatch.Create(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestBatchCTXPrenoteAddendaRecords tests validating prenote addenda records +func TestBatchCTXPrenoteAddendaRecords(t *testing.T) { + testBatchCTXPrenoteAddendaRecords(t) +} + +// BenchmarkBatchPrenoteAddendaRecords benchmarks validating prenote addenda records +func BenchmarkBatchCTXPrenoteAddendaRecords(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testBatchCTXPrenoteAddendaRecords(b) + } +} diff --git a/batcher.go b/batcher.go index ca541b2d9..ce8f68651 100644 --- a/batcher.go +++ b/batcher.go @@ -65,4 +65,5 @@ var ( msgBatchCheckSerialNumber = "Check Serial Number is required for SEC code %v" msgBatchTransactionCode = "Transaction code %v is not allowed for batch type %v" msgBatchCardTransactionType = "Card Transaction Type %v is invalid" + msgBatchTransactionCodeAddenda = "Addenda not allowed for transaction code %v for batch type %v" ) From 73034af498c04546be88eb933cd7759ce52873c4 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Mon, 30 Jul 2018 14:21:52 -0400 Subject: [PATCH 0349/1694] #212 TransactionCodeDescription Obsolete TransactionCodeDescription - Specfic to SEC type, so if needed code in batch*. Code aded to batchCOR --- batchCOR.go | 26 +++++++++++++++++++++----- entryDetail.go | 15 --------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/batchCOR.go b/batchCOR.go index 3fc04e965..285b0037b 100644 --- a/batchCOR.go +++ b/batchCOR.go @@ -53,14 +53,30 @@ func (batch *BatchCOR) Validate() error { } for _, entry := range batch.Entries { - // COR TransactionCode must be a Return or NOC transaction Code - // Return/NOC of a credit 21, 31, 41, 51 - // Return/NOC of a debit 26, 36, 46, 56 - if entry.TransactionCodeDescription() != ReturnOrNoc { + /* COR TransactionCode must be a Return or NOC transaction Code + Return/NOC + Credit: 21, 31, 41, 51 + Debit: 26, 36, 46, 56 + + Automated payment/deposit + Credit: 22, 32, 42, 52 + Debit: 27, 37, 47, 55 (reversal) + + Prenote + Credit: 23, 33, 43, 53 + Debit: 28, 38, 48 + + Zero dollar amount with remittance data + Credit: 24, 34, 44, 54 + Debit: 29, 39, 49 + */ + 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, "COR") return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg} } - } return nil diff --git a/entryDetail.go b/entryDetail.go index 00225b265..1c757fb54 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -84,7 +84,6 @@ const ( // CategoryNOC defines the entry as being a notification of change of a forward entry to the originating institution CategoryNOC = "NOC" // ReturnOrNoc is the description for the following TransactionCode: 21, 31, 41, 51, 26, 36, 46, 56 - ReturnOrNoc = "RN" ) // NewEntryDetail returns a new EntryDetail with default values for non exported fields @@ -432,17 +431,3 @@ func (ed *EntryDetail) CreditOrDebit() string { } return "" } - -// TransactionCodeDescription determines the transaction code description based on the second number -// in the TransactionCode -func (ed *EntryDetail) TransactionCodeDescription() string { - tc := strconv.Itoa(ed.TransactionCode) - // Take the second number in the TransactionCode - switch tc[1:2] { - // Return or NOC - case "1", "6": - return ReturnOrNoc - default: - } - return "" -} From 11b62e72f44818a1c4f6b332ecb44dc4a942a312 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 31 Jul 2018 09:47:43 -0400 Subject: [PATCH 0350/1694] #212 Write Test #212 Write Test --- test/ach-ctx-write/main.go | 80 +++++++++++++++++++++++++++++++++ test/ach-ctx-write/main_test.go | 7 +++ 2 files changed, 87 insertions(+) create mode 100644 test/ach-ctx-write/main.go create mode 100644 test/ach-ctx-write/main_test.go diff --git a/test/ach-ctx-write/main.go b/test/ach-ctx-write/main.go new file mode 100644 index 000000000..c2b836d1a --- /dev/null +++ b/test/ach-ctx-write/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "github.com/bkmoovio/ach" + "log" + "os" + "time" +) + +func main() { + // Example transfer to write an ACH CTX file to send/credit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + // Set originator bank ODFI and destination Operator for the financial institution + // this is the funding/receiving source of the transfer + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "121042882" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 220 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "CTX" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "ACH CTX" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "121042882" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 22: Credit to Store checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "12345678" // Receivers bank account number + entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.IdentificationNumber = "45689033" + entry.SetCTXAddendaRecords(2) + entry.SetCTXReceivingCompany("Receiver Company") + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.DiscretionaryData = "01" + + addenda1 := ach.NewAddenda05() + addenda1.PaymentRelatedInformation = "Debit First Account" + addenda1.SequenceNumber = 1 + addenda1.EntryDetailSequenceNumber = 0000001 + + addenda2 := ach.NewAddenda05() + addenda2.PaymentRelatedInformation = "Debit Second Account" + addenda2.SequenceNumber = 2 + addenda2.EntryDetailSequenceNumber = 0000001 + + // build the batch + batch := ach.NewBatchCTX(bh) + batch.AddEntry(entry) + batch.GetEntries()[0].AddAddenda(addenda1) + batch.GetEntries()[0].AddAddenda(addenda2) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/test/ach-ctx-write/main_test.go b/test/ach-ctx-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-ctx-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From c934dc7d80d5b577398066500f33fe2f21bd9b3f Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 31 Jul 2018 09:59:37 -0400 Subject: [PATCH 0351/1694] #212 CTX Read test #212CTX Read test --- test/ach-ctx-read/ctx-debit.ach | 10 +++++++++ test/ach-ctx-read/main.go | 36 +++++++++++++++++++++++++++++++++ test/ach-ctx-read/main_test.go | 7 +++++++ test/ach-ctx-write/main.go | 2 +- 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/ach-ctx-read/ctx-debit.ach create mode 100644 test/ach-ctx-read/main.go create mode 100644 test/ach-ctx-read/main_test.go diff --git a/test/ach-ctx-read/ctx-debit.ach b/test/ach-ctx-read/ctx-debit.ach new file mode 100644 index 000000000..f34ab39e1 --- /dev/null +++ b/test/ach-ctx-read/ctx-debit.ach @@ -0,0 +1,10 @@ +101 231380104 1210428821807310000A094101Federal Reserve Bank My Bank Name +5220Name on Account 121042882 CTXACH CTX 180801 0121042880000001 +62723138010412345678 010000000045689033 0002Receiver Company 011121042880000001 +705Debit First Account 00010000001 +705Debit Second Account 00020000001 +82200000030023138010000100000000000000000000121042882 121042880000001 +9000001000001000000030023138010000100000000000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-ctx-read/main.go b/test/ach-ctx-read/main.go new file mode 100644 index 000000000..a96ab1adc --- /dev/null +++ b/test/ach-ctx-read/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/moov-io/ach" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("ctx-debit.ach") + if err != nil { + log.Fatal(err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("Total Amount Credit: %v \n", achFile.Control.TotalCreditEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("Addenda1: %v \n", achFile.Batches[0].GetEntries()[0].Addendum[0].String()) + fmt.Printf("Addenda2: %v \n", achFile.Batches[0].GetEntries()[0].Addendum[1].String()) +} diff --git a/test/ach-ctx-read/main_test.go b/test/ach-ctx-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-ctx-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-ctx-write/main.go b/test/ach-ctx-write/main.go index c2b836d1a..ab73221d8 100644 --- a/test/ach-ctx-write/main.go +++ b/test/ach-ctx-write/main.go @@ -34,7 +34,7 @@ func main() { // can be multiple entry's per batch entry := ach.NewEntryDetail() // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) - entry.TransactionCode = 27 // Code 22: Credit to Store checking account + entry.TransactionCode = 27 // Code 27: Debit checking account entry.SetRDFI("231380104") // Receivers bank transit routing number entry.DFIAccountNumber = "12345678" // Receivers bank account number entry.Amount = 100000000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 From 7b6045086cb4602780627f33e6dc395403fd210d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 31 Jul 2018 10:07:02 -0400 Subject: [PATCH 0352/1694] #212 IAT correction #212 IAT correction --- test/ach-iat-read/{iat-ach.ach => iat-credit.ach} | 2 +- test/ach-iat-read/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename test/ach-iat-read/{iat-ach.ach => iat-credit.ach} (98%) diff --git a/test/ach-iat-read/iat-ach.ach b/test/ach-iat-read/iat-credit.ach similarity index 98% rename from test/ach-iat-read/iat-ach.ach rename to test/ach-iat-read/iat-credit.ach index 838ccda73..3c9f45f44 100644 --- a/test/ach-iat-read/iat-ach.ach +++ b/test/ach-iat-read/iat-credit.ach @@ -17,4 +17,4 @@ 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 diff --git a/test/ach-iat-read/main.go b/test/ach-iat-read/main.go index 8dabd9608..a502082fc 100644 --- a/test/ach-iat-read/main.go +++ b/test/ach-iat-read/main.go @@ -9,7 +9,7 @@ import ( func main() { // open a file for reading. Any io.Reader Can be used - f, err := os.Open("iat-ach.ach") + f, err := os.Open("iat-credit.ach") if err != nil { log.Fatal(err) } From 0c4657902cdb872ad322575a086fd4752f303943 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Tue, 31 Jul 2018 10:43:24 -0500 Subject: [PATCH 0353/1694] Merge conflict resolution --- file.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/file.go b/file.go index 2e6d397b0..603b49d74 100644 --- a/file.go +++ b/file.go @@ -37,7 +37,8 @@ var ( msgFileControl = "none or more than one file control exists" msgFileHeader = "none or more than one file headers exists" msgUnknownRecordType = "%s is an unknown record type" - msgFileNoneSEC = "%v SEC(standard entry class) is not implemented" + msgFileNoneSEC = "%v Standard Entry Class Code is not implemented" + msgFileIATSEC = "%v Standard Entry Class Code should use iatBatch" ) // FileError is an error describing issues validating a file @@ -53,16 +54,16 @@ func (e *FileError) Error() string { // File contains the structures of a parsed ACH File. type File struct { - // ID is a client defined string used as a reference to this record. - ID string `json:"id"` - Header FileHeader `json:"fileHeader"` - Batches []Batcher `json:"batches"` - Control FileControl `json:"fileControl"` + ID string `json:"id"` + Header FileHeader `json:"fileHeader"` + Batches []Batcher `json:"batches"` + IATBatches []IATBatch `json:"IATBatches"` + Control FileControl `json:"fileControl"` // NotificationOfChange (Notification of change) is a slice of references to BatchCOR in file.Batches - NotificationOfChange []*BatchCOR `json:"notificationOfChange,omitempty"` + NotificationOfChange []*BatchCOR // ReturnEntries is a slice of references to file.Batches that contain return entries - ReturnEntries []Batcher `json:"returnEntries,omitempty"` + ReturnEntries []Batcher converters } @@ -82,7 +83,7 @@ func (f *File) Create() error { return err } // Requires at least one Batch in the new file. - if len(f.Batches) <= 0 { + if len(f.Batches) <= 0 && len(f.IATBatches) <= 0 { return &FileError{FieldName: "Batches", Value: strconv.Itoa(len(f.Batches)), Msg: "must have []*Batches to be built"} } // add 2 for FileHeader/control and reset if build was called twice do to error @@ -106,6 +107,21 @@ func (f *File) Create() error { totalDebitAmount = totalDebitAmount + batch.GetControl().TotalDebitEntryDollarAmount totalCreditAmount = totalCreditAmount + batch.GetControl().TotalCreditEntryDollarAmount + } + for i, iatBatch := range f.IATBatches { + // create ascending batch numbers + f.IATBatches[i].GetHeader().BatchNumber = batchSeq + f.IATBatches[i].GetControl().BatchNumber = batchSeq + batchSeq++ + // sum file entry and addenda records. Assume batch.Create() batch properly calculated control + fileEntryAddendaCount = fileEntryAddendaCount + iatBatch.GetControl().EntryAddendaCount + // add 2 for Batch header/control + entry added count + totalRecordsInFile = totalRecordsInFile + 2 + iatBatch.GetControl().EntryAddendaCount + // sum hash from batch control. Assume Batch.Build properly calculated field. + fileEntryHashSum = fileEntryHashSum + iatBatch.GetControl().EntryHash + totalDebitAmount = totalDebitAmount + iatBatch.GetControl().TotalDebitEntryDollarAmount + totalCreditAmount = totalCreditAmount + iatBatch.GetControl().TotalCreditEntryDollarAmount + } // create FileControl from calculated values fc := NewFileControl() @@ -138,6 +154,12 @@ func (f *File) AddBatch(batch Batcher) []Batcher { return f.Batches } +// AddIATBatch appends a IATBatch to the ach.File +func (f *File) AddIATBatch(iatBatch IATBatch) []IATBatch { + f.IATBatches = append(f.IATBatches, iatBatch) + return f.IATBatches +} + // SetHeader allows for header to be built. func (f *File) SetHeader(h FileHeader) *File { f.Header = h @@ -147,7 +169,7 @@ func (f *File) SetHeader(h FileHeader) *File { // Validate NACHA rules on the entire batch before being added to a File func (f *File) Validate() error { // The value of the Batch Count Field is equal to the number of Company/Batch/Header Records in the file. - if f.Control.BatchCount != len(f.Batches) { + if f.Control.BatchCount != (len(f.Batches) + len(f.IATBatches)) { msg := fmt.Sprintf(msgFileCalculatedControlEquality, len(f.Batches), f.Control.BatchCount) return &FileError{FieldName: "BatchCount", Value: strconv.Itoa(len(f.Batches)), Msg: msg} } @@ -163,7 +185,7 @@ func (f *File) Validate() error { return f.isEntryHash() } -// isEntryAddenda is prepared by hashing the RDFI’s 8-digit Routing Number in each entry. +// isEntryAddendaCount is prepared by hashing the RDFI’s 8-digit Routing Number in each entry. //The Entry Hash provides a check against inadvertent alteration of data func (f *File) isEntryAddendaCount() error { count := 0 @@ -171,6 +193,10 @@ func (f *File) isEntryAddendaCount() error { for _, batch := range f.Batches { count += batch.GetControl().EntryAddendaCount } + // IAT + for _, iatBatch := range f.IATBatches { + count += iatBatch.GetControl().EntryAddendaCount + } if f.Control.EntryAddendaCount != count { msg := fmt.Sprintf(msgFileCalculatedControlEquality, count, f.Control.EntryAddendaCount) return &FileError{FieldName: "EntryAddendaCount", Value: f.Control.EntryAddendaCountField(), Msg: msg} @@ -187,6 +213,11 @@ func (f *File) isFileAmount() error { debit += batch.GetControl().TotalDebitEntryDollarAmount credit += batch.GetControl().TotalCreditEntryDollarAmount } + // IAT + for _, iatBatch := range f.IATBatches { + debit += iatBatch.GetControl().TotalDebitEntryDollarAmount + credit += iatBatch.GetControl().TotalCreditEntryDollarAmount + } if f.Control.TotalDebitEntryDollarAmountInFile != debit { msg := fmt.Sprintf(msgFileCalculatedControlEquality, debit, f.Control.TotalDebitEntryDollarAmountInFile) return &FileError{FieldName: "TotalDebitEntryDollarAmountInFile", Value: f.Control.TotalDebitEntryDollarAmountInFileField(), Msg: msg} @@ -215,5 +246,9 @@ func (f *File) calculateEntryHash() string { for _, batch := range f.Batches { hash = hash + batch.GetControl().EntryHash } + // IAT + for _, iatBatch := range f.IATBatches { + hash = hash + iatBatch.GetControl().EntryHash + } return f.numericField(hash, 10) } From 5b0a11386d70bdd8c5d56726c432e71c85a1b353 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 2 Aug 2018 14:37:58 -0400 Subject: [PATCH 0354/1694] #233 IAT Returns #233 IAT Returns --- addenda99.go | 23 +++++++++++++ iatBatch.go | 39 +++++++++++++-------- iatBatchHeader_test.go | 16 +++++++++ iatBatch_test.go | 55 ++++++++++++++++++++++++++++- iatEntryDetail.go | 5 ++- reader.go | 78 +++++++++++++++++++++++++++++++++--------- 6 files changed, 184 insertions(+), 32 deletions(-) diff --git a/addenda99.go b/addenda99.go index 676cfbbf3..810947203 100644 --- a/addenda99.go +++ b/addenda99.go @@ -157,6 +157,29 @@ func (Addenda99 *Addenda99) AddendaInformationField() string { return Addenda99.alphaField(Addenda99.AddendaInformation, 44) } +//IATPaymentAmount sets original forward entry payment amount characters 1-10 of underlying AddendaInformation +func (Addenda99 *Addenda99) IATPaymentAmount(s string) { + Addenda99.AddendaInformation = Addenda99.stringField(s, 10) +} + +//IATAddendaInformation sets Addenda Information for IAT return items, characters 10-44 of +// underlying AddendaInformation +func (Addenda99 *Addenda99) IATAddendaInformation(s string) { + Addenda99.AddendaInformation = Addenda99.AddendaInformation + Addenda99.alphaField(s, 34) +} + +//IATPaymentAmountField returns original forward entry payment amount int, characters 1-10 of +// underlying AddendaInformation +func (Addenda99 *Addenda99) IATPaymentAmountField() int { + return Addenda99.parseNumField(Addenda99.AddendaInformation[0:10]) +} + +//IATAddendaInformationField returns a space padded AddendaInformation string, characters 10-44 of +// underlying AddendaInformation +func (Addenda99 *Addenda99) IATAddendaInformationField() string { + return Addenda99.alphaField(Addenda99.AddendaInformation[9:44], 34) +} + // TraceNumberField returns a zero padded traceNumber string func (Addenda99 *Addenda99) TraceNumberField() string { return Addenda99.numericField(Addenda99.TraceNumber, 15) diff --git a/iatBatch.go b/iatBatch.go index 7b7723ed5..8a1ffc92e 100644 --- a/iatBatch.go +++ b/iatBatch.go @@ -14,8 +14,7 @@ var ( msgIATBatchAddendaIndicator = "is invalid for addenda record(s) found" // There can be up to 2 optional Addenda17 records and up to 5 optional Addenda18 records msgBatchIATAddendum = "7 Addendum is the maximum for SEC code IAT" - msgBatchIATAddenda17 = "2 Addenda17 is the maximum for SEC code IAT" - msgBatchIATAddenda18 = "5 Addenda18 is the maximum for SEC code IAT" + msgBatchIATAddendumCount = "%v Addenda %v for SEC Code IAT" msgBatchIATInvalidAddendumer = "invalid Addendumer for SEC Code IAT" ) @@ -505,27 +504,39 @@ func (batch *IATBatch) Validate() error { if len(entry.Addendum) > 7 { return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATAddendum} } + + // Counter for addendumer for 17, 18,, and 99 + // ToDo: Come up with a better way? + addenda17Count := 0 addenda18Count := 0 + addenda99Count := 0 for _, IATAddenda := range entry.Addendum { - if (IATAddenda.TypeCode() != "17") && (IATAddenda.TypeCode() != "18") { - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATInvalidAddendumer} - } - if IATAddenda.TypeCode() == "17" { + switch IATAddenda.TypeCode() { + case "17": addenda17Count = addenda17Count + 1 - } - if IATAddenda.TypeCode() == "18" { + if addenda17Count > 2 { + msg := fmt.Sprintf(msgBatchIATAddendumCount, addenda17Count, "17") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg} + } + case "18": addenda18Count = addenda18Count + 1 + if addenda18Count > 5 { + msg := fmt.Sprintf(msgBatchIATAddendumCount, addenda18Count, "18") + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", 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} + } + default: + return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATInvalidAddendumer} } } - if addenda17Count > 2 { - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATAddenda17} - } - if addenda18Count > 5 { - return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msgBatchIATAddenda18} - } } // Add type specific validation. // ... diff --git a/iatBatchHeader_test.go b/iatBatchHeader_test.go index db223ec95..1a958b371 100644 --- a/iatBatchHeader_test.go +++ b/iatBatchHeader_test.go @@ -25,6 +25,22 @@ func mockIATBatchHeaderFF() *IATBatchHeader { return bh } +// mockIATBatchReturnHeaderFF creates a IAT Return BatchHeader that is Fixed-Fixed +func mockIATReturnBatchHeaderFF() *IATBatchHeader { + bh := NewIATBatchHeader() + bh.ServiceClassCode = 220 + bh.ForeignExchangeIndicator = "FF" + bh.ForeignExchangeReferenceIndicator = 3 + bh.ISODestinationCountryCode = "US" + bh.OriginatorIdentification = "123456789" + bh.StandardEntryClassCode = "IAT" + 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() diff --git a/iatBatch_test.go b/iatBatch_test.go index 3aaa03c69..74cbb1002 100644 --- a/iatBatch_test.go +++ b/iatBatch_test.go @@ -101,6 +101,16 @@ func mockInvalidAddenda17() *Addenda17 { return addenda17 } +func mockIATAddenda99() *Addenda99 { + addenda99 := NewAddenda99() + addenda99.ReturnCode = "R07" + addenda99.OriginalTrace = 231380100000001 + addenda99.OriginalDFI = "12104288" + addenda99.IATPaymentAmount("0000100000") + addenda99.IATAddendaInformation("Authorization Revoked") + return addenda99 +} + // TestMockIATBatch validates mockIATBatch func TestMockIATBatch(t *testing.T) { iatBatch := mockIATBatch() @@ -1776,7 +1786,6 @@ func testIATBatchAddenda18Count(t testing.TB) { mockBatch.Entries[0].Addenda15 = mockAddenda15() mockBatch.Entries[0].Addenda16 = mockAddenda16() mockBatch.Entries[0].AddIATAddenda(mockAddenda17()) - mockBatch.Entries[0].AddIATAddenda(mockAddenda17B()) mockBatch.Entries[0].AddIATAddenda(mockAddenda18()) mockBatch.Entries[0].AddIATAddenda(mockAddenda18B()) mockBatch.Entries[0].AddIATAddenda(mockAddenda18C()) @@ -1882,3 +1891,47 @@ func BenchmarkIATBatchBHODFI(b *testing.B) { } } + +// testIATBatchAddenda99Count validates IATBatch Addenda99 Count +func testIATBatchAddenda99Count(t testing.TB) { + mockBatch := IATBatch{} + mockBatch.SetHeader(mockIATReturnBatchHeaderFF()) + mockBatch.AddEntry(mockIATEntryDetail()) + mockBatch.Entries[0].Addenda10 = mockAddenda10() + mockBatch.Entries[0].Addenda11 = mockAddenda11() + mockBatch.Entries[0].Addenda12 = mockAddenda12() + mockBatch.Entries[0].Addenda13 = mockAddenda13() + mockBatch.Entries[0].Addenda14 = mockAddenda14() + mockBatch.Entries[0].Addenda15 = mockAddenda15() + mockBatch.Entries[0].Addenda16 = mockAddenda16() + mockBatch.Entries[0].AddIATAddenda(mockIATAddenda99()) + mockBatch.category = CategoryReturn + + if err := mockBatch.build(); err != nil { + t.Errorf("%T: %s", err, err) + } + + if err := mockBatch.Validate(); err != nil { + if e, ok := err.(*BatchError); ok { + if e.FieldName != "Addendum" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } + +} + +// TestIATBatchAddenda99Count tests validating IATBatch Addenda99 Count +func TestIATBatchAddenda99Count(t *testing.T) { + testIATBatchAddenda99Count(t) +} + +// BenchmarkIATBatchAddenda99Count benchmarks validating IATBatch Addenda99 Count +func BenchmarkIATBatchAddenda99Count(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATBatchAddenda99Count(b) + } +} diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 9255c2bbc..7621dba52 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -295,7 +295,10 @@ func (ed *IATEntryDetail) AddIATAddenda(addenda Addendumer) []Addendumer { ed.AddendaRecordIndicator = 1 // checks to make sure that we only have either or, not both switch addenda.(type) { - // Addenda17 + case *Addenda99: + ed.Category = CategoryReturn + ed.Addendum = append(ed.Addendum, addenda) + return ed.Addendum default: ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) diff --git a/reader.go b/reader.go index 6e12b2807..d008ba643 100644 --- a/reader.go +++ b/reader.go @@ -214,17 +214,28 @@ func (r *Reader) parseED() error { // parseEd parses determines whether to parse an IATEntryDetail Addenda or EntryDetail Addenda func (r *Reader) parseEDAddenda() error { - switch r.line[1:3] { - //ToDo; What to do about 98 and 99 ? - case "10", "11", "12", "13", "14", "15", "16", "17", "18": - if err := r.parseIATAddenda(); err != nil { + + if r.currentBatch != nil { + if err := r.parseAddenda(); err != nil { return err } - default: - if err := r.parseAddenda(); err != nil { + } else { + if err := r.parseIATAddenda(); err != nil { return err } } + + /* switch r.line[1:3] { + //ToDo; What to do about 98 and 99 ? + case "10", "11", "12", "13", "14", "15", "16", "17", "18": + if err := r.parseIATAddenda(); err != nil { + return err + } + default: + if err := r.parseAddenda(); err != nil { + return err + } + }*/ return nil } @@ -445,6 +456,28 @@ func (r *Reader) parseIATAddenda() error { } func (r *Reader) switchIATAddenda(entryIndex int) error { + + switch r.line[1:3] { + // IAT mandatory and optional Addenda + case "10", "11", "12", "13", "14", "15", "16", "17", "18": + err := r.mandatoryOptionalIATAddenda(entryIndex) + if err != nil { + return err + } + // IAT return Addenda + case "99": + err := r.returnIATAddenda(entryIndex) + if err != nil { + return err + } + } + return nil +} + +// mandatoryOptionalIATAddenda parses and validates mandatory IAT addenda records: +// Addenda10, Addenda11, Addenda12, Addenda13, Addenda14, Addenda15, Addenda16 +func (r *Reader) mandatoryOptionalIATAddenda(entryIndex int) error { + switch r.line[1:3] { case "10": addenda10 := NewAddenda10() @@ -452,64 +485,77 @@ func (r *Reader) switchIATAddenda(entryIndex int) error { if err := addenda10.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda10 = addenda10 + r.IATCurrentBatch.Entries[entryIndex].Addenda10 = addenda10 case "11": addenda11 := NewAddenda11() addenda11.Parse(r.line) if err := addenda11.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda11 = addenda11 + r.IATCurrentBatch.Entries[entryIndex].Addenda11 = addenda11 case "12": addenda12 := NewAddenda12() addenda12.Parse(r.line) if err := addenda12.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda12 = addenda12 + r.IATCurrentBatch.Entries[entryIndex].Addenda12 = addenda12 case "13": - addenda13 := NewAddenda13() addenda13.Parse(r.line) if err := addenda13.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda13 = addenda13 + r.IATCurrentBatch.Entries[entryIndex].Addenda13 = addenda13 case "14": addenda14 := NewAddenda14() addenda14.Parse(r.line) if err := addenda14.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda14 = addenda14 + r.IATCurrentBatch.Entries[entryIndex].Addenda14 = addenda14 case "15": addenda15 := NewAddenda15() addenda15.Parse(r.line) if err := addenda15.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda15 = addenda15 + r.IATCurrentBatch.Entries[entryIndex].Addenda15 = addenda15 case "16": addenda16 := NewAddenda16() addenda16.Parse(r.line) if err := addenda16.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].Addenda16 = addenda16 + r.IATCurrentBatch.Entries[entryIndex].Addenda16 = addenda16 case "17": addenda17 := NewAddenda17() addenda17.Parse(r.line) if err := addenda17.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda17) + r.IATCurrentBatch.Entries[entryIndex].AddIATAddenda(addenda17) case "18": addenda18 := NewAddenda18() addenda18.Parse(r.line) if err := addenda18.Validate(); err != nil { return err } - r.IATCurrentBatch.GetEntries()[entryIndex].AddIATAddenda(addenda18) + r.IATCurrentBatch.Entries[entryIndex].AddIATAddenda(addenda18) } + + return nil +} + +// returnIATAddenda parses and validates IAT return record Addenda99 +func (r *Reader) returnIATAddenda(entryIndex int) error { + + addenda99 := NewAddenda99() + addenda99.Parse(r.line) + if err := addenda99.Validate(); err != nil { + return err + } + r.IATCurrentBatch.Entries[entryIndex].AddIATAddenda(addenda99) + return nil } From 90347f3df4572a775e2f202f492b3dfe39f7bc91 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Thu, 2 Aug 2018 14:45:03 -0400 Subject: [PATCH 0355/1694] #233 #233 --- reader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reader.go b/reader.go index d008ba643..8e0024a55 100644 --- a/reader.go +++ b/reader.go @@ -474,8 +474,8 @@ func (r *Reader) switchIATAddenda(entryIndex int) error { return nil } -// mandatoryOptionalIATAddenda parses and validates mandatory IAT addenda records: -// Addenda10, Addenda11, Addenda12, Addenda13, Addenda14, Addenda15, Addenda16 +// mandatoryOptionalIATAddenda parses and validates mandatory IAT addenda records: Addenda10, +// Addenda11, Addenda12, Addenda13, Addenda14, Addenda15, Addenda16, Addenda17, Addenda18 func (r *Reader) mandatoryOptionalIATAddenda(entryIndex int) error { switch r.line[1:3] { From ab046efbe74fc6d4d6d66ee91cf80faf6dffdd46 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 3 Aug 2018 11:55:05 -0400 Subject: [PATCH 0356/1694] # 233 testIATReturn Added # 233 testIATReturn Added --- writer_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/writer_test.go b/writer_test.go index 389e067f8..44c8f7165 100644 --- a/writer_test.go +++ b/writer_test.go @@ -291,3 +291,64 @@ func BenchmarkPPDIATWrite(b *testing.B) { testPPDIATWrite(b) } } + +// testIATReturn writes a IAT ACH Return file +func testIATReturn(t testing.TB) { + file := NewFile().SetHeader(mockFileHeader()) + iatBatch := IATBatch{} + iatBatch.SetHeader(mockIATBatchHeaderFF()) + iatBatch.AddEntry(mockIATEntryDetail()) + iatBatch.Entries[0].Addenda10 = mockAddenda10() + iatBatch.Entries[0].Addenda11 = mockAddenda11() + iatBatch.Entries[0].Addenda12 = mockAddenda12() + iatBatch.Entries[0].Addenda13 = mockAddenda13() + iatBatch.Entries[0].Addenda14 = mockAddenda14() + iatBatch.Entries[0].Addenda15 = mockAddenda15() + iatBatch.Entries[0].Addenda16 = mockAddenda16() + iatBatch.Entries[0].AddIATAddenda(mockIATAddenda99()) + iatBatch.Create() + file.AddIATBatch(iatBatch) + + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + b := &bytes.Buffer{} + f := NewWriter(b) + + if err := f.Write(file); err != nil { + t.Errorf("%T: %s", err, err) + } + + r := NewReader(strings.NewReader(b.String())) + _, err := r.Read() + if err != nil { + t.Errorf("%T: %s", err, err) + } + if err = r.File.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + + /* // Write IAT records to standard output. Anything io.Writer + w := NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush()*/ +} + +// TestIATReturn tests writing a IAT ACH Return file +func TestIATReturn(t *testing.T) { + testIATReturn(t) +} + +// BenchmarkIATReturn benchmarks validating writing a IAT ACH Return file +func BenchmarkIATReturn(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testIATReturn(b) + } +} From 0f11eba10062810100bd9b6cd5a72a504437840d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Fri, 3 Aug 2018 12:25:07 -0400 Subject: [PATCH 0357/1694] CCD-example CCD-example --- test/ach-ccd-read/ccd-debit.ach | 10 +++++ test/ach-ccd-read/main.go | 43 ++++++++++++++++++ test/ach-ccd-read/main_test.go | 7 +++ test/ach-ccd-write/main.go | 79 +++++++++++++++++++++++++++++++++ test/ach-ccd-write/main_test.go | 7 +++ 5 files changed, 146 insertions(+) create mode 100644 test/ach-ccd-read/ccd-debit.ach create mode 100644 test/ach-ccd-read/main.go create mode 100644 test/ach-ccd-read/main_test.go create mode 100644 test/ach-ccd-write/main.go create mode 100644 test/ach-ccd-write/main_test.go diff --git a/test/ach-ccd-read/ccd-debit.ach b/test/ach-ccd-read/ccd-debit.ach new file mode 100644 index 000000000..4d6c88269 --- /dev/null +++ b/test/ach-ccd-read/ccd-debit.ach @@ -0,0 +1,10 @@ +101 231380104 0313000121808030000A094101Federal Reserve Bank My Bank Name +5220Name on Account 031300012 CCDVndr Pay 180804 0031300010000001 +627231380104744-5678-99 0000500000location #1 Best Co. #1 S 0031300010000001 +627231380104744-5678-99 0000000125Fee #1 Best Co. #1 S 0031300010000002 +82200000020046276020000000500125000000000000031300012 031300010000001 +9000001000001000000020046276020000000500125000000000000 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 \ No newline at end of file diff --git a/test/ach-ccd-read/main.go b/test/ach-ccd-read/main.go new file mode 100644 index 000000000..b5788e7f7 --- /dev/null +++ b/test/ach-ccd-read/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/moov-io/ach" +) + +func main() { + // open a file for reading. Any io.Reader Can be used + f, err := os.Open("ccd-debit.ach") + if err != nil { + log.Fatal(err) + } + r := ach.NewReader(f) + achFile, err := r.Read() + if err != nil { + fmt.Printf("Issue reading file: %+v \n", err) + } + // ensure we have a validated file structure + if achFile.Validate(); err != nil { + fmt.Printf("Could not validate entire read file: %v", err) + } + // If you trust the file but it's formatting is off building will probably resolve the malformed file. + if achFile.Create(); err != nil { + fmt.Printf("Could not build file with read properties: %v", err) + } + + fmt.Printf("Total Amount Debit: %v \n", achFile.Control.TotalDebitEntryDollarAmountInFile) + fmt.Printf("Total Amount Credit: %v \n", achFile.Control.TotalCreditEntryDollarAmountInFile) + fmt.Printf("SEC Code: %v \n", achFile.Batches[0].GetHeader().StandardEntryClassCode) + fmt.Printf("CCD Entry Discretionary Data: %v \n", achFile.Batches[0].GetEntries()[0].DiscretionaryDataField()) + fmt.Printf("CCD Entry Identification Number: %v \n", achFile.Batches[0].GetEntries()[0].IdentificationNumberField()) + fmt.Printf("CCD Entry Receiving Company: %v \n", achFile.Batches[0].GetEntries()[0].ReceivingCompanyField()) + fmt.Printf("CCD Entry Trace Number: %v \n", achFile.Batches[0].GetEntries()[0].TraceNumberField()) + fmt.Printf("CCD Fee Discretionary Data: %v \n", achFile.Batches[0].GetEntries()[1].DiscretionaryDataField()) + fmt.Printf("CCD Fee Identification Number: %v \n", achFile.Batches[0].GetEntries()[1].IdentificationNumberField()) + fmt.Printf("CCD Fee Receiving Company: %v \n", achFile.Batches[0].GetEntries()[1].ReceivingCompanyField()) + fmt.Printf("CCD Fee Trace Number: %v \n", achFile.Batches[0].GetEntries()[1].TraceNumberField()) + +} diff --git a/test/ach-ccd-read/main_test.go b/test/ach-ccd-read/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-ccd-read/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} diff --git a/test/ach-ccd-write/main.go b/test/ach-ccd-write/main.go new file mode 100644 index 000000000..3bd267d14 --- /dev/null +++ b/test/ach-ccd-write/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "log" + "os" + "time" + + "github.com/moov-io/ach" +) + +func main() { + // Example transfer to write an ACH CCD file to send/credit a external institutions account + // Important: All financial institutions are different and will require registration and exact field values. + + // Set originator bank ODFI and destination Operator for the financial institution + // this is the funding/receiving source of the transfer + fh := ach.NewFileHeader() + fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent + fh.ImmediateOrigin = "031300012" // Routing Number of the ACH Operator or sending point that is sending the file + fh.FileCreationDate = time.Now() // Today's Date + fh.ImmediateDestinationName = "Federal Reserve Bank" + fh.ImmediateOriginName = "My Bank Name" + + // BatchHeader identifies the originating entity and the type of transactions contained in the batch + bh := ach.NewBatchHeader() + bh.ServiceClassCode = 220 // ACH credit pushes money out, 225 debits/pulls money in. + bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver + bh.CompanyIdentification = fh.ImmediateOrigin + bh.StandardEntryClassCode = "CCD" // Consumer destination vs Company CCD + bh.CompanyEntryDescription = "Vndr Pay" // will be on receiving accounts statement + bh.EffectiveEntryDate = time.Now().AddDate(0, 0, 1) + bh.ODFIIdentification = "031300012" // Originating Routing Number + + // Identifies the receivers account information + // can be multiple entry's per batch + entry := ach.NewEntryDetail() + // Identifies the entry as a debit and credit entry AND to what type of account (Savings, DDA, Loan, GL) + entry.TransactionCode = 27 // Code 22: Demand Debit(deposit) to checking account + entry.SetRDFI("231380104") // Receivers bank transit routing number + entry.DFIAccountNumber = "744-5678-99" // Receivers bank account number + entry.Amount = 500000 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entry.IdentificationNumber = "location #1" + entry.SetReceivingCompany("Best Co. #1") + entry.SetTraceNumber(bh.ODFIIdentification, 1) + entry.DiscretionaryData = "S" + + entryOne := ach.NewEntryDetail() // Fee Entry + entryOne.TransactionCode = 27 // Code 22: Demand Debit(deposit) to checking account + entryOne.SetRDFI("231380104") // Receivers bank transit routing number + entryOne.DFIAccountNumber = "744-5678-99" // Receivers bank account number + entryOne.Amount = 125 // Amount of transaction with no decimal. One dollar and eleven cents = 111 + entryOne.IdentificationNumber = "Fee #1" + entryOne.SetReceivingCompany("Best Co. #1") + entryOne.SetTraceNumber(bh.ODFIIdentification, 2) + entryOne.DiscretionaryData = "S" + + // build the batch + batch := ach.NewBatchCCD(bh) + batch.AddEntry(entry) + batch.AddEntry(entryOne) + if err := batch.Create(); err != nil { + log.Fatalf("Unexpected error building batch: %s\n", err) + } + + // build the file + file := ach.NewFile() + file.SetHeader(fh) + file.AddBatch(batch) + if err := file.Create(); err != nil { + log.Fatalf("Unexpected error building file: %s\n", err) + } + + // write the file to std out. Anything io.Writer + w := ach.NewWriter(os.Stdout) + if err := w.Write(file); err != nil { + log.Fatalf("Unexpected error: %s\n", err) + } + w.Flush() +} diff --git a/test/ach-ccd-write/main_test.go b/test/ach-ccd-write/main_test.go new file mode 100644 index 000000000..45fcc779c --- /dev/null +++ b/test/ach-ccd-write/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test(t *testing.T) { + main() +} From 3593a7b5517cc48c5f527b5df84ea4b38fb9db83 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 13 Aug 2018 13:02:19 -0600 Subject: [PATCH 0358/1694] Remove Batch from File on DeleteBatch --- server/repositoryInMemory.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/repositoryInMemory.go b/server/repositoryInMemory.go index b02dce03d..4cf663bd9 100644 --- a/server/repositoryInMemory.go +++ b/server/repositoryInMemory.go @@ -111,13 +111,13 @@ func (r *repositoryInMemory) FindAllBatches(fileID string) []*ach.Batcher { func (r *repositoryInMemory) DeleteBatch(fileID string, batchID string) error { r.mtx.RLock() defer r.mtx.RUnlock() - for _, val := range r.files[fileID].Batches { - if val.ID() == batchID { - //append() - //delete(val) - // TODO delete the bach from the file - return ErrNotFound + + for i := len(r.files[fileID].Batches) - 1; i >= 0; i-- { + if r.files[fileID].Batches[i].ID() == batchID { + r.files[fileID].Batches = append(r.files[fileID].Batches[:i], r.files[fileID].Batches[i+1:]...) + //fmt.Println(r.files[fileID].Batches) + return nil } } - return nil + return ErrNotFound } From 928bd47802d2c131f6512a34313e2d642ec22408 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 14 Aug 2018 19:47:32 -0500 Subject: [PATCH 0359/1694] switch String() to use strings.Builder internally --- addenda02.go | 29 +++++++++++++++-------------- addenda05.go | 14 ++++++++------ addenda10.go | 22 ++++++++++++---------- addenda11.go | 16 +++++++++------- addenda12.go | 18 ++++++++++-------- addenda13.go | 20 +++++++++++--------- addenda14.go | 20 +++++++++++--------- addenda15.go | 16 +++++++++------- addenda16.go | 16 +++++++++------- addenda17.go | 14 ++++++++------ addenda18.go | 22 ++++++++++++---------- addenda98.go | 23 ++++++++++++----------- addenda99.go | 21 +++++++++++---------- batchControl.go | 27 ++++++++++++++------------- batchHeader.go | 31 ++++++++++++++++--------------- entryDetail.go | 28 +++++++++++++++------------- fileControl.go | 26 +++++++++++++++----------- fileHeader.go | 33 ++++++++++++++++----------------- iatBatchHeader.go | 39 ++++++++++++++++++++------------------- iatEntryDetail.go | 31 +++++++++++++++++-------------- 20 files changed, 250 insertions(+), 216 deletions(-) diff --git a/addenda02.go b/addenda02.go index 804cd153d..79c0547e1 100644 --- a/addenda02.go +++ b/addenda02.go @@ -89,20 +89,21 @@ func (addenda02 *Addenda02) Parse(record string) { // String writes the Addenda02 struct to a 94 character string. func (addenda02 *Addenda02) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v", - addenda02.recordType, - addenda02.typeCode, - addenda02.ReferenceInformationOneField(), - addenda02.ReferenceInformationTwoField(), - addenda02.TerminalIdentificationCodeField(), - addenda02.TransactionSerialNumberField(), - addenda02.TransactionDateField(), - addenda02.AuthorizationCodeOrExpireDateField(), - addenda02.TerminalLocationField(), - addenda02.TerminalCityField(), - addenda02.TerminalStateField(), - addenda02.TraceNumberField(), - ) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda02.recordType) + buf.WriteString(addenda02.typeCode) + buf.WriteString(addenda02.ReferenceInformationOneField()) + buf.WriteString(addenda02.ReferenceInformationTwoField()) + buf.WriteString(addenda02.TerminalIdentificationCodeField()) + buf.WriteString(addenda02.TransactionSerialNumberField()) + buf.WriteString(addenda02.TransactionDateField()) + buf.WriteString(addenda02.AuthorizationCodeOrExpireDateField()) + buf.WriteString(addenda02.TerminalLocationField()) + buf.WriteString(addenda02.TerminalCityField()) + buf.WriteString(addenda02.TerminalStateField()) + buf.WriteString(addenda02.TraceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda05.go b/addenda05.go index 570ee8f94..ed1263ce9 100644 --- a/addenda05.go +++ b/addenda05.go @@ -60,12 +60,14 @@ func (addenda05 *Addenda05) Parse(record string) { // String writes the Addenda05 struct to a 94 character string. func (addenda05 *Addenda05) String() string { - return fmt.Sprintf("%v%v%v%v%v", - addenda05.recordType, - addenda05.typeCode, - addenda05.PaymentRelatedInformationField(), - addenda05.SequenceNumberField(), - addenda05.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda05.recordType) + buf.WriteString(addenda05.typeCode) + buf.WriteString(addenda05.PaymentRelatedInformationField()) + buf.WriteString(addenda05.SequenceNumberField()) + buf.WriteString(addenda05.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda10.go b/addenda10.go index 1d6cf7cec..2458ea48b 100644 --- a/addenda10.go +++ b/addenda10.go @@ -79,16 +79,18 @@ func (addenda10 *Addenda10) Parse(record string) { // String writes the Addenda10 struct to a 94 character string. func (addenda10 *Addenda10) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v", - addenda10.recordType, - addenda10.typeCode, - // TransactionTypeCode Validator - addenda10.TransactionTypeCode, - addenda10.ForeignPaymentAmountField(), - addenda10.ForeignTraceNumberField(), - addenda10.NameField(), - addenda10.reservedField(), - addenda10.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda10.recordType) + buf.WriteString(addenda10.typeCode) + // TransactionTypeCode Validator + buf.WriteString(addenda10.TransactionTypeCode) + buf.WriteString(addenda10.ForeignPaymentAmountField()) + buf.WriteString(addenda10.ForeignTraceNumberField()) + buf.WriteString(addenda10.NameField()) + buf.WriteString(addenda10.reservedField()) + buf.WriteString(addenda10.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda11.go b/addenda11.go index 831ba1775..1fce2e2cd 100644 --- a/addenda11.go +++ b/addenda11.go @@ -66,13 +66,15 @@ func (addenda11 *Addenda11) Parse(record string) { // String writes the Addenda11 struct to a 94 character string. func (addenda11 *Addenda11) String() string { - return fmt.Sprintf("%v%v%v%v%v%v", - addenda11.recordType, - addenda11.typeCode, - addenda11.OriginatorNameField(), - addenda11.OriginatorStreetAddressField(), - addenda11.reservedField(), - addenda11.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda11.recordType) + buf.WriteString(addenda11.typeCode) + buf.WriteString(addenda11.OriginatorNameField()) + buf.WriteString(addenda11.OriginatorStreetAddressField()) + buf.WriteString(addenda11.reservedField()) + buf.WriteString(addenda11.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda12.go b/addenda12.go index 054cda81a..68800b446 100644 --- a/addenda12.go +++ b/addenda12.go @@ -71,14 +71,16 @@ func (addenda12 *Addenda12) Parse(record string) { // String writes the Addenda12 struct to a 94 character string. func (addenda12 *Addenda12) String() string { - return fmt.Sprintf("%v%v%v%v%v%v", - addenda12.recordType, - addenda12.typeCode, - addenda12.OriginatorCityStateProvinceField(), - // ToDo Validator for backslash - addenda12.OriginatorCountryPostalCodeField(), - addenda12.reservedField(), - addenda12.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda12.recordType) + buf.WriteString(addenda12.typeCode) + buf.WriteString(addenda12.OriginatorCityStateProvinceField()) + // ToDo Validator for backslash + buf.WriteString(addenda12.OriginatorCountryPostalCodeField()) + buf.WriteString(addenda12.reservedField()) + buf.WriteString(addenda12.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda13.go b/addenda13.go index 3a154f1a6..19f5cdeb4 100644 --- a/addenda13.go +++ b/addenda13.go @@ -91,15 +91,17 @@ func (addenda13 *Addenda13) Parse(record string) { // String writes the Addenda13 struct to a 94 character string. func (addenda13 *Addenda13) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v", - addenda13.recordType, - addenda13.typeCode, - addenda13.ODFINameField(), - addenda13.ODFIIDNumberQualifierField(), - addenda13.ODFIIdentificationField(), - addenda13.ODFIBranchCountryCodeField(), - addenda13.reservedField(), - addenda13.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda13.recordType) + buf.WriteString(addenda13.typeCode) + buf.WriteString(addenda13.ODFINameField()) + buf.WriteString(addenda13.ODFIIDNumberQualifierField()) + buf.WriteString(addenda13.ODFIIdentificationField()) + buf.WriteString(addenda13.ODFIBranchCountryCodeField()) + buf.WriteString(addenda13.reservedField()) + buf.WriteString(addenda13.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda14.go b/addenda14.go index 1c9ff163f..016c87224 100644 --- a/addenda14.go +++ b/addenda14.go @@ -87,15 +87,17 @@ func (addenda14 *Addenda14) Parse(record string) { // String writes the Addenda14 struct to a 94 character string. func (addenda14 *Addenda14) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v", - addenda14.recordType, - addenda14.typeCode, - addenda14.RDFINameField(), - addenda14.RDFIIDNumberQualifierField(), - addenda14.RDFIIdentificationField(), - addenda14.RDFIBranchCountryCodeField(), - addenda14.reservedField(), - addenda14.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda14.recordType) + buf.WriteString(addenda14.typeCode) + buf.WriteString(addenda14.RDFINameField()) + buf.WriteString(addenda14.RDFIIDNumberQualifierField()) + buf.WriteString(addenda14.RDFIIdentificationField()) + buf.WriteString(addenda14.RDFIBranchCountryCodeField()) + buf.WriteString(addenda14.reservedField()) + buf.WriteString(addenda14.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda15.go b/addenda15.go index 0cdd8665f..933b8c1cb 100644 --- a/addenda15.go +++ b/addenda15.go @@ -67,13 +67,15 @@ func (addenda15 *Addenda15) Parse(record string) { // String writes the Addenda15 struct to a 94 character string. func (addenda15 *Addenda15) String() string { - return fmt.Sprintf("%v%v%v%v%v%v", - addenda15.recordType, - addenda15.typeCode, - addenda15.ReceiverIDNumberField(), - addenda15.ReceiverStreetAddressField(), - addenda15.reservedField(), - addenda15.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda15.recordType) + buf.WriteString(addenda15.typeCode) + buf.WriteString(addenda15.ReceiverIDNumberField()) + buf.WriteString(addenda15.ReceiverStreetAddressField()) + buf.WriteString(addenda15.reservedField()) + buf.WriteString(addenda15.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda16.go b/addenda16.go index a53728213..812bda043 100644 --- a/addenda16.go +++ b/addenda16.go @@ -70,13 +70,15 @@ func (addenda16 *Addenda16) Parse(record string) { // String writes the Addenda16 struct to a 94 character string. func (addenda16 *Addenda16) String() string { - return fmt.Sprintf("%v%v%v%v%v%v", - addenda16.recordType, - addenda16.typeCode, - addenda16.ReceiverCityStateProvinceField(), - addenda16.ReceiverCountryPostalCodeField(), - addenda16.reservedField(), - addenda16.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda16.recordType) + buf.WriteString(addenda16.typeCode) + buf.WriteString(addenda16.ReceiverCityStateProvinceField()) + buf.WriteString(addenda16.ReceiverCountryPostalCodeField()) + buf.WriteString(addenda16.reservedField()) + buf.WriteString(addenda16.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda17.go b/addenda17.go index fe897f088..1b387b3fc 100644 --- a/addenda17.go +++ b/addenda17.go @@ -65,12 +65,14 @@ func (addenda17 *Addenda17) Parse(record string) { // String writes the Addenda17 struct to a 94 character string. func (addenda17 *Addenda17) String() string { - return fmt.Sprintf("%v%v%v%v%v", - addenda17.recordType, - addenda17.typeCode, - addenda17.PaymentRelatedInformationField(), - addenda17.SequenceNumberField(), - addenda17.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda17.recordType) + buf.WriteString(addenda17.typeCode) + buf.WriteString(addenda17.PaymentRelatedInformationField()) + buf.WriteString(addenda17.SequenceNumberField()) + buf.WriteString(addenda17.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda18.go b/addenda18.go index bfa597dac..8b6c53c2a 100644 --- a/addenda18.go +++ b/addenda18.go @@ -94,16 +94,18 @@ func (addenda18 *Addenda18) Parse(record string) { // String writes the Addenda18 struct to a 94 character string. func (addenda18 *Addenda18) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v", - addenda18.recordType, - addenda18.typeCode, - addenda18.ForeignCorrespondentBankNameField(), - addenda18.ForeignCorrespondentBankIDNumberQualifierField(), - addenda18.ForeignCorrespondentBankIDNumberField(), - addenda18.ForeignCorrespondentBankBranchCountryCodeField(), - addenda18.reservedField(), - addenda18.SequenceNumberField(), - addenda18.EntryDetailSequenceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda18.recordType) + buf.WriteString(addenda18.typeCode) + buf.WriteString(addenda18.ForeignCorrespondentBankNameField()) + buf.WriteString(addenda18.ForeignCorrespondentBankIDNumberQualifierField()) + buf.WriteString(addenda18.ForeignCorrespondentBankIDNumberField()) + buf.WriteString(addenda18.ForeignCorrespondentBankBranchCountryCodeField()) + buf.WriteString(addenda18.reservedField()) + buf.WriteString(addenda18.SequenceNumberField()) + buf.WriteString(addenda18.EntryDetailSequenceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/addenda98.go b/addenda98.go index 017d82925..199cc8b79 100644 --- a/addenda98.go +++ b/addenda98.go @@ -86,17 +86,18 @@ func (addenda98 *Addenda98) Parse(record string) { // String writes the Addenda98 struct to a 94 character string func (addenda98 *Addenda98) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v", - addenda98.recordType, - addenda98.TypeCode(), - addenda98.ChangeCode, - addenda98.OriginalTraceField(), - " ", //6 char reserved field - addenda98.OriginalDFIField(), - addenda98.CorrectedDataField(), - " ", // 15 char reserved field - addenda98.TraceNumberField(), - ) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(addenda98.recordType) + buf.WriteString(addenda98.TypeCode()) + buf.WriteString(addenda98.ChangeCode) + buf.WriteString(addenda98.OriginalTraceField()) + buf.WriteString(" ") // 6 char reserved field + buf.WriteString(addenda98.OriginalDFIField()) + buf.WriteString(addenda98.CorrectedDataField()) + buf.WriteString(" ") // 15 char reserved field + buf.WriteString(addenda98.TraceNumberField()) + return buf.String() } // Validate verifies NACHA rules for Addenda98 diff --git a/addenda99.go b/addenda99.go index 810947203..05bcae96c 100644 --- a/addenda99.go +++ b/addenda99.go @@ -98,16 +98,17 @@ func (Addenda99 *Addenda99) Parse(record string) { // String writes the Addenda99 struct to a 94 character string func (Addenda99 *Addenda99) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v", - Addenda99.recordType, - Addenda99.TypeCode(), - Addenda99.ReturnCode, - Addenda99.OriginalTraceField(), - Addenda99.DateOfDeathField(), - Addenda99.OriginalDFIField(), - Addenda99.AddendaInformationField(), - Addenda99.TraceNumberField(), - ) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(Addenda99.recordType) + buf.WriteString(Addenda99.TypeCode()) + buf.WriteString(Addenda99.ReturnCode) + buf.WriteString(Addenda99.OriginalTraceField()) + buf.WriteString(Addenda99.DateOfDeathField()) + buf.WriteString(Addenda99.OriginalDFIField()) + buf.WriteString(Addenda99.AddendaInformationField()) + buf.WriteString(Addenda99.TraceNumberField()) + return buf.String() } // Validate verifies NACHA rules for Addenda99 diff --git a/batchControl.go b/batchControl.go index 077e23666..95dad91cd 100644 --- a/batchControl.go +++ b/batchControl.go @@ -109,19 +109,20 @@ func NewBatchControl() *BatchControl { // String writes the BatchControl struct to a 94 character string. func (bc *BatchControl) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v", - bc.recordType, - bc.ServiceClassCode, - bc.EntryAddendaCountField(), - bc.EntryHashField(), - bc.TotalDebitEntryDollarAmountField(), - bc.TotalCreditEntryDollarAmountField(), - bc.CompanyIdentificationField(), - bc.MessageAuthenticationCodeField(), - " ", - bc.ODFIIdentificationField(), - bc.BatchNumberField(), - ) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(bc.recordType) + buf.WriteString(string(bc.ServiceClassCode)) + buf.WriteString(bc.EntryAddendaCountField()) + buf.WriteString(bc.EntryHashField()) + buf.WriteString(bc.TotalDebitEntryDollarAmountField()) + buf.WriteString(bc.TotalCreditEntryDollarAmountField()) + buf.WriteString(bc.CompanyIdentificationField()) + buf.WriteString(bc.MessageAuthenticationCodeField()) + buf.WriteString(" ") + buf.WriteString(bc.ODFIIdentificationField()) + buf.WriteString(bc.BatchNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/batchHeader.go b/batchHeader.go index 31fbd1947..cf5716e30 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -156,21 +156,22 @@ func (bh *BatchHeader) Parse(record string) { // String writes the BatchHeader struct to a 94 character string. func (bh *BatchHeader) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v%v", - bh.recordType, - bh.ServiceClassCode, - bh.CompanyNameField(), - bh.CompanyDiscretionaryDataField(), - bh.CompanyIdentificationField(), - bh.StandardEntryClassCode, - bh.CompanyEntryDescriptionField(), - bh.CompanyDescriptiveDateField(), - bh.EffectiveEntryDateField(), - bh.settlementDateField(), - bh.OriginatorStatusCode, - bh.ODFIIdentificationField(), - bh.BatchNumberField(), - ) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(bh.recordType) + buf.WriteString(string(bh.ServiceClassCode)) + buf.WriteString(bh.CompanyNameField()) + buf.WriteString(bh.CompanyDiscretionaryDataField()) + buf.WriteString(bh.CompanyIdentificationField()) + buf.WriteString(bh.StandardEntryClassCode) + buf.WriteString(bh.CompanyEntryDescriptionField()) + buf.WriteString(bh.CompanyDescriptiveDateField()) + buf.WriteString(bh.EffectiveEntryDateField()) + buf.WriteString(bh.settlementDateField()) + buf.WriteString(string(bh.OriginatorStatusCode)) + buf.WriteString(bh.ODFIIdentificationField()) + buf.WriteString(bh.BatchNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/entryDetail.go b/entryDetail.go index 1c757fb54..8db19817d 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -126,18 +126,20 @@ func (ed *EntryDetail) Parse(record string) { // String writes the EntryDetail struct to a 94 character string. func (ed *EntryDetail) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v", - ed.recordType, - ed.TransactionCode, - ed.RDFIIdentificationField(), - ed.CheckDigit, - ed.DFIAccountNumberField(), - ed.AmountField(), - ed.IdentificationNumberField(), - ed.IndividualNameField(), - ed.DiscretionaryDataField(), - ed.AddendaRecordIndicator, - ed.TraceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(ed.recordType) + buf.WriteString(string(ed.TransactionCode)) + buf.WriteString(ed.RDFIIdentificationField()) + buf.WriteString(ed.CheckDigit) + buf.WriteString(ed.DFIAccountNumberField()) + buf.WriteString(ed.AmountField()) + buf.WriteString(ed.IdentificationNumberField()) + buf.WriteString(ed.IndividualNameField()) + buf.WriteString(ed.DiscretionaryDataField()) + buf.WriteString(string(ed.AddendaRecordIndicator)) + buf.WriteString(ed.TraceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated @@ -224,7 +226,7 @@ func (ed *EntryDetail) AddAddenda(addenda Addendumer) []Addendumer { ed.Addendum = nil ed.Addendum = append(ed.Addendum, addenda) return ed.Addendum - // default is current *Addenda05 + // default is current *Addenda05 default: ed.Category = CategoryForward ed.Addendum = append(ed.Addendum, addenda) diff --git a/fileControl.go b/fileControl.go index d63dd7f70..9a507359e 100644 --- a/fileControl.go +++ b/fileControl.go @@ -4,7 +4,10 @@ package ach -import "fmt" +import ( + "fmt" + "strings" +) // FileControl record contains entry counts, dollar totals and hash // totals accumulated from each batch control record in the file. @@ -71,16 +74,17 @@ func NewFileControl() FileControl { // String writes the FileControl struct to a 94 character string. func (fc *FileControl) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v", - fc.recordType, - fc.BatchCountField(), - fc.BlockCountField(), - fc.EntryAddendaCountField(), - fc.EntryHashField(), - fc.TotalDebitEntryDollarAmountInFileField(), - fc.TotalCreditEntryDollarAmountInFileField(), - fc.reserved, - ) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(fc.recordType) + buf.WriteString(fc.BatchCountField()) + buf.WriteString(fc.BlockCountField()) + buf.WriteString(fc.EntryAddendaCountField()) + buf.WriteString(fc.EntryHashField()) + buf.WriteString(fc.TotalDebitEntryDollarAmountInFileField()) + buf.WriteString(fc.TotalCreditEntryDollarAmountInFileField()) + buf.WriteString(fc.reserved) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/fileHeader.go b/fileHeader.go index f5fec7d8c..d4e0d6f05 100644 --- a/fileHeader.go +++ b/fileHeader.go @@ -138,28 +138,27 @@ func (fh *FileHeader) Parse(record string) { // String writes the FileHeader struct to a 94 character string. func (fh *FileHeader) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v%v", - fh.recordType, - fh.priorityCode, - fh.ImmediateDestinationField(), - fh.ImmediateOriginField(), - fh.FileCreationDateField(), - fh.FileCreationTimeField(), - fh.FileIDModifier, - fh.recordSize, - fh.blockingFactor, - fh.formatCode, - fh.ImmediateDestinationNameField(), - fh.ImmediateOriginNameField(), - fh.ReferenceCodeField(), - ) - + var buf strings.Builder + buf.Grow(94) + buf.WriteString(fh.recordType) + buf.WriteString(fh.priorityCode) + buf.WriteString(fh.ImmediateDestinationField()) + buf.WriteString(fh.ImmediateOriginField()) + buf.WriteString(fh.FileCreationDateField()) + buf.WriteString(fh.FileCreationTimeField()) + buf.WriteString(fh.FileIDModifier) + buf.WriteString(fh.recordSize) + buf.WriteString(fh.blockingFactor) + buf.WriteString(fh.formatCode) + buf.WriteString(fh.ImmediateDestinationNameField()) + buf.WriteString(fh.ImmediateOriginNameField()) + buf.WriteString(fh.ReferenceCodeField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated // The first error encountered is returned and stops the parsing. func (fh *FileHeader) Validate() error { - if err := fh.fieldInclusion(); err != nil { return err } diff --git a/iatBatchHeader.go b/iatBatchHeader.go index 22d86253f..4471b148f 100644 --- a/iatBatchHeader.go +++ b/iatBatchHeader.go @@ -222,25 +222,26 @@ func (iatBh *IATBatchHeader) Parse(record string) { // String writes the BatchHeader struct to a 94 character string. func (iatBh *IATBatchHeader) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v%v%v%v%v%v", - iatBh.recordType, - iatBh.ServiceClassCode, - iatBh.IATIndicatorField(), - iatBh.ForeignExchangeIndicatorField(), - iatBh.ForeignExchangeReferenceIndicatorField(), - iatBh.ForeignExchangeReferenceField(), - iatBh.ISODestinationCountryCodeField(), - iatBh.OriginatorIdentificationField(), - iatBh.StandardEntryClassCode, - iatBh.CompanyEntryDescriptionField(), - iatBh.ISOOriginatingCurrencyCodeField(), - iatBh.ISODestinationCurrencyCodeField(), - iatBh.EffectiveEntryDateField(), - iatBh.settlementDateField(), - iatBh.OriginatorStatusCode, - iatBh.ODFIIdentificationField(), - iatBh.BatchNumberField(), - ) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(iatBh.recordType) + buf.WriteString(string(iatBh.ServiceClassCode)) + buf.WriteString(iatBh.IATIndicatorField()) + buf.WriteString(iatBh.ForeignExchangeIndicatorField()) + buf.WriteString(iatBh.ForeignExchangeReferenceIndicatorField()) + buf.WriteString(iatBh.ForeignExchangeReferenceField()) + buf.WriteString(iatBh.ISODestinationCountryCodeField()) + buf.WriteString(iatBh.OriginatorIdentificationField()) + buf.WriteString(iatBh.StandardEntryClassCode) + buf.WriteString(iatBh.CompanyEntryDescriptionField()) + buf.WriteString(iatBh.ISOOriginatingCurrencyCodeField()) + buf.WriteString(iatBh.ISODestinationCurrencyCodeField()) + buf.WriteString(iatBh.EffectiveEntryDateField()) + buf.WriteString(iatBh.settlementDateField()) + buf.WriteString(string(iatBh.OriginatorStatusCode)) + buf.WriteString(iatBh.ODFIIdentificationField()) + buf.WriteString(iatBh.BatchNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 7621dba52..65fd411a3 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -7,6 +7,7 @@ package ach import ( "fmt" "strconv" + "strings" ) // IATEntryDetail contains the actual transaction data for an individual entry. @@ -151,20 +152,22 @@ func (ed *IATEntryDetail) Parse(record string) { // String writes the EntryDetail struct to a 94 character string. func (ed *IATEntryDetail) String() string { - return fmt.Sprintf("%v%v%v%v%v%v%v%v%v%v%v%v%v", - ed.recordType, - ed.TransactionCode, - ed.RDFIIdentificationField(), - ed.CheckDigit, - ed.AddendaRecordsField(), - ed.reservedField(), - ed.AmountField(), - ed.DFIAccountNumberField(), - ed.reservedTwoField(), - ed.OFACSreeningIndicatorField(), - ed.SecondaryOFACSreeningIndicatorField(), - ed.AddendaRecordIndicator, - ed.TraceNumberField()) + var buf strings.Builder + buf.Grow(94) + buf.WriteString(ed.recordType) + buf.WriteString(string(ed.TransactionCode)) + buf.WriteString(ed.RDFIIdentificationField()) + buf.WriteString(ed.CheckDigit) + buf.WriteString(ed.AddendaRecordsField()) + buf.WriteString(ed.reservedField()) + buf.WriteString(ed.AmountField()) + buf.WriteString(ed.DFIAccountNumberField()) + buf.WriteString(ed.reservedTwoField()) + buf.WriteString(ed.OFACSreeningIndicatorField()) + buf.WriteString(ed.SecondaryOFACSreeningIndicatorField()) + buf.WriteString(string(ed.AddendaRecordIndicator)) + buf.WriteString(ed.TraceNumberField()) + return buf.String() } // Validate performs NACHA format rule checks on the record and returns an error if not Validated From 264805c90498d8b26d39cea7defb1fa9ab1d9cb5 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 14 Aug 2018 19:54:42 -0500 Subject: [PATCH 0360/1694] require Go 1.10+ run 1.11rc1 in CI --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 163aba0b0..f1224c1af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go sudo: false go: -- 1.9.x - 1.10.x +- 1.11rc1 env: global: secure: QPkcX77j8QEqTwOYyLGItqvxYwE6Na5WaSZWjmhp48OlxYatWRHxJBwcFYSn1OWD5FMn+3oW39fHknReIxtrnhXMaNvI7x3/0gy4zujD/xZ2xAg7NsQ+l5buvEFO8/LEwwo0fp4knItFcBv8xH/ziJBJyXvgfMtj7Is4Q/pB1p6pWDdVy1vtAj3zH02bcqh1yXXS3HvcD8UhTszfU017gVNXDN1ow0rp1L3ainr3btrVK9izUxZfKvb7PlWJO1ogah7xNr/dIOJLsx2SfKgzKp+3H28L2WegtbzON74Op4jXvRywCwqjmUt/nwJ/Y9anunMNHT136h+ye4ziG1i/VdbWq0Q4PopQ8yYqinujG7SjfQio+wNCV2cwc2r/WjNBjbH0N9/Pflogq3RHvgy/9VtPif1tY+RrZCSntohoEZbYpVcFQFE1xDyf6xq/uLxVeEcCU33gqq7cKEfpcUgyCITa+yCPfBdtgkLBJ8h7Sew1j08D1kTKUW6g3D1epmwlCh/Z16oHG5VwSnCLGDjJy8wm/hQk1i/g7qeP7g24CfNzffzlFBCy88HhjzmrhUpcaTyfVVDf4h8wK6Zu/J3dHjHXQYwfiQRqpMa+2DYyjGgZhniccuh4GWolGZauDQdmO9SD4Ugyt9PEMk02i32ax3A4XE/Q6VNOam+qszviX3Q= From 5a2ad09287effb3df61ff0197a37c6ec0e7b84ee Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 15:17:31 -0500 Subject: [PATCH 0361/1694] doc: mention Go 1.10+ requirement --- README.md | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 021d859f7..da1d172ae 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Package 'moov-io/ach' implements a file reader and writer for parsing [ACH](http ACH is under active development but already in production for multiple companies. Please star the project if you are interested in its progress. -* Library currently supports the reading and writing +* Library currently supports the reading and writing * ARC (Accounts Receivable Entry) * BOC (Back Office Conversion) * CCD (Corporate credit or debit) @@ -38,7 +38,7 @@ ACH is under active development but already in production for multiple companies * Addenda Type Code 98 (NOC) * Addenda Type Code 99 (Return) * IAT - + ## Project Roadmap * Additional SEC codes will be added based on library users needs. Please open an issue with a valid test file. * Review the project issues for more detailed information @@ -63,25 +63,25 @@ if err != nil { if achFile.Validate(); err != nil { fmt.Printf("Could not validate entire read file: %v", err) } -// Check if any Notifications Of Change exist in the file +// Check if any Notifications Of Change exist in the file if len(achFile.NotificationOfChange) > 0 { for _, batch := range achFile.NotificationOfChange { a98 := batch.GetEntries()[0].Addendum[0].(*Addenda98) println(a98.CorrectedData) - } -} -// Check if any Return Entries exist in the file + } +} +// Check if any Return Entries exist in the file if len(achFile.ReturnEntries) > 0 { for _, batch := range achFile.ReturnEntries { aReturn := batch.GetEntries()[0].Addendum[0].(*Addenda99) println(aReturn.ReturnCode) - } + } } -``` +``` ### Create a file The following is based on [simple file creation](https://github.com/moov-io/ach/tree/master/test/simple-file-creation) - + ```go fh := ach.NewFileHeader() fh.ImmediateDestination = "9876543210" // A blank space followed by your ODFI's transit/routing number @@ -94,7 +94,7 @@ The following is based on [simple file creation](https://github.com/moov-io/ach/ file.SetHeader(fh) ``` -Explicitly create a PPD batch file. +Explicitly create a PPD batch file. Errors only if payment type is not supported @@ -153,7 +153,7 @@ To add one or more optional addenda records for an entry addenda := NewAddenda05() addenda.PaymentRelatedInformation = "Bonus pay for amazing work on #OSS" ``` -Add the addenda record to the detail entry +Add the addenda record to the detail entry ```go entry.AddAddenda(addenda) @@ -249,7 +249,7 @@ w.Flush() } ``` -Which will generate a well formed ACH flat file. +Which will generate a well formed ACH flat file. ```text 101 210000890 1234567891708290000A094101Your Bank Your Company #00000A1 @@ -261,10 +261,10 @@ Which will generate a well formed ACH flat file. 6221020010175343121 0000000799#123456 Wade Arnold R 1234567890000001 705Monthly Membership Subscription 00010000001 82200000020010200101000000000000000000000799123456789 234567890000002 -9000002000001000000040020400202000000017500000000000799 +9000002000001000000040020400202000000017500000000000799 ``` -### Create an IAT file +### Create an IAT file ```go file := NewFile().SetHeader(mockFileHeader()) @@ -337,14 +337,14 @@ Which will generate a well formed ACH flat file. w := NewWriter(os.Stdout) if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) - } + } w.Flush() ``` This will generate a well formed flat IAT ACH file ```text -101 987654321 1234567891807200000A094101Federal Reserve Bank My Bank Name +101 987654321 1234567891807200000A094101Federal Reserve Bank My Bank Name 5220 FF3 US123456789 IATTRADEPAYMTCADUSD010101 0231380100000001 6221210428820007 0000100000123456789 1231380100000001 710ANN000000000000100000928383-23938 BEK Enterprises 0000001 @@ -379,25 +379,27 @@ This will generate a well formed flat IAT ACH file 718Bank of Turkey 0112312345678910 TR 00040000001 718Bank of United Kingdom 011234567890123456789012345678901234GB 00050000001 82200000150012104288000000002000000000000000 231380100000002 -9000002000004000000300024208576000000002000000000100000 +9000002000004000000300024208576000000002000000000100000 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 ``` -# Getting Help +# Getting Help - channel | info + channel | info ------- | ------- Google Group [moov-users](https://groups.google.com/forum/#!forum/moov-users)| The Moov users Google group is for contributors other people contributing to the Moov project. You can join them without a google account by sending an email to [moov-users+subscribe@googlegroups.com](mailto:moov-users+subscribe@googlegroups.com). After receiving the join-request message, you can simply reply to that to confirm the subscription. Twitter [@moov_io](https://twitter.com/moov_io) | You can follow Moov.IO's Twitter feed to get updates on our project(s). You can also tweet us questions or just share blogs or stories. -[GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. -[moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. +[GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. +[moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. + +## Contributing -## Contributing +Yes please! Please review our [Contributing guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) to get started! -Yes please! Please review our [Contributing guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) to get started! +Note: This project requires Go 1.10 or higher to compile. ## License From 087d3e8af78114d4357da687d1f660a32e4d5788 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 17:02:12 -0500 Subject: [PATCH 0362/1694] doc: link to docs.moov.io --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index da1d172ae..7c8e427f9 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ moov-io/ach [![Apache 2 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/moov-io/ach/master/LICENSE) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoov-io%2Fach.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoov-io%2Fach?ref=badge_shield) +Package `github.com/moov-io/ach` implements a file reader and writer for parsing [ACH](https://en.wikipedia.org/wiki/Automated_Clearing_House) Automated Clearing House files. ACH is the primary method of electronic money movement throughout the United States. -Package 'moov-io/ach' implements a file reader and writer for parsing [ACH](https://en.wikipedia.org/wiki/Automated_Clearing_House -) Automated Clearing House files. ACH is the primary method of electronic money movement throughout the United States. +Docs: [docs.moov.io](http://docs.moov.io/en/latest/) ## Project Status @@ -390,6 +390,7 @@ This will generate a well formed flat IAT ACH file channel | info ------- | ------- + [Project Documentation](http://docs.moov.io/en/latest/) | Our project documentation available online. Google Group [moov-users](https://groups.google.com/forum/#!forum/moov-users)| The Moov users Google group is for contributors other people contributing to the Moov project. You can join them without a google account by sending an email to [moov-users+subscribe@googlegroups.com](mailto:moov-users+subscribe@googlegroups.com). After receiving the join-request message, you can simply reply to that to confirm the subscription. Twitter [@moov_io](https://twitter.com/moov_io) | You can follow Moov.IO's Twitter feed to get updates on our project(s). You can also tweet us questions or just share blogs or stories. [GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. From a6b8ad9346056c954a65ccf6819bd16fe593eb8c Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 17:08:19 -0500 Subject: [PATCH 0363/1694] doc: mention supported platforms --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da1d172ae..d43ff87f2 100644 --- a/README.md +++ b/README.md @@ -386,7 +386,7 @@ This will generate a well formed flat IAT ACH file 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 ``` -# Getting Help +## Getting Help channel | info ------- | ------- @@ -395,6 +395,13 @@ Twitter [@moov_io](https://twitter.com/moov_io) | You can follow Moov.IO's Twitt [GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. [moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. +## Supported and Tested Platforms + +- 64-bit Linux (Ubuntu, Debian), macOS, and Windows +- Rasberry Pi + +Note: 32-bit platforms have known issues and is not supported. + ## Contributing Yes please! Please review our [Contributing guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) to get started! From 3c321ff4bb51116088f797d4e7c56263433fab49 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 17:09:22 -0500 Subject: [PATCH 0364/1694] run travis-ci osx build Issue: https://github.com/moov-io/ach/issues/238 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index f1224c1af..937bd0c60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,12 @@ language: go sudo: false +os: +- linux +- osx go: - 1.10.x - 1.11rc1 +osx_image: xcode9.1 env: global: secure: QPkcX77j8QEqTwOYyLGItqvxYwE6Na5WaSZWjmhp48OlxYatWRHxJBwcFYSn1OWD5FMn+3oW39fHknReIxtrnhXMaNvI7x3/0gy4zujD/xZ2xAg7NsQ+l5buvEFO8/LEwwo0fp4knItFcBv8xH/ziJBJyXvgfMtj7Is4Q/pB1p6pWDdVy1vtAj3zH02bcqh1yXXS3HvcD8UhTszfU017gVNXDN1ow0rp1L3ainr3btrVK9izUxZfKvb7PlWJO1ogah7xNr/dIOJLsx2SfKgzKp+3H28L2WegtbzON74Op4jXvRywCwqjmUt/nwJ/Y9anunMNHT136h+ye4ziG1i/VdbWq0Q4PopQ8yYqinujG7SjfQio+wNCV2cwc2r/WjNBjbH0N9/Pflogq3RHvgy/9VtPif1tY+RrZCSntohoEZbYpVcFQFE1xDyf6xq/uLxVeEcCU33gqq7cKEfpcUgyCITa+yCPfBdtgkLBJ8h7Sew1j08D1kTKUW6g3D1epmwlCh/Z16oHG5VwSnCLGDjJy8wm/hQk1i/g7qeP7g24CfNzffzlFBCy88HhjzmrhUpcaTyfVVDf4h8wK6Zu/J3dHjHXQYwfiQRqpMa+2DYyjGgZhniccuh4GWolGZauDQdmO9SD4Ugyt9PEMk02i32ax3A4XE/Q6VNOam+qszviX3Q= From 8d774d14108ed757bdf84ea0b15a10a259a92f6f Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 17:12:20 -0500 Subject: [PATCH 0365/1694] run tests on windows via appveyor Issue: https://github.com/moov-io/ach/issues/238 --- .appveyor.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000..9594631c9 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,22 @@ +image: + - Visual Studio 2015 + +build: off + +clone_folder: c:\gopath\src\github.com\moov-io/ach + +environment: + GOPATH: c:\gopath + CGO_ENABLED: '1' + GOOS: windows + GOARCH: amd64 + DEBUG: true + +stack: go 1.10 + +before_test: + - go vet ./... + - go fmt ./... + +test_script: + - go test ./... From d5ca3986594940eace368b448e4f7eeede1fc695 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 17:36:21 -0500 Subject: [PATCH 0366/1694] fix import path in tests --- test/ach-cie-write/main.go | 2 +- test/ach-ctx-write/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/ach-cie-write/main.go b/test/ach-cie-write/main.go index fc9f8ffc6..cf35380fe 100644 --- a/test/ach-cie-write/main.go +++ b/test/ach-cie-write/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "log" "os" "time" diff --git a/test/ach-ctx-write/main.go b/test/ach-ctx-write/main.go index ab73221d8..a27f71aa9 100644 --- a/test/ach-ctx-write/main.go +++ b/test/ach-ctx-write/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/bkmoovio/ach" + "github.com/moov-io/ach" "log" "os" "time" From f71cfca321867c9c20b1287c0f26190e7431e403 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 17:41:07 -0500 Subject: [PATCH 0367/1694] don't build on Go 1.11rc1 yet --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 937bd0c60..5ada7fef1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ os: - osx go: - 1.10.x -- 1.11rc1 osx_image: xcode9.1 env: global: From 98ae97d9e9d188336c8245d2a5580a4a54a75e58 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Wed, 15 Aug 2018 17:46:14 -0500 Subject: [PATCH 0368/1694] on String() methods, properly format strings string() in Go will print based on the integer as a character code rather than printing the integer. --- batchControl.go | 2 +- batchHeader.go | 4 ++-- entryDetail.go | 4 ++-- iatBatchHeader.go | 4 ++-- iatEntryDetail.go | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/batchControl.go b/batchControl.go index 95dad91cd..ade90bff5 100644 --- a/batchControl.go +++ b/batchControl.go @@ -112,7 +112,7 @@ func (bc *BatchControl) String() string { var buf strings.Builder buf.Grow(94) buf.WriteString(bc.recordType) - buf.WriteString(string(bc.ServiceClassCode)) + buf.WriteString(fmt.Sprintf("%v", bc.ServiceClassCode)) buf.WriteString(bc.EntryAddendaCountField()) buf.WriteString(bc.EntryHashField()) buf.WriteString(bc.TotalDebitEntryDollarAmountField()) diff --git a/batchHeader.go b/batchHeader.go index cf5716e30..8769741e7 100644 --- a/batchHeader.go +++ b/batchHeader.go @@ -159,7 +159,7 @@ func (bh *BatchHeader) String() string { var buf strings.Builder buf.Grow(94) buf.WriteString(bh.recordType) - buf.WriteString(string(bh.ServiceClassCode)) + buf.WriteString(fmt.Sprintf("%v", bh.ServiceClassCode)) buf.WriteString(bh.CompanyNameField()) buf.WriteString(bh.CompanyDiscretionaryDataField()) buf.WriteString(bh.CompanyIdentificationField()) @@ -168,7 +168,7 @@ func (bh *BatchHeader) String() string { buf.WriteString(bh.CompanyDescriptiveDateField()) buf.WriteString(bh.EffectiveEntryDateField()) buf.WriteString(bh.settlementDateField()) - buf.WriteString(string(bh.OriginatorStatusCode)) + buf.WriteString(fmt.Sprintf("%v", bh.OriginatorStatusCode)) buf.WriteString(bh.ODFIIdentificationField()) buf.WriteString(bh.BatchNumberField()) return buf.String() diff --git a/entryDetail.go b/entryDetail.go index 8db19817d..0e3ca20ea 100644 --- a/entryDetail.go +++ b/entryDetail.go @@ -129,7 +129,7 @@ func (ed *EntryDetail) String() string { var buf strings.Builder buf.Grow(94) buf.WriteString(ed.recordType) - buf.WriteString(string(ed.TransactionCode)) + buf.WriteString(fmt.Sprintf("%v", ed.TransactionCode)) buf.WriteString(ed.RDFIIdentificationField()) buf.WriteString(ed.CheckDigit) buf.WriteString(ed.DFIAccountNumberField()) @@ -137,7 +137,7 @@ func (ed *EntryDetail) String() string { buf.WriteString(ed.IdentificationNumberField()) buf.WriteString(ed.IndividualNameField()) buf.WriteString(ed.DiscretionaryDataField()) - buf.WriteString(string(ed.AddendaRecordIndicator)) + buf.WriteString(fmt.Sprintf("%v", ed.AddendaRecordIndicator)) buf.WriteString(ed.TraceNumberField()) return buf.String() } diff --git a/iatBatchHeader.go b/iatBatchHeader.go index 4471b148f..009c159a8 100644 --- a/iatBatchHeader.go +++ b/iatBatchHeader.go @@ -225,7 +225,7 @@ func (iatBh *IATBatchHeader) String() string { var buf strings.Builder buf.Grow(94) buf.WriteString(iatBh.recordType) - buf.WriteString(string(iatBh.ServiceClassCode)) + buf.WriteString(fmt.Sprintf("%v", iatBh.ServiceClassCode)) buf.WriteString(iatBh.IATIndicatorField()) buf.WriteString(iatBh.ForeignExchangeIndicatorField()) buf.WriteString(iatBh.ForeignExchangeReferenceIndicatorField()) @@ -238,7 +238,7 @@ func (iatBh *IATBatchHeader) String() string { buf.WriteString(iatBh.ISODestinationCurrencyCodeField()) buf.WriteString(iatBh.EffectiveEntryDateField()) buf.WriteString(iatBh.settlementDateField()) - buf.WriteString(string(iatBh.OriginatorStatusCode)) + buf.WriteString(fmt.Sprintf("%v", iatBh.OriginatorStatusCode)) buf.WriteString(iatBh.ODFIIdentificationField()) buf.WriteString(iatBh.BatchNumberField()) return buf.String() diff --git a/iatEntryDetail.go b/iatEntryDetail.go index 65fd411a3..55aed7fad 100644 --- a/iatEntryDetail.go +++ b/iatEntryDetail.go @@ -155,7 +155,7 @@ func (ed *IATEntryDetail) String() string { var buf strings.Builder buf.Grow(94) buf.WriteString(ed.recordType) - buf.WriteString(string(ed.TransactionCode)) + buf.WriteString(fmt.Sprintf("%v", ed.TransactionCode)) buf.WriteString(ed.RDFIIdentificationField()) buf.WriteString(ed.CheckDigit) buf.WriteString(ed.AddendaRecordsField()) @@ -165,7 +165,7 @@ func (ed *IATEntryDetail) String() string { buf.WriteString(ed.reservedTwoField()) buf.WriteString(ed.OFACSreeningIndicatorField()) buf.WriteString(ed.SecondaryOFACSreeningIndicatorField()) - buf.WriteString(string(ed.AddendaRecordIndicator)) + buf.WriteString(fmt.Sprintf("%v", ed.AddendaRecordIndicator)) buf.WriteString(ed.TraceNumberField()) return buf.String() } From 2780cbd0eab09f462b797fecc0832570393d8b24 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 15 Aug 2018 21:27:46 -0400 Subject: [PATCH 0369/1694] IAT README Updates IAT README Updates --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 94948f42f..9b23cf2b9 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ ACH is under active development but already in production for multiple companies * CIE (Customer-Initiated Entry) * COR (Automated Notification of Change(NOC)) * CTX (Corporate Trade Exchange) + * IAT * POP (Point of Purchase) * POS (Point of Sale) * PPD (Prearranged payment and deposits) @@ -35,9 +36,14 @@ ACH is under active development but already in production for multiple companies * Addenda Type Code 10 (IAT) * Addenda Type Code 11 (IAT) * Addenda Type Code 12 (IAT) + * Addenda Type Code 13 (IAT) + * Addenda Type Code 14 (IAT) + * Addenda Type Code 15 (IAT) + * Addenda Type Code 16 (IAT) + * Addenda Type Code 17 (IAT Optional) + * Addenda Type Code 18 (IAT Optional) * Addenda Type Code 98 (NOC) * Addenda Type Code 99 (Return) - * IAT ## Project Roadmap * Additional SEC codes will be added based on library users needs. Please open an issue with a valid test file. From e3de24cc650f2ff493b43afc7584a622673bf123 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 15 Aug 2018 21:29:06 -0400 Subject: [PATCH 0370/1694] IAT README Update IAT README Update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b23cf2b9..f6ab879c1 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ACH is under active development but already in production for multiple companies * CIE (Customer-Initiated Entry) * COR (Automated Notification of Change(NOC)) * CTX (Corporate Trade Exchange) - * IAT + * IAT (International ACH Transactions) * POP (Point of Purchase) * POS (Point of Sale) * PPD (Prearranged payment and deposits) From c7bce4a76784671a42541bebd9d4063b8d567836 Mon Sep 17 00:00:00 2001 From: Wade Arnold Date: Mon, 20 Aug 2018 10:11:07 -0500 Subject: [PATCH 0371/1694] Slack invite link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f6ab879c1..590bbf680 100644 --- a/README.md +++ b/README.md @@ -400,7 +400,7 @@ This will generate a well formed flat IAT ACH file Google Group [moov-users](https://groups.google.com/forum/#!forum/moov-users)| The Moov users Google group is for contributors other people contributing to the Moov project. You can join them without a google account by sending an email to [moov-users+subscribe@googlegroups.com](mailto:moov-users+subscribe@googlegroups.com). After receiving the join-request message, you can simply reply to that to confirm the subscription. Twitter [@moov_io](https://twitter.com/moov_io) | You can follow Moov.IO's Twitter feed to get updates on our project(s). You can also tweet us questions or just share blogs or stories. [GitHub Issue](https://github.com/moov-io) | If you are able to reproduce an problem please open a GitHub Issue under the specific project that caused the error. -[moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. +[moov-io slack](http://moov-io.slack.com/) | Join our slack channel to have an interactive discussion about the development of the project. [Request and invote to the slack channel](https://join.slack.com/t/moov-io/shared_invite/enQtNDE5NzIwNTYxODEwLTRkYTcyZDI5ZTlkZWRjMzlhMWVhMGZlOTZiOTk4MmM3MmRhZDY4OTJiMDVjOTE2MGEyNWYzYzY1MGMyMThiZjg) ## Supported and Tested Platforms From af255bca1b50c975dc326eb87b10df976b125275 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 21 Aug 2018 14:35:19 -0500 Subject: [PATCH 0372/1694] validate addenda99 typeCode --- addenda99.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addenda99.go b/addenda99.go index 05bcae96c..29707e1a6 100644 --- a/addenda99.go +++ b/addenda99.go @@ -113,12 +113,16 @@ func (Addenda99 *Addenda99) String() string { // Validate verifies NACHA rules for Addenda99 func (Addenda99 *Addenda99) Validate() error { - if Addenda99.recordType != "7" { msg := fmt.Sprintf(msgRecordType, 7) return &FieldError{FieldName: "recordType", Value: Addenda99.recordType, Msg: msg} } - // @TODO Type Code should be 99. + if Addenda99.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: Addenda99.typeCode, Msg: msgFieldInclusion} + } + if Addenda99.typeCode != "99" { + return &FieldError{FieldName: "TypeCode", Value: Addenda99.typeCode, Msg: msgAddendaTypeCode} + } _, ok := returnCodeDict[Addenda99.ReturnCode] if !ok { From 1cbf3d6f60e69c7ee2f7670023dc46494a5a441d Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 21 Aug 2018 21:26:51 -0400 Subject: [PATCH 0373/1694] Addenda Type Code checks and tests Addenda Type Code checks and tests --- addenda05.go | 4 ++++ addenda05_test.go | 28 ++++++++++++++++++++++++ addenda98.go | 3 +++ addenda98_test.go | 28 ++++++++++++++++++++++++ addenda99_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+) diff --git a/addenda05.go b/addenda05.go index ed1263ce9..3ca62ba1b 100644 --- a/addenda05.go +++ b/addenda05.go @@ -83,6 +83,10 @@ func (addenda05 *Addenda05) Validate() error { if err := addenda05.isTypeCode(addenda05.typeCode); err != nil { return &FieldError{FieldName: "TypeCode", Value: addenda05.typeCode, Msg: err.Error()} } + // Type Code must be 05 + if addenda05.typeCode != "05" { + return &FieldError{FieldName: "TypeCode", Value: addenda05.typeCode, Msg: msgAddendaTypeCode} + } if err := addenda05.isAlphanumeric(addenda05.PaymentRelatedInformation); err != nil { return &FieldError{FieldName: "PaymentRelatedInformation", Value: addenda05.PaymentRelatedInformation, Msg: err.Error()} } diff --git a/addenda05_test.go b/addenda05_test.go index 7e489c401..578459d99 100644 --- a/addenda05_test.go +++ b/addenda05_test.go @@ -186,3 +186,31 @@ func BenchmarkAddenda05TypeCodeNil(b *testing.B) { testAddenda05TypeCodeNil(b) } } + +// testAddenda05TypeCode05 TypeCode is 05 +func testAddenda05TypeCode05(t testing.TB) { + addenda05 := mockAddenda05() + addenda05.typeCode = "99" + if err := addenda05.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda05TypeCode05 tests TypeCode is 05 +func TestAddenda05TypeCode05(t *testing.T) { + testAddenda05TypeCode05(t) +} + +// BenchmarkAddenda05TypeCode05 benchmarks TypeCode is 05 +func BenchmarkAddenda05TypeCode05(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda05TypeCode05(b) + } +} diff --git a/addenda98.go b/addenda98.go index 199cc8b79..2d084c8eb 100644 --- a/addenda98.go +++ b/addenda98.go @@ -106,6 +106,9 @@ func (addenda98 *Addenda98) Validate() error { msg := fmt.Sprintf(msgRecordType, 7) return &FieldError{FieldName: "recordType", Value: addenda98.recordType, Msg: msg} } + if addenda98.typeCode == "" { + return &FieldError{FieldName: "TypeCode", Value: addenda98.typeCode, Msg: msgFieldInclusion} + } // Type Code must be 98 if addenda98.typeCode != "98" { return &FieldError{FieldName: "TypeCode", Value: addenda98.typeCode, Msg: msgAddendaTypeCode} diff --git a/addenda98_test.go b/addenda98_test.go index 86c5d6310..2cded9cbd 100644 --- a/addenda98_test.go +++ b/addenda98_test.go @@ -278,3 +278,31 @@ func BenchmarkAddenda98TraceNumberField(b *testing.B) { testAddenda98TraceNumberField(b) } } + +// testAddenda98TypeCodeNil validates TypeCode is "" +func testAddenda98TypeCodeNil(t testing.TB) { + addenda98 := mockAddenda98() + addenda98.typeCode = "" + if err := addenda98.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda98TypeCodeES tests TypeCode is "" +func TestAddenda98TypeCodeNil(t *testing.T) { + testAddenda98TypeCodeNil(t) +} + +// BenchmarkAddenda98TypeCodeNil benchmarks TypeCode is "" +func BenchmarkAddenda98TypeCodeNil(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda98TypeCodeNil(b) + } +} diff --git a/addenda99_test.go b/addenda99_test.go index 0b6340e79..df2f486d9 100644 --- a/addenda99_test.go +++ b/addenda99_test.go @@ -281,3 +281,59 @@ func BenchmarkAddenda99ValidRecordType(b *testing.B) { testAddenda99ValidRecordType(b) } } + +// testAddenda99TypeCode99 TypeCode is 99 +func testAddenda99TypeCode99(t testing.TB) { + addenda99 := mockAddenda99() + addenda99.typeCode = "05" + if err := addenda99.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda99TypeCode99 tests TypeCode is 99 +func TestAddenda99TypeCode99(t *testing.T) { + testAddenda99TypeCode99(t) +} + +// BenchmarkAddenda99TypeCode99 benchmarks TypeCode is 99 +func BenchmarkAddenda99TypeCode99(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99TypeCode99(b) + } +} + +// testAddenda99TypeCodeNil validates TypeCode is "" +func testAddenda99TypeCodeNil(t testing.TB) { + addenda99 := mockAddenda99() + addenda99.typeCode = "" + if err := addenda99.Validate(); err != nil { + if e, ok := err.(*FieldError); ok { + if e.FieldName != "TypeCode" { + t.Errorf("%T: %s", err, err) + } + } else { + t.Errorf("%T: %s", err, err) + } + } +} + +// TestAddenda99TypeCodeES tests TypeCode is "" +func TestAddenda99TypeCodeNil(t *testing.T) { + testAddenda99TypeCodeNil(t) +} + +// BenchmarkAddenda99TypeCodeNil benchmarks TypeCode is "" +func BenchmarkAddenda99TypeCodeNil(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testAddenda99TypeCodeNil(b) + } +} From b1dc4a898a9e722dccf4ded16cd1a82aefb8f457 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 28 Aug 2018 20:53:06 -0500 Subject: [PATCH 0374/1694] write test for empty file Read with error string check I didn't see an explicit test for calling Read on an empty io.Reader This matches the exact error string (to grab "file headers") to make sure that's what is returned to callers in the future. --- reader_test.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/reader_test.go b/reader_test.go index 4e51a9807..115643ea6 100644 --- a/reader_test.go +++ b/reader_test.go @@ -220,11 +220,42 @@ func BenchmarkTwoFileControls(b *testing.B) { } } +// testFileLineEmpty verifies empty files fail to parse +func testFileLineEmpty(t testing.TB) { + line := "" + r := NewReader(strings.NewReader(line)) + _, err := r.Read() + + if p, ok := err.(*ParseError); ok { + if e, ok := p.Err.(*FileError); ok { + if e.Msg != "none or more than one file headers exists" { // from msgFileHeader + t.Errorf("%#v", e) + } + } else { + t.Errorf("%T: %s", e, e) + } + } +} + +// TestFileLineEmpty tests validating file line is short +func TestFileLineEmpty(t *testing.T) { + testFileLineEmpty(t) +} + +// BenchmarkFileLineEmpty benchmarks validating file line is short +func BenchmarkFileLineEmpty(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + testFileLineEmpty(b) + } +} + // testFileLineShort validates file line is short func testFileLineShort(t testing.TB) { - var line = "1 line is only 70 characters ........................................!" + line := "1 line is only 70 characters ........................................!" r := NewReader(strings.NewReader(line)) _, err := r.Read() + if p, ok := err.(*ParseError); ok { if e, ok := p.Err.(*FileError); ok { if e.FieldName != "RecordLength" { From d2f5edeba1c021c1aefabdd67a8b837e7306efa6 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 28 Aug 2018 20:55:37 -0500 Subject: [PATCH 0375/1694] misc spelling fixes --- reader.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reader.go b/reader.go index 8e0024a55..7a480d2ee 100644 --- a/reader.go +++ b/reader.go @@ -100,12 +100,12 @@ func (r *Reader) Read() (File, error) { } } if (FileHeader{}) == r.File.Header { - // Their must be at least one File Header + // There must be at least one File Header r.recordName = "FileHeader" return r.File, r.error(&FileError{Msg: msgFileHeader}) } if (FileControl{}) == r.File.Control { - // Their must be at least one File Control + // There must be at least one File Control r.recordName = "FileControl" return r.File, r.error(&FileError{Msg: msgFileControl}) } @@ -243,7 +243,7 @@ func (r *Reader) parseEDAddenda() error { func (r *Reader) parseFileHeader() error { r.recordName = "FileHeader" if (FileHeader{}) != r.File.Header { - // Their can only be one File Header per File exit + // There can only be one File Header per File exit r.error(&FileError{Msg: msgFileHeader}) } r.File.Header.Parse(r.line) From 995f2d97280c2298619af9bb229b99ac39d448a1 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Wed, 29 Aug 2018 08:57:15 -0400 Subject: [PATCH 0376/1694] Reader comment removal Reader comment removal --- reader.go | 18 ------------------ reader_test.go | 7 +++---- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/reader.go b/reader.go index 7a480d2ee..096c9aef0 100644 --- a/reader.go +++ b/reader.go @@ -214,7 +214,6 @@ func (r *Reader) parseED() error { // parseEd parses determines whether to parse an IATEntryDetail Addenda or EntryDetail Addenda func (r *Reader) parseEDAddenda() error { - if r.currentBatch != nil { if err := r.parseAddenda(); err != nil { return err @@ -224,18 +223,6 @@ func (r *Reader) parseEDAddenda() error { return err } } - - /* switch r.line[1:3] { - //ToDo; What to do about 98 and 99 ? - case "10", "11", "12", "13", "14", "15", "16", "17", "18": - if err := r.parseIATAddenda(); err != nil { - return err - } - default: - if err := r.parseAddenda(); err != nil { - return err - } - }*/ return nil } @@ -345,7 +332,6 @@ func (r *Reader) parseAddenda() error { msg := fmt.Sprint(msgBatchAddendaIndicator) return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) } - return nil } @@ -405,7 +391,6 @@ func (r *Reader) parseIATBatchHeader() error { // Passing BatchHeader into NewBatchIAT creates a Batcher of IAT SEC code type. iatBatch := NewIATBatch(bh) - r.addIATCurrentBatch(iatBatch) return nil @@ -451,7 +436,6 @@ func (r *Reader) parseIATAddenda() error { msg := fmt.Sprint(msgIATBatchAddendaIndicator) return r.error(&FileError{FieldName: "AddendaRecordIndicator", Msg: msg}) } - return nil } @@ -543,7 +527,6 @@ func (r *Reader) mandatoryOptionalIATAddenda(entryIndex int) error { } r.IATCurrentBatch.Entries[entryIndex].AddIATAddenda(addenda18) } - return nil } @@ -556,6 +539,5 @@ func (r *Reader) returnIATAddenda(entryIndex int) error { return err } r.IATCurrentBatch.Entries[entryIndex].AddIATAddenda(addenda99) - return nil } diff --git a/reader_test.go b/reader_test.go index 115643ea6..223596a29 100644 --- a/reader_test.go +++ b/reader_test.go @@ -225,10 +225,9 @@ func testFileLineEmpty(t testing.TB) { line := "" r := NewReader(strings.NewReader(line)) _, err := r.Read() - if p, ok := err.(*ParseError); ok { if e, ok := p.Err.(*FileError); ok { - if e.Msg != "none or more than one file headers exists" { // from msgFileHeader + if e.Msg != msgFileHeader { t.Errorf("%#v", e) } } else { @@ -237,12 +236,12 @@ func testFileLineEmpty(t testing.TB) { } } -// TestFileLineEmpty tests validating file line is short +// TestFileLineEmpty tests validating empty file fails to parse func TestFileLineEmpty(t *testing.T) { testFileLineEmpty(t) } -// BenchmarkFileLineEmpty benchmarks validating file line is short +// BenchmarkFileLineEmpty benchmarks validating empty file fails to parse func BenchmarkFileLineEmpty(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From c51ce9cd072b8f624cb89ca06f7e175b155239bc Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 4 Sep 2018 08:44:21 -0700 Subject: [PATCH 0377/1694] doc: mention additional git remotes --- CONTRIBUTING.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2110a230..02387196e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,28 @@ We use GitHub to manage reviews of pull requests. Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). * When in doubt follow the [Go Proverbs](https://go-proverbs.github.io/) + +## Getting the code + +We recommend using additional git remote's for pushing/pulling code. Go cares about where the `ach` project lives relative to `GOPATH`. + +To pull our source code run: + +``` +$ go get github.com/moov-io/ach +``` + +Then, add your (or another user's) fork. + +``` +$ cd $GOPATH/src/github.com/moov-io/ach + +$ git remote add $user git@github.com:$user/ach.git + +$ git fetch $user +``` + +Now, feel free to branch and push (`git push $user $branch`) to your remote and send us Pull Requests! ## Pull Requests From 13c662c361d3b03470aee1576ff8947136460a66 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 4 Sep 2018 09:31:17 -0700 Subject: [PATCH 0378/1694] server: fix import paths --- cmd/server/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index bedd426d4..986529564 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -8,8 +8,8 @@ import ( "os/signal" "syscall" - "github.com/wadearnold/ach/server" - "github.com/wadearnold/kit/log" + "github.com/moov-io/ach/server" + "github.com/moov-io/kit/log" ) /** From 595db2de4b2f5cf3b7a128cac7cea7e70028ebc8 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 4 Sep 2018 09:52:07 -0700 Subject: [PATCH 0379/1694] build: add go.mod Issue: https://github.com/moov-io/ach/issues/253 Issue: https://github.com/moov-io/ach/issues/257 --- .appveyor.yml | 2 +- .travis.yml | 2 +- README.md | 2 +- go.mod | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 go.mod diff --git a/.appveyor.yml b/.appveyor.yml index 9594631c9..9bdc1b1d7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -12,7 +12,7 @@ environment: GOARCH: amd64 DEBUG: true -stack: go 1.10 +stack: go 1.11 before_test: - go vet ./... diff --git a/.travis.yml b/.travis.yml index 5ada7fef1..0bb7f734c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: - linux - osx go: -- 1.10.x +- 1.11 osx_image: xcode9.1 env: global: diff --git a/README.md b/README.md index 590bbf680..46fd7e2bb 100644 --- a/README.md +++ b/README.md @@ -413,7 +413,7 @@ Note: 32-bit platforms have known issues and is not supported. Yes please! Please review our [Contributing guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) to get started! -Note: This project requires Go 1.10 or higher to compile. +Note: This project requires Go 1.11 or higher to compile. ## License diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..9180b9b20 --- /dev/null +++ b/go.mod @@ -0,0 +1 @@ +module github.com/moov-io/ach From ba07b9eb2a604f9e8144c1ad8fd2823253ebfa43 Mon Sep 17 00:00:00 2001 From: Adam Shannon Date: Tue, 4 Sep 2018 10:35:41 -0700 Subject: [PATCH 0380/1694] fixup uuid dep, add vendor/ --- cmd/server/main.go | 2 +- go.mod | 10 + go.sum | 14 + server/service.go | 4 +- vendor/github.com/go-kit/kit/LICENSE | 22 + vendor/github.com/go-kit/kit/endpoint/doc.go | 5 + .../go-kit/kit/endpoint/endpoint.go | 28 + vendor/github.com/go-kit/kit/log/README.md | 147 ++++ vendor/github.com/go-kit/kit/log/doc.go | 116 +++ .../github.com/go-kit/kit/log/json_logger.go | 89 ++ vendor/github.com/go-kit/kit/log/log.go | 135 ++++ .../go-kit/kit/log/logfmt_logger.go | 62 ++ .../github.com/go-kit/kit/log/nop_logger.go | 8 + vendor/github.com/go-kit/kit/log/stdlib.go | 116 +++ vendor/github.com/go-kit/kit/log/sync.go | 116 +++ vendor/github.com/go-kit/kit/log/value.go | 102 +++ .../go-kit/kit/transport/http/client.go | 181 +++++ .../go-kit/kit/transport/http/doc.go | 2 + .../kit/transport/http/encode_decode.go | 30 + .../transport/http/request_response_funcs.go | 133 +++ .../go-kit/kit/transport/http/server.go | 233 ++++++ vendor/github.com/go-logfmt/logfmt/.gitignore | 4 + .../github.com/go-logfmt/logfmt/.travis.yml | 15 + vendor/github.com/go-logfmt/logfmt/LICENSE | 22 + vendor/github.com/go-logfmt/logfmt/README.md | 33 + vendor/github.com/go-logfmt/logfmt/decode.go | 237 ++++++ vendor/github.com/go-logfmt/logfmt/doc.go | 6 + vendor/github.com/go-logfmt/logfmt/encode.go | 321 ++++++++ vendor/github.com/go-logfmt/logfmt/fuzz.go | 126 +++ .../github.com/go-logfmt/logfmt/jsonstring.go | 277 +++++++ vendor/github.com/go-stack/stack/.travis.yml | 15 + vendor/github.com/go-stack/stack/LICENSE.md | 21 + vendor/github.com/go-stack/stack/README.md | 38 + vendor/github.com/go-stack/stack/go.mod | 1 + vendor/github.com/go-stack/stack/stack.go | 400 +++++++++ vendor/github.com/gofrs/uuid/v3/.gitignore | 15 + vendor/github.com/gofrs/uuid/v3/.travis.yml | 22 + vendor/github.com/gofrs/uuid/v3/LICENSE | 20 + vendor/github.com/gofrs/uuid/v3/README.md | 101 +++ vendor/github.com/gofrs/uuid/v3/codec.go | 214 +++++ vendor/github.com/gofrs/uuid/v3/fuzz.go | 47 ++ vendor/github.com/gofrs/uuid/v3/generator.go | 299 +++++++ vendor/github.com/gofrs/uuid/v3/go.mod | 1 + vendor/github.com/gofrs/uuid/v3/sql.go | 105 +++ vendor/github.com/gofrs/uuid/v3/uuid.go | 189 +++++ vendor/github.com/gorilla/context/.travis.yml | 19 + vendor/github.com/gorilla/context/LICENSE | 27 + vendor/github.com/gorilla/context/README.md | 10 + vendor/github.com/gorilla/context/context.go | 143 ++++ vendor/github.com/gorilla/context/doc.go | 88 ++ vendor/github.com/gorilla/mux/.travis.yml | 23 + .../github.com/gorilla/mux/ISSUE_TEMPLATE.md | 11 + vendor/github.com/gorilla/mux/LICENSE | 27 + vendor/github.com/gorilla/mux/README.md | 649 +++++++++++++++ .../github.com/gorilla/mux/context_gorilla.go | 26 + .../github.com/gorilla/mux/context_native.go | 24 + vendor/github.com/gorilla/mux/doc.go | 306 +++++++ vendor/github.com/gorilla/mux/middleware.go | 72 ++ vendor/github.com/gorilla/mux/mux.go | 588 ++++++++++++++ vendor/github.com/gorilla/mux/regexp.go | 332 ++++++++ vendor/github.com/gorilla/mux/route.go | 763 ++++++++++++++++++ vendor/github.com/gorilla/mux/test_helpers.go | 19 + vendor/github.com/kr/logfmt/.gitignore | 3 + vendor/github.com/kr/logfmt/Readme | 12 + vendor/github.com/kr/logfmt/decode.go | 184 +++++ vendor/github.com/kr/logfmt/scanner.go | 149 ++++ vendor/github.com/kr/logfmt/unquote.go | 149 ++++ vendor/modules.txt | 16 + 68 files changed, 7721 insertions(+), 3 deletions(-) create mode 100644 go.sum create mode 100644 vendor/github.com/go-kit/kit/LICENSE create mode 100644 vendor/github.com/go-kit/kit/endpoint/doc.go create mode 100644 vendor/github.com/go-kit/kit/endpoint/endpoint.go create mode 100644 vendor/github.com/go-kit/kit/log/README.md create mode 100644 vendor/github.com/go-kit/kit/log/doc.go create mode 100644 vendor/github.com/go-kit/kit/log/json_logger.go create mode 100644 vendor/github.com/go-kit/kit/log/log.go create mode 100644 vendor/github.com/go-kit/kit/log/logfmt_logger.go create mode 100644 vendor/github.com/go-kit/kit/log/nop_logger.go create mode 100644 vendor/github.com/go-kit/kit/log/stdlib.go create mode 100644 vendor/github.com/go-kit/kit/log/sync.go create mode 100644 vendor/github.com/go-kit/kit/log/value.go create mode 100644 vendor/github.com/go-kit/kit/transport/http/client.go create mode 100644 vendor/github.com/go-kit/kit/transport/http/doc.go create mode 100644 vendor/github.com/go-kit/kit/transport/http/encode_decode.go create mode 100644 vendor/github.com/go-kit/kit/transport/http/request_response_funcs.go create mode 100644 vendor/github.com/go-kit/kit/transport/http/server.go create mode 100644 vendor/github.com/go-logfmt/logfmt/.gitignore create mode 100644 vendor/github.com/go-logfmt/logfmt/.travis.yml create mode 100644 vendor/github.com/go-logfmt/logfmt/LICENSE create mode 100644 vendor/github.com/go-logfmt/logfmt/README.md create mode 100644 vendor/github.com/go-logfmt/logfmt/decode.go create mode 100644 vendor/github.com/go-logfmt/logfmt/doc.go create mode 100644 vendor/github.com/go-logfmt/logfmt/encode.go create mode 100644 vendor/github.com/go-logfmt/logfmt/fuzz.go create mode 100644 vendor/github.com/go-logfmt/logfmt/jsonstring.go create mode 100644 vendor/github.com/go-stack/stack/.travis.yml create mode 100644 vendor/github.com/go-stack/stack/LICENSE.md create mode 100644 vendor/github.com/go-stack/stack/README.md create mode 100644 vendor/github.com/go-stack/stack/go.mod create mode 100644 vendor/github.com/go-stack/stack/stack.go create mode 100644 vendor/github.com/gofrs/uuid/v3/.gitignore create mode 100644 vendor/github.com/gofrs/uuid/v3/.travis.yml create mode 100644 vendor/github.com/gofrs/uuid/v3/LICENSE create mode 100644 vendor/github.com/gofrs/uuid/v3/README.md create mode 100644 vendor/github.com/gofrs/uuid/v3/codec.go create mode 100644 vendor/github.com/gofrs/uuid/v3/fuzz.go create mode 100644 vendor/github.com/gofrs/uuid/v3/generator.go create mode 100644 vendor/github.com/gofrs/uuid/v3/go.mod create mode 100644 vendor/github.com/gofrs/uuid/v3/sql.go create mode 100644 vendor/github.com/gofrs/uuid/v3/uuid.go create mode 100644 vendor/github.com/gorilla/context/.travis.yml create mode 100644 vendor/github.com/gorilla/context/LICENSE create mode 100644 vendor/github.com/gorilla/context/README.md create mode 100644 vendor/github.com/gorilla/context/context.go create mode 100644 vendor/github.com/gorilla/context/doc.go create mode 100644 vendor/github.com/gorilla/mux/.travis.yml create mode 100644 vendor/github.com/gorilla/mux/ISSUE_TEMPLATE.md create mode 100644 vendor/github.com/gorilla/mux/LICENSE create mode 100644 vendor/github.com/gorilla/mux/README.md create mode 100644 vendor/github.com/gorilla/mux/context_gorilla.go create mode 100644 vendor/github.com/gorilla/mux/context_native.go create mode 100644 vendor/github.com/gorilla/mux/doc.go create mode 100644 vendor/github.com/gorilla/mux/middleware.go create mode 100644 vendor/github.com/gorilla/mux/mux.go create mode 100644 vendor/github.com/gorilla/mux/regexp.go create mode 100644 vendor/github.com/gorilla/mux/route.go create mode 100644 vendor/github.com/gorilla/mux/test_helpers.go create mode 100644 vendor/github.com/kr/logfmt/.gitignore create mode 100644 vendor/github.com/kr/logfmt/Readme create mode 100644 vendor/github.com/kr/logfmt/decode.go create mode 100644 vendor/github.com/kr/logfmt/scanner.go create mode 100644 vendor/github.com/kr/logfmt/unquote.go create mode 100644 vendor/modules.txt diff --git a/cmd/server/main.go b/cmd/server/main.go index 986529564..a9c3b1c83 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -9,7 +9,7 @@ import ( "syscall" "github.com/moov-io/ach/server" - "github.com/moov-io/kit/log" + "github.com/go-kit/kit/log" ) /** diff --git a/go.mod b/go.mod index 9180b9b20..61a8a7587 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,11 @@ module github.com/moov-io/ach + +require ( + github.com/go-kit/kit v0.7.0 + github.com/go-logfmt/logfmt v0.3.0 // indirect + github.com/go-stack/stack v1.8.0 // indirect + github.com/gofrs/uuid/v3 v3.1.1 + github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/mux v1.6.2 + github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..2c3fe51f4 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/go-kit/kit v0.7.0 h1:ApufNmWF1H6/wUbAG81hZOHmqwd0zRf8mNfLjYj/064= +github.com/go-kit/kit v0.7.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid/v3 v3.1.1 h1:sqMK0jjyOJ7HV36lwG2GZ6TD2hK8RB7dJQVDakI65U4= +github.com/gofrs/uuid/v3 v3.1.1/go.mod h1:xPwMqoocQ1L5G6pXX5BcE7N5jlzn2o19oqAKxwZW/kI= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= diff --git a/server/service.go b/server/service.go index 07985f21a..da3292873 100644 --- a/server/service.go +++ b/server/service.go @@ -5,7 +5,8 @@ import ( "strings" "github.com/moov-io/ach" - uuid "github.com/satori/go.uuid" + + uuid "github.com/gofrs/uuid/v3" ) var ( @@ -133,7 +134,6 @@ func (s *service) DeleteBatch(fileID string, batchID string) error { } // Utility Functions -// ***** // NextID generates a new resource ID func NextID() string { diff --git a/vendor/github.com/go-kit/kit/LICENSE b/vendor/github.com/go-kit/kit/LICENSE new file mode 100644 index 000000000..9d83342ac --- /dev/null +++ b/vendor/github.com/go-kit/kit/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Peter Bourgon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/go-kit/kit/endpoint/doc.go b/vendor/github.com/go-kit/kit/endpoint/doc.go new file mode 100644 index 000000000..84e27b95d --- /dev/null +++ b/vendor/github.com/go-kit/kit/endpoint/doc.go @@ -0,0 +1,5 @@ +// Package endpoint defines an abstraction for RPCs. +// +// Endpoints are a fundamental building block for many Go kit components. +// Endpoints are implemented by servers, and called by clients. +package endpoint diff --git a/vendor/github.com/go-kit/kit/endpoint/endpoint.go b/vendor/github.com/go-kit/kit/endpoint/endpoint.go new file mode 100644 index 000000000..1b64f50ed --- /dev/null +++ b/vendor/github.com/go-kit/kit/endpoint/endpoint.go @@ -0,0 +1,28 @@ +package endpoint + +import ( + "context" +) + +// Endpoint is the fundamental building block of servers and clients. +// It represents a single RPC method. +type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error) + +// Nop is an endpoint that does nothing and returns a nil error. +// Useful for tests. +func Nop(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil } + +// Middleware is a chainable behavior modifier for endpoints. +type Middleware func(Endpoint) Endpoint + +// Chain is a helper function for composing middlewares. Requests will +// traverse them in the order they're declared. That is, the first middleware +// is treated as the outermost middleware. +func Chain(outer Middleware, others ...Middleware) Middleware { + return func(next Endpoint) Endpoint { + for i := len(others) - 1; i >= 0; i-- { // reverse + next = others[i](next) + } + return outer(next) + } +} diff --git a/vendor/github.com/go-kit/kit/log/README.md b/vendor/github.com/go-kit/kit/log/README.md new file mode 100644 index 000000000..7222f8009 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/README.md @@ -0,0 +1,147 @@ +# package log + +`package log` provides a minimal interface for structured logging in services. +It may be wrapped to encode conventions, enforce type-safety, provide leveled +logging, and so on. It can be used for both typical application log events, +and log-structured data streams. + +## Structured logging + +Structured logging is, basically, conceding to the reality that logs are +_data_, and warrant some level of schematic rigor. Using a stricter, +key/value-oriented message format for our logs, containing contextual and +semantic information, makes it much easier to get insight into the +operational activity of the systems we build. Consequently, `package log` is +of the strong belief that "[the benefits of structured logging outweigh the +minimal effort involved](https://www.thoughtworks.com/radar/techniques/structured-logging)". + +Migrating from unstructured to structured logging is probably a lot easier +than you'd expect. + +```go +// Unstructured +log.Printf("HTTP server listening on %s", addr) + +// Structured +logger.Log("transport", "HTTP", "addr", addr, "msg", "listening") +``` + +## Usage + +### Typical application logging + +```go +w := log.NewSyncWriter(os.Stderr) +logger := log.NewLogfmtLogger(w) +logger.Log("question", "what is the meaning of life?", "answer", 42) + +// Output: +// question="what is the meaning of life?" answer=42 +``` + +### Contextual Loggers + +```go +func main() { + var logger log.Logger + logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) + logger = log.With(logger, "instance_id", 123) + + logger.Log("msg", "starting") + NewWorker(log.With(logger, "component", "worker")).Run() + NewSlacker(log.With(logger, "component", "slacker")).Run() +} + +// Output: +// instance_id=123 msg=starting +// instance_id=123 component=worker msg=running +// instance_id=123 component=slacker msg=running +``` + +### Interact with stdlib logger + +Redirect stdlib logger to Go kit logger. + +```go +import ( + "os" + stdlog "log" + kitlog "github.com/go-kit/kit/log" +) + +func main() { + logger := kitlog.NewJSONLogger(kitlog.NewSyncWriter(os.Stdout)) + stdlog.SetOutput(kitlog.NewStdlibAdapter(logger)) + stdlog.Print("I sure like pie") +} + +// Output: +// {"msg":"I sure like pie","ts":"2016/01/01 12:34:56"} +``` + +Or, if, for legacy reasons, you need to pipe all of your logging through the +stdlib log package, you can redirect Go kit logger to the stdlib logger. + +```go +logger := kitlog.NewLogfmtLogger(kitlog.StdlibWriter{}) +logger.Log("legacy", true, "msg", "at least it's something") + +// Output: +// 2016/01/01 12:34:56 legacy=true msg="at least it's something" +``` + +### Timestamps and callers + +```go +var logger log.Logger +logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) +logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) + +logger.Log("msg", "hello") + +// Output: +// ts=2016-01-01T12:34:56Z caller=main.go:15 msg=hello +``` + +## Supported output formats + +- [Logfmt](https://brandur.org/logfmt) ([see also](https://blog.codeship.com/logfmt-a-log-format-thats-easy-to-read-and-write)) +- JSON + +## Enhancements + +`package log` is centered on the one-method Logger interface. + +```go +type Logger interface { + Log(keyvals ...interface{}) error +} +``` + +This interface, and its supporting code like is the product of much iteration +and evaluation. For more details on the evolution of the Logger interface, +see [The Hunt for a Logger Interface](http://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide#1), +a talk by [Chris Hines](https://github.com/ChrisHines). +Also, please see +[#63](https://github.com/go-kit/kit/issues/63), +[#76](https://github.com/go-kit/kit/pull/76), +[#131](https://github.com/go-kit/kit/issues/131), +[#157](https://github.com/go-kit/kit/pull/157), +[#164](https://github.com/go-kit/kit/issues/164), and +[#252](https://github.com/go-kit/kit/pull/252) +to review historical conversations about package log and the Logger interface. + +Value-add packages and suggestions, +like improvements to [the leveled logger](https://godoc.org/github.com/go-kit/kit/log/level), +are of course welcome. Good proposals should + +- Be composable with [contextual loggers](https://godoc.org/github.com/go-kit/kit/log#With), +- Not break the behavior of [log.Caller](https://godoc.org/github.com/go-kit/kit/log#Caller) in any wrapped contextual loggers, and +- Be friendly to packages that accept only an unadorned log.Logger. + +## Benchmarks & comparisons + +There are a few Go logging benchmarks and comparisons that include Go kit's package log. + +- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench) includes kit/log +- [uber-common/zap](https://github.com/uber-common/zap), a zero-alloc logging library, includes a comparison with kit/log diff --git a/vendor/github.com/go-kit/kit/log/doc.go b/vendor/github.com/go-kit/kit/log/doc.go new file mode 100644 index 000000000..918c0af46 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/doc.go @@ -0,0 +1,116 @@ +// Package log provides a structured logger. +// +// Structured logging produces logs easily consumed later by humans or +// machines. Humans might be interested in debugging errors, or tracing +// specific requests. Machines might be interested in counting interesting +// events, or aggregating information for off-line processing. In both cases, +// it is important that the log messages are structured and actionable. +// Package log is designed to encourage both of these best practices. +// +// Basic Usage +// +// The fundamental interface is Logger. Loggers create log events from +// key/value data. The Logger interface has a single method, Log, which +// accepts a sequence of alternating key/value pairs, which this package names +// keyvals. +// +// type Logger interface { +// Log(keyvals ...interface{}) error +// } +// +// Here is an example of a function using a Logger to create log events. +// +// func RunTask(task Task, logger log.Logger) string { +// logger.Log("taskID", task.ID, "event", "starting task") +// ... +// logger.Log("taskID", task.ID, "event", "task complete") +// } +// +// The keys in the above example are "taskID" and "event". The values are +// task.ID, "starting task", and "task complete". Every key is followed +// immediately by its value. +// +// Keys are usually plain strings. Values may be any type that has a sensible +// encoding in the chosen log format. With structured logging it is a good +// idea to log simple values without formatting them. This practice allows +// the chosen logger to encode values in the most appropriate way. +// +// Contextual Loggers +// +// A contextual logger stores keyvals that it includes in all log events. +// Building appropriate contextual loggers reduces repetition and aids +// consistency in the resulting log output. With and WithPrefix add context to +// a logger. We can use With to improve the RunTask example. +// +// func RunTask(task Task, logger log.Logger) string { +// logger = log.With(logger, "taskID", task.ID) +// logger.Log("event", "starting task") +// ... +// taskHelper(task.Cmd, logger) +// ... +// logger.Log("event", "task complete") +// } +// +// The improved version emits the same log events as the original for the +// first and last calls to Log. Passing the contextual logger to taskHelper +// enables each log event created by taskHelper to include the task.ID even +// though taskHelper does not have access to that value. Using contextual +// loggers this way simplifies producing log output that enables tracing the +// life cycle of individual tasks. (See the Contextual example for the full +// code of the above snippet.) +// +// Dynamic Contextual Values +// +// A Valuer function stored in a contextual logger generates a new value each +// time an event is logged. The Valuer example demonstrates how this feature +// works. +// +// Valuers provide the basis for consistently logging timestamps and source +// code location. The log package defines several valuers for that purpose. +// See Timestamp, DefaultTimestamp, DefaultTimestampUTC, Caller, and +// DefaultCaller. A common logger initialization sequence that ensures all log +// entries contain a timestamp and source location looks like this: +// +// logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) +// logger = log.With(logger, "ts", log.DefaultTimestampUTC, "caller", log.DefaultCaller) +// +// Concurrent Safety +// +// Applications with multiple goroutines want each log event written to the +// same logger to remain separate from other log events. Package log provides +// two simple solutions for concurrent safe logging. +// +// NewSyncWriter wraps an io.Writer and serializes each call to its Write +// method. Using a SyncWriter has the benefit that the smallest practical +// portion of the logging logic is performed within a mutex, but it requires +// the formatting Logger to make only one call to Write per log event. +// +// NewSyncLogger wraps any Logger and serializes each call to its Log method. +// Using a SyncLogger has the benefit that it guarantees each log event is +// handled atomically within the wrapped logger, but it typically serializes +// both the formatting and output logic. Use a SyncLogger if the formatting +// logger may perform multiple writes per log event. +// +// Error Handling +// +// This package relies on the practice of wrapping or decorating loggers with +// other loggers to provide composable pieces of functionality. It also means +// that Logger.Log must return an error because some +// implementations—especially those that output log data to an io.Writer—may +// encounter errors that cannot be handled locally. This in turn means that +// Loggers that wrap other loggers should return errors from the wrapped +// logger up the stack. +// +// Fortunately, the decorator pattern also provides a way to avoid the +// necessity to check for errors every time an application calls Logger.Log. +// An application required to panic whenever its Logger encounters +// an error could initialize its logger as follows. +// +// fmtlogger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) +// logger := log.LoggerFunc(func(keyvals ...interface{}) error { +// if err := fmtlogger.Log(keyvals...); err != nil { +// panic(err) +// } +// return nil +// }) +package log diff --git a/vendor/github.com/go-kit/kit/log/json_logger.go b/vendor/github.com/go-kit/kit/log/json_logger.go new file mode 100644 index 000000000..66094b4dd --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/json_logger.go @@ -0,0 +1,89 @@ +package log + +import ( + "encoding" + "encoding/json" + "fmt" + "io" + "reflect" +) + +type jsonLogger struct { + io.Writer +} + +// NewJSONLogger returns a Logger that encodes keyvals to the Writer as a +// single JSON object. Each log event produces no more than one call to +// w.Write. The passed Writer must be safe for concurrent use by multiple +// goroutines if the returned Logger will be used concurrently. +func NewJSONLogger(w io.Writer) Logger { + return &jsonLogger{w} +} + +func (l *jsonLogger) Log(keyvals ...interface{}) error { + n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd + m := make(map[string]interface{}, n) + for i := 0; i < len(keyvals); i += 2 { + k := keyvals[i] + var v interface{} = ErrMissingValue + if i+1 < len(keyvals) { + v = keyvals[i+1] + } + merge(m, k, v) + } + return json.NewEncoder(l.Writer).Encode(m) +} + +func merge(dst map[string]interface{}, k, v interface{}) { + var key string + switch x := k.(type) { + case string: + key = x + case fmt.Stringer: + key = safeString(x) + default: + key = fmt.Sprint(x) + } + + // We want json.Marshaler and encoding.TextMarshaller to take priority over + // err.Error() and v.String(). But json.Marshall (called later) does that by + // default so we force a no-op if it's one of those 2 case. + switch x := v.(type) { + case json.Marshaler: + case encoding.TextMarshaler: + case error: + v = safeError(x) + case fmt.Stringer: + v = safeString(x) + } + + dst[key] = v +} + +func safeString(str fmt.Stringer) (s string) { + defer func() { + if panicVal := recover(); panicVal != nil { + if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { + s = "NULL" + } else { + panic(panicVal) + } + } + }() + s = str.String() + return +} + +func safeError(err error) (s interface{}) { + defer func() { + if panicVal := recover(); panicVal != nil { + if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { + s = nil + } else { + panic(panicVal) + } + } + }() + s = err.Error() + return +} diff --git a/vendor/github.com/go-kit/kit/log/log.go b/vendor/github.com/go-kit/kit/log/log.go new file mode 100644 index 000000000..66a9e2fde --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/log.go @@ -0,0 +1,135 @@ +package log + +import "errors" + +// Logger is the fundamental interface for all log operations. Log creates a +// log event from keyvals, a variadic sequence of alternating keys and values. +// Implementations must be safe for concurrent use by multiple goroutines. In +// particular, any implementation of Logger that appends to keyvals or +// modifies or retains any of its elements must make a copy first. +type Logger interface { + Log(keyvals ...interface{}) error +} + +// ErrMissingValue is appended to keyvals slices with odd length to substitute +// the missing value. +var ErrMissingValue = errors.New("(MISSING)") + +// With returns a new contextual logger with keyvals prepended to those passed +// to calls to Log. If logger is also a contextual logger created by With or +// WithPrefix, keyvals is appended to the existing context. +// +// The returned Logger replaces all value elements (odd indexes) containing a +// Valuer with their generated value for each call to its Log method. +func With(logger Logger, keyvals ...interface{}) Logger { + if len(keyvals) == 0 { + return logger + } + l := newContext(logger) + kvs := append(l.keyvals, keyvals...) + if len(kvs)%2 != 0 { + kvs = append(kvs, ErrMissingValue) + } + return &context{ + logger: l.logger, + // Limiting the capacity of the stored keyvals ensures that a new + // backing array is created if the slice must grow in Log or With. + // Using the extra capacity without copying risks a data race that + // would violate the Logger interface contract. + keyvals: kvs[:len(kvs):len(kvs)], + hasValuer: l.hasValuer || containsValuer(keyvals), + } +} + +// WithPrefix returns a new contextual logger with keyvals prepended to those +// passed to calls to Log. If logger is also a contextual logger created by +// With or WithPrefix, keyvals is prepended to the existing context. +// +// The returned Logger replaces all value elements (odd indexes) containing a +// Valuer with their generated value for each call to its Log method. +func WithPrefix(logger Logger, keyvals ...interface{}) Logger { + if len(keyvals) == 0 { + return logger + } + l := newContext(logger) + // Limiting the capacity of the stored keyvals ensures that a new + // backing array is created if the slice must grow in Log or With. + // Using the extra capacity without copying risks a data race that + // would violate the Logger interface contract. + n := len(l.keyvals) + len(keyvals) + if len(keyvals)%2 != 0 { + n++ + } + kvs := make([]interface{}, 0, n) + kvs = append(kvs, keyvals...) + if len(kvs)%2 != 0 { + kvs = append(kvs, ErrMissingValue) + } + kvs = append(kvs, l.keyvals...) + return &context{ + logger: l.logger, + keyvals: kvs, + hasValuer: l.hasValuer || containsValuer(keyvals), + } +} + +// context is the Logger implementation returned by With and WithPrefix. It +// wraps a Logger and holds keyvals that it includes in all log events. Its +// Log method calls bindValues to generate values for each Valuer in the +// context keyvals. +// +// A context must always have the same number of stack frames between calls to +// its Log method and the eventual binding of Valuers to their value. This +// requirement comes from the functional requirement to allow a context to +// resolve application call site information for a Caller stored in the +// context. To do this we must be able to predict the number of logging +// functions on the stack when bindValues is called. +// +// Two implementation details provide the needed stack depth consistency. +// +// 1. newContext avoids introducing an additional layer when asked to +// wrap another context. +// 2. With and WithPrefix avoid introducing an additional layer by +// returning a newly constructed context with a merged keyvals rather +// than simply wrapping the existing context. +type context struct { + logger Logger + keyvals []interface{} + hasValuer bool +} + +func newContext(logger Logger) *context { + if c, ok := logger.(*context); ok { + return c + } + return &context{logger: logger} +} + +// Log replaces all value elements (odd indexes) containing a Valuer in the +// stored context with their generated value, appends keyvals, and passes the +// result to the wrapped Logger. +func (l *context) Log(keyvals ...interface{}) error { + kvs := append(l.keyvals, keyvals...) + if len(kvs)%2 != 0 { + kvs = append(kvs, ErrMissingValue) + } + if l.hasValuer { + // If no keyvals were appended above then we must copy l.keyvals so + // that future log events will reevaluate the stored Valuers. + if len(keyvals) == 0 { + kvs = append([]interface{}{}, l.keyvals...) + } + bindValues(kvs[:len(l.keyvals)]) + } + return l.logger.Log(kvs...) +} + +// LoggerFunc is an adapter to allow use of ordinary functions as Loggers. If +// f is a function with the appropriate signature, LoggerFunc(f) is a Logger +// object that calls f. +type LoggerFunc func(...interface{}) error + +// Log implements Logger by calling f(keyvals...). +func (f LoggerFunc) Log(keyvals ...interface{}) error { + return f(keyvals...) +} diff --git a/vendor/github.com/go-kit/kit/log/logfmt_logger.go b/vendor/github.com/go-kit/kit/log/logfmt_logger.go new file mode 100644 index 000000000..a00305298 --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/logfmt_logger.go @@ -0,0 +1,62 @@ +package log + +import ( + "bytes" + "io" + "sync" + + "github.com/go-logfmt/logfmt" +) + +type logfmtEncoder struct { + *logfmt.Encoder + buf bytes.Buffer +} + +func (l *logfmtEncoder) Reset() { + l.Encoder.Reset() + l.buf.Reset() +} + +var logfmtEncoderPool = sync.Pool{ + New: func() interface{} { + var enc logfmtEncoder + enc.Encoder = logfmt.NewEncoder(&enc.buf) + return &enc + }, +} + +type logfmtLogger struct { + w io.Writer +} + +// NewLogfmtLogger returns a logger that encodes keyvals to the Writer in +// logfmt format. Each log event produces no more than one call to w.Write. +// The passed Writer must be safe for concurrent use by multiple goroutines if +// the returned Logger will be used concurrently. +func NewLogfmtLogger(w io.Writer) Logger { + return &logfmtLogger{w} +} + +func (l logfmtLogger) Log(keyvals ...interface{}) error { + enc := logfmtEncoderPool.Get().(*logfmtEncoder) + enc.Reset() + defer logfmtEncoderPool.Put(enc) + + if err := enc.EncodeKeyvals(keyvals...); err != nil { + return err + } + + // Add newline to the end of the buffer + if err := enc.EndRecord(); err != nil { + return err + } + + // The Logger interface requires implementations to be safe for concurrent + // use by multiple goroutines. For this implementation that means making + // only one call to l.w.Write() for each call to Log. + if _, err := l.w.Write(enc.buf.Bytes()); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/go-kit/kit/log/nop_logger.go b/vendor/github.com/go-kit/kit/log/nop_logger.go new file mode 100644 index 000000000..1047d626c --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/nop_logger.go @@ -0,0 +1,8 @@ +package log + +type nopLogger struct{} + +// NewNopLogger returns a logger that doesn't do anything. +func NewNopLogger() Logger { return nopLogger{} } + +func (nopLogger) Log(...interface{}) error { return nil } diff --git a/vendor/github.com/go-kit/kit/log/stdlib.go b/vendor/github.com/go-kit/kit/log/stdlib.go new file mode 100644 index 000000000..ff96b5dee --- /dev/null +++ b/vendor/github.com/go-kit/kit/log/stdlib.go @@ -0,0 +1,116 @@ +package log + +import ( + "io" + "log" + "regexp" + "strings" +) + +// StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's +// designed to be passed to a Go kit logger as the writer, for cases where +// it's necessary to redirect all Go kit log output to the stdlib logger. +// +// If you have any choice in the matter, you shouldn't use this. Prefer to +// redirect the stdlib log to the Go kit logger via NewStdlibAdapter. +type StdlibWriter struct{} + +// Write implements io.Writer. +func (w StdlibWriter) Write(p []byte) (int, error) { + log.Print(strings.TrimSpace(string(p))) + return len(p), nil +} + +// StdlibAdapter wraps a Logger and allows it to be passed to the stdlib +// logger's SetOutput. It will extract date/timestamps, filenames, and +// messages, and place them under relevant keys. +type StdlibAdapter struct { + Logger + timestampKey string + fileKey string + messageKey string +} + +// StdlibAdapterOption sets a parameter for the StdlibAdapter. +type StdlibAdapterOption func(*StdlibAdapter) + +// TimestampKey sets the key for the timestamp field. By default, it's "ts". +func TimestampKey(key string) StdlibAdapterOption { + return func(a *StdlibAdapter) { a.timestampKey = key } +} + +// FileKey sets the key for the file and line field. By default, it's "caller". +func FileKey(key string) StdlibAdapterOption { + return func(a *StdlibAdapter) { a.fileKey = key } +} + +// MessageKey sets the key for the actual log message. By default, it's "msg". +func MessageKey(key string) StdlibAdapterOption { + return func(a *StdlibAdapter) { a.messageKey = key } +} + +// NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed +// logger. It's designed to be passed to log.SetOutput. +func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { + a := StdlibAdapter{ + Logger: logger, + timestampKey: "ts", + fileKey: "caller", + messageKey: "msg", + } + for _, option := range options { + option(&a) + } + return a +} + +func (a StdlibAdapter) Write(p []byte) (int, error) { + result := subexps(p) + keyvals := []interface{}{} + var timestamp string + if date, ok := result["date"]; ok && date != "" { + timestamp = date + } + if time, ok := result["time"]; ok && time != "" { + if timestamp != "" { + timestamp += " " + } + timestamp += time + } + if timestamp != "" { + keyvals = append(keyvals, a.timestampKey, timestamp) + } + if file, ok := result["file"]; ok && file != "" { + keyvals = append(keyvals, a.fileKey, file) + } + if msg, ok := result["msg"]; ok { + keyvals = append(keyvals, a.messageKey, msg) + } + if err := a.Logger.Log(keyvals...); err != nil { + return 0, err + } + return len(p), nil +} + +const ( + logRegexpDate = `(?P[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` + logRegexpTime = `(?P