github.com/moov-io/imagecashletter@v0.10.1/CheckDetailAddendumC.go (about)

     1  // Copyright 2020 The Moov Authors
     2  // Use of this source code is governed by an Apache License
     3  // license that can be found in the LICENSE file.
     4  
     5  package imagecashletter
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  	"unicode/utf8"
    14  )
    15  
    16  // Errors specific to a CheckDetailAddendumC Record
    17  
    18  // CheckDetailAddendumC Record
    19  type CheckDetailAddendumC struct {
    20  	// ID is a client defined string used as a reference to this record.
    21  	ID string `json:"id"`
    22  	// RecordType defines the type of record.
    23  	recordType string
    24  	// RecordNumber is a number representing the order in which each CheckDetailAddendumC was created.
    25  	// CheckDetailAddendumC shall be in sequential order starting with 1.  Maximum 99,
    26  	RecordNumber int `json:"recordNumber"`
    27  	// RoutingNumber (Endorsing Bank Routing Number) is valid routing and transit number indicating the bank that
    28  	// endorsed the check.
    29  	// Format: TTTTAAAAC, where:
    30  	// TTTT Federal Reserve Prefix
    31  	// AAAA ABA Institution Identifier
    32  	// C Check Digit
    33  	// For a number that identifies a non-financial institution: NNNNNNNNN
    34  	EndorsingBankRoutingNumber string `json:"endorsingBankRoutingNumber"`
    35  	// BOFDEndorsementBusinessDate is the business date the check was endorsed.
    36  	// Format: YYYYMMDD, where: YYYY year, MM month, DD day
    37  	// Values:
    38  	// YYYY 1993 through 9999
    39  	// MM 01 through 12
    40  	// DD 01 through 31
    41  	BOFDEndorsementBusinessDate time.Time `json:"bofdEndorsementBusinessDate"`
    42  	// EndorsingItemSequenceNumber is a number that identifies the item at the endorsing bank.
    43  	EndorsingBankItemSequenceNumber string `json:"endorsingBankItemSequenceNumber"`
    44  	// TruncationIndicator identifies if the institution truncated the original check item.
    45  	// Values: Y: Yes this institution truncated this original check item and this is first endorsement
    46  	// for the institution.
    47  	// N: No this institution did not truncate the original check or, this is not the first endorsement for the
    48  	// institution or, this item is an IRD not an original check item (EPC equals 4).
    49  	TruncationIndicator string `json:"truncationIndicator"`
    50  	// EndorsingConversionIndicator is a code that indicates the conversion within the processing institution among
    51  	// original paper check, image and IRD. The indicator is specific to the action of institution identified in the
    52  	// Endorsing Bank RoutingNumber.
    53  	// Values:
    54  	// 0: Did not convert physical document
    55  	// 1: Original paper converted to IRD
    56  	// 2: Original paper converted to image
    57  	// 3: IRD converted to another IRD
    58  	// 4: IRD converted to image of IRD
    59  	// 5: Image converted to an IRD
    60  	// 6: Image converted to another image (e.g., transcoded)
    61  	// 7: Did not convert image (e.g., same as source)
    62  	// 8: Undetermined
    63  	EndorsingBankConversionIndicator string `json:"endorsingBankConversionIndicator"`
    64  	// EndorsingCorrectionIndicator identifies whether and how the MICR line of this item was repaired by the
    65  	// creator of this CheckDetailAddendumC Record for fields other than Payor Bank Routing Number and Amount.
    66  	// Values:
    67  	// 0: No Repair
    68  	// 1: Repaired (form of repair unknown)
    69  	// 2: Repaired without Operator intervention
    70  	// 3: Repaired with Operator intervention
    71  	// 4: Undetermined if repair has been done or no
    72  	EndorsingBankCorrectionIndicator int `json:"endorsingBankCorrectionIndicator"`
    73  	// ReturnReason is a code that indicates the reason for non-payment.
    74  	ReturnReason string `json:"returnReason"`
    75  	// UserField identifies a field used at the discretion of users of the standard.
    76  	UserField string `json:"userField"`
    77  	//EndorsingBankIdentifier
    78  	// Values:
    79  	// 0: Depository Bank (BOFD) - this value is used when the CheckDetailAddendumC Record reflects the Return
    80  	// Processing Bank in lieu of BOFD.
    81  	// 1: Other Collecting Bank
    82  	// 2: Other Returning Bank
    83  	// 3: Payor Bank
    84  	EndorsingBankIdentifier int `json:"endorsingBankIdentifier"`
    85  	// reserved is a field reserved for future use.  Reserved should be blank.
    86  	reserved string
    87  	// validator is composed for image cash letter data validation
    88  	validator
    89  	// converters is composed for image cash letter to golang Converters
    90  	converters
    91  }
    92  
    93  // NewCheckDetailAddendumC returns a new CheckDetailAddendumC with default values for non exported fields
    94  func NewCheckDetailAddendumC() CheckDetailAddendumC {
    95  	cdAddendumC := CheckDetailAddendumC{}
    96  	cdAddendumC.setRecordType()
    97  	return cdAddendumC
    98  }
    99  
   100  func (cdAddendumC *CheckDetailAddendumC) setRecordType() {
   101  	if cdAddendumC == nil {
   102  		return
   103  	}
   104  	cdAddendumC.recordType = "28"
   105  }
   106  
   107  // Parse takes the input record string and parses the CheckDetailAddendumC values
   108  func (cdAddendumC *CheckDetailAddendumC) Parse(record string) {
   109  	if utf8.RuneCountInString(record) < 60 {
   110  		return // line too short
   111  	}
   112  
   113  	// Character position 1-2, Always "28"
   114  	cdAddendumC.setRecordType()
   115  	// 03-04
   116  	cdAddendumC.RecordNumber = cdAddendumC.parseNumField(record[2:4])
   117  	// 05-13
   118  	cdAddendumC.EndorsingBankRoutingNumber = cdAddendumC.parseStringField(record[4:13])
   119  	// 14-21
   120  	cdAddendumC.BOFDEndorsementBusinessDate = cdAddendumC.parseYYYYMMDDDate(record[13:21])
   121  	// 22-36
   122  	cdAddendumC.EndorsingBankItemSequenceNumber = cdAddendumC.parseStringField(record[21:36])
   123  	// 37-37
   124  	cdAddendumC.TruncationIndicator = cdAddendumC.parseStringField(record[36:37])
   125  	// 38-38
   126  	cdAddendumC.EndorsingBankConversionIndicator = cdAddendumC.parseStringField(record[37:38])
   127  	// 39-39
   128  	cdAddendumC.EndorsingBankCorrectionIndicator = cdAddendumC.parseNumField(record[38:39])
   129  	// 40-40
   130  	cdAddendumC.ReturnReason = cdAddendumC.parseStringField(record[39:40])
   131  	// 41-59
   132  	cdAddendumC.UserField = cdAddendumC.parseStringField(record[40:59])
   133  	// 60-60
   134  	cdAddendumC.EndorsingBankIdentifier = cdAddendumC.parseNumField(record[59:60])
   135  	// 61-80
   136  	cdAddendumC.reserved = "                    "
   137  }
   138  
   139  func (cdAddendumC *CheckDetailAddendumC) UnmarshalJSON(data []byte) error {
   140  	type Alias CheckDetailAddendumC
   141  	aux := struct {
   142  		*Alias
   143  	}{
   144  		(*Alias)(cdAddendumC),
   145  	}
   146  	if err := json.Unmarshal(data, &aux); err != nil {
   147  		return err
   148  	}
   149  	cdAddendumC.setRecordType()
   150  	return nil
   151  }
   152  
   153  // String writes the CheckDetailAddendumC struct to a string.
   154  func (cdAddendumC *CheckDetailAddendumC) String() string {
   155  	if cdAddendumC == nil {
   156  		return ""
   157  	}
   158  
   159  	var buf strings.Builder
   160  	buf.Grow(80)
   161  	buf.WriteString(cdAddendumC.recordType)
   162  	buf.WriteString(cdAddendumC.RecordNumberField())
   163  	buf.WriteString(cdAddendumC.EndorsingBankRoutingNumberField())
   164  	buf.WriteString(cdAddendumC.BOFDEndorsementBusinessDateField())
   165  	buf.WriteString(cdAddendumC.EndorsingBankItemSequenceNumberField())
   166  	buf.WriteString(cdAddendumC.TruncationIndicatorField())
   167  	buf.WriteString(cdAddendumC.EndorsingBankConversionIndicatorField())
   168  	buf.WriteString(cdAddendumC.EndorsingBankCorrectionIndicatorField())
   169  	buf.WriteString(cdAddendumC.ReturnReasonField())
   170  	buf.WriteString(cdAddendumC.UserFieldField())
   171  	buf.WriteString(cdAddendumC.EndorsingBankIdentifierField())
   172  	buf.WriteString(cdAddendumC.reservedField())
   173  	return buf.String()
   174  }
   175  
   176  // Validate performs image cash letter format rule checks on the record and returns an error if not Validated
   177  // The first error encountered is returned and stops the parsing.
   178  func (cdAddendumC *CheckDetailAddendumC) Validate() error {
   179  	if err := cdAddendumC.fieldInclusion(); err != nil {
   180  		return err
   181  	}
   182  	if cdAddendumC.recordType != "28" {
   183  		msg := fmt.Sprintf(msgRecordType, 28)
   184  		return &FieldError{FieldName: "recordType", Value: cdAddendumC.recordType, Msg: msg}
   185  	}
   186  	if err := cdAddendumC.isNumeric(cdAddendumC.EndorsingBankRoutingNumber); err != nil {
   187  		return &FieldError{FieldName: "EndorsingBankRoutingNumber",
   188  			Value: cdAddendumC.EndorsingBankRoutingNumber, Msg: err.Error()}
   189  	}
   190  	// Mandatory
   191  	if err := cdAddendumC.isTruncationIndicator(cdAddendumC.TruncationIndicator); err != nil {
   192  		return &FieldError{FieldName: "TruncationIndicator",
   193  			Value: cdAddendumC.TruncationIndicator, Msg: err.Error()}
   194  	}
   195  	// Conditional
   196  	if cdAddendumC.EndorsingBankConversionIndicator != "" {
   197  		if err := cdAddendumC.isConversionIndicator(cdAddendumC.EndorsingBankConversionIndicator); err != nil {
   198  			return &FieldError{FieldName: "EndorsingBankConversionIndicator",
   199  				Value: cdAddendumC.EndorsingBankConversionIndicator, Msg: err.Error()}
   200  		}
   201  	}
   202  	// Conditional
   203  	if cdAddendumC.EndorsingBankCorrectionIndicatorField() != "" {
   204  		if err := cdAddendumC.isCorrectionIndicator(cdAddendumC.EndorsingBankCorrectionIndicator); err != nil {
   205  			return &FieldError{FieldName: "EndorsingBankCorrectionIndicator",
   206  				Value: cdAddendumC.EndorsingBankCorrectionIndicatorField(), Msg: err.Error()}
   207  		}
   208  	}
   209  	if err := cdAddendumC.isAlphanumeric(cdAddendumC.ReturnReason); err != nil {
   210  		return &FieldError{FieldName: "ReturnReason",
   211  			Value: cdAddendumC.ReturnReason, Msg: err.Error()}
   212  	}
   213  	if err := cdAddendumC.isAlphanumericSpecial(cdAddendumC.UserField); err != nil {
   214  		return &FieldError{FieldName: "UserField", Value: cdAddendumC.UserField, Msg: err.Error()}
   215  	}
   216  	if err := cdAddendumC.isEndorsingBankIdentifier(cdAddendumC.EndorsingBankIdentifier); err != nil {
   217  		return &FieldError{FieldName: "EndorsingBankIdentifier",
   218  			Value: cdAddendumC.EndorsingBankIdentifierField(), Msg: err.Error()}
   219  	}
   220  	return nil
   221  }
   222  
   223  // fieldInclusion validate mandatory fields are not default values. If fields are
   224  // invalid the Electronic Exchange will be returned.
   225  func (cdAddendumC *CheckDetailAddendumC) fieldInclusion() error {
   226  	if cdAddendumC.recordType == "" {
   227  		return &FieldError{FieldName: "recordType",
   228  			Value: cdAddendumC.recordType,
   229  			Msg:   msgFieldInclusion + ", did you use CheckDetailAddendumC()?"}
   230  	}
   231  	if cdAddendumC.RecordNumber == 0 {
   232  		return &FieldError{FieldName: "RecordNumber",
   233  			Value: cdAddendumC.RecordNumberField(),
   234  			Msg:   msgFieldInclusion + ", did you use CheckDetailAddendumC()?"}
   235  	}
   236  	if cdAddendumC.EndorsingBankRoutingNumber == "" {
   237  		return &FieldError{FieldName: "EndorsingBankRoutingNumber",
   238  			Value: cdAddendumC.EndorsingBankRoutingNumber,
   239  			Msg:   msgFieldInclusion + ", did you use CheckDetailAddendumC()?"}
   240  	}
   241  	if cdAddendumC.EndorsingBankRoutingNumberField() == "000000000" {
   242  		return &FieldError{FieldName: "EndorsingBankRoutingNumber",
   243  			Value: cdAddendumC.EndorsingBankRoutingNumber,
   244  			Msg:   msgFieldInclusion + ", did you use CheckDetailAddendumC()?"}
   245  	}
   246  	if cdAddendumC.BOFDEndorsementBusinessDate.IsZero() {
   247  		return &FieldError{FieldName: "BOFDEndorsementBusinessDate",
   248  			Value: cdAddendumC.BOFDEndorsementBusinessDate.String(),
   249  			Msg:   msgFieldInclusion + ", did you use CheckDetailAddendumC()?"}
   250  	}
   251  	if cdAddendumC.EndorsingBankItemSequenceNumberField() == "               " {
   252  		return &FieldError{FieldName: "EndorsingBankItemSequenceNumber",
   253  			Value: cdAddendumC.EndorsingBankItemSequenceNumber,
   254  			Msg:   msgFieldInclusion + ", did you use CheckDetailAddendumC()?"}
   255  	}
   256  	if cdAddendumC.TruncationIndicator == "" {
   257  		return &FieldError{FieldName: "TruncationIndicator",
   258  			Value: cdAddendumC.TruncationIndicator,
   259  			Msg:   msgFieldInclusion + ", did you use CheckDetailAddendumC()?"}
   260  	}
   261  	return nil
   262  }
   263  
   264  // RecordNumberField gets a string of the RecordNumber field
   265  func (cdAddendumC *CheckDetailAddendumC) RecordNumberField() string {
   266  	return cdAddendumC.numericField(cdAddendumC.RecordNumber, 2)
   267  }
   268  
   269  // EndorsingBankRoutingNumberField gets a string of the EndorsingBankRoutingNumber field
   270  func (cdAddendumC *CheckDetailAddendumC) EndorsingBankRoutingNumberField() string {
   271  	return cdAddendumC.stringField(cdAddendumC.EndorsingBankRoutingNumber, 9)
   272  }
   273  
   274  // BOFDEndorsementBusinessDateField gets the BOFDEndorsementBusinessDate in YYYYMMDD format
   275  func (cdAddendumC *CheckDetailAddendumC) BOFDEndorsementBusinessDateField() string {
   276  	return cdAddendumC.formatYYYYMMDDDate(cdAddendumC.BOFDEndorsementBusinessDate)
   277  }
   278  
   279  // EndorsingBankItemSequenceNumberField gets the EndorsingBankItemSequenceNumber field
   280  func (cdAddendumC *CheckDetailAddendumC) EndorsingBankItemSequenceNumberField() string {
   281  	return cdAddendumC.alphaField(cdAddendumC.EndorsingBankItemSequenceNumber, 15)
   282  }
   283  
   284  // TruncationIndicatorField gets the TruncationIndicator field
   285  func (cdAddendumC *CheckDetailAddendumC) TruncationIndicatorField() string {
   286  	return cdAddendumC.alphaField(cdAddendumC.TruncationIndicator, 1)
   287  }
   288  
   289  // EndorsingBankConversionIndicatorField gets the EndorsingBankConversionIndicator field
   290  func (cdAddendumC *CheckDetailAddendumC) EndorsingBankConversionIndicatorField() string {
   291  	return cdAddendumC.alphaField(cdAddendumC.EndorsingBankConversionIndicator, 1)
   292  }
   293  
   294  // EndorsingBankCorrectionIndicatorField gets a string of the EndorsingBankCorrectionIndicator field
   295  func (cdAddendumC *CheckDetailAddendumC) EndorsingBankCorrectionIndicatorField() string {
   296  	return cdAddendumC.numericField(cdAddendumC.EndorsingBankCorrectionIndicator, 1)
   297  }
   298  
   299  // ReturnReasonField gets the ReturnReason field
   300  func (cdAddendumC *CheckDetailAddendumC) ReturnReasonField() string {
   301  	return cdAddendumC.alphaField(cdAddendumC.ReturnReason, 1)
   302  }
   303  
   304  // UserFieldField gets the UserField field
   305  func (cdAddendumC *CheckDetailAddendumC) UserFieldField() string {
   306  	return cdAddendumC.alphaField(cdAddendumC.UserField, 19)
   307  }
   308  
   309  // EndorsingBankIdentifierField gets the EndorsingBankIdentifier field
   310  func (cdAddendumC *CheckDetailAddendumC) EndorsingBankIdentifierField() string {
   311  	return cdAddendumC.numericField(cdAddendumC.EndorsingBankIdentifier, 1)
   312  }
   313  
   314  // reservedField gets reserved - blank space
   315  func (cdAddendumC *CheckDetailAddendumC) reservedField() string {
   316  	return cdAddendumC.alphaField(cdAddendumC.reserved, 20)
   317  }
   318  
   319  // SetEndorsingBankItemSequenceNumber sets EndorsingBankItemSequenceNumber
   320  func (cdAddendumC *CheckDetailAddendumC) SetEndorsingBankItemSequenceNumber(seq int) string {
   321  	itemSequence := strconv.Itoa(seq)
   322  	cdAddendumC.EndorsingBankItemSequenceNumber = itemSequence
   323  	return cdAddendumC.EndorsingBankItemSequenceNumber
   324  }