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 }