github.com/moov-io/imagecashletter@v0.10.1/userPayeeEndorsement.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 "strings" 11 "time" 12 "unicode/utf8" 13 ) 14 15 // The User Payee Endorsement Format Record is conditional, and contains a user controlled number of fields. The 16 // record is used based on clearing arrangements. The Record can occur anywhere in the file based on those clearing 17 // arrangements, HOWEVER it is typically recommended that it appear in the checkDetail or ReturnDetail. 18 // The current implementation of the User Payee Endorsement Format Record is for the Concrete Type Only. 19 // For reader and writer implementation, please adjust based on specific clearing arrangements, or contact MOOV for 20 // your particular implementation. 21 22 // UserPayeeEndorsement Record 23 type UserPayeeEndorsement struct { 24 // ID is a client defined string used as a reference to this record. 25 ID string `json:"id"` 26 // RecordType defines the type of record. 27 recordType string 28 // OwnerIdentifierIndicator indicates the type of number represented in OwnerIdentifier 29 OwnerIdentifierIndicator int `json:"ownerIdentifierIndicator"` 30 // OwnerIdentifier is a number used by the organization that controls the definition and formatting of this record. 31 OwnerIdentifier string `json:"ownerIdentifier"` 32 // OwnerIdentifierModifier is a modifier which uniquely identifies the owner within the owning organization. 33 OwnerIdentifierModifier string `json:"ownerIdentifierModifier"` 34 // UserRecordFormatType uniquely identifies the particular format used to parse and interrogate this record. 35 // Provides a means for differentiating user record data layouts. This field shall not be populated with 001 36 // since this is reserved for the UserRecordFormatType 001 PayeeEndorsement. 37 UserRecordFormatType string `json:"userRecordFormatType"` 38 // FormatTypeVersionLevel is a code identifies the version of the UserRecordFormatType. Provides a means for 39 // identifying different versions of a record layout. 40 FormatTypeVersionLevel string `json:"formatTypeVersionLevel"` 41 // LengthUserData is the number of characters or bytes contained in the UserData and must be greater than 0. 42 LengthUserData string `json:"LengthUserData"` 43 // PayeeName is the payee name to which the check is written. 44 PayeeName string `json:"payeeName"` 45 // EndorsementDate The year, month, and day that the immediate origin institution creates the file which 46 // shall be in Eastern Time zone format. Other time zones may be used under clearing arrangements. 47 // Format: YYYYMMDD, where: YYYY year, MM month, DD day 48 // Values: 49 // YYYY 1993 through 9999 50 // MM 01 through 12 51 // DD 01 through 31 52 EndorsementDate time.Time `json:"endorsementDate"` 53 // BankRoutingNumber is a number that identifies the institution or the organization where the item is being 54 // deposited. (note: should be the routing number associated with the BankAccountNumber. 55 // Format: TTTTAAAAC, where: 56 // TTTT Federal Reserve Prefix 57 // AAAA ABA Institution Identifier 58 // C Check Digit 59 // For a number that identifies a non-financial institution: NNNNNNNNN 60 BankRoutingNumber string `json:"bankRoutingNumber"` 61 // BankAccountNumber is the Bank Account Number of the endorsing organization. 62 BankAccountNumber string `json:"bankAccountNumber"` 63 // CustomerIdentifier is a number or code identifying the customer of the endorser (e.g., driver’s license number 64 // or shopper number, etc.). 65 CustomerIdentifier string `json:"customerIdentifier"` 66 // CustomerContactInformation is Customer contact information (e.g., phone number, e-mail, addresses, etc). 67 // Unique field data shall be separated by commas. 68 CustomerContactInformation string `json:"customerContactInformation"` 69 // StoreMerchantProcessingSiteNumber is a number or code identifying the merchant, store or processing site. 70 StoreMerchantProcessingSiteNumber string `json:"storeMerchantProcessingSiteNumber"` 71 // InternalControlSequenceNumber is a number or code defined by the customer for audit proposes 72 // (i.e., this can include item sequence, pocket, pass information). 73 InternalControlSequenceNumber string `json:"internalControlSequenceNumber"` 74 // Time is the time associated with this transaction. The default time shall be in Eastern Time zone format. The 75 // local time zone or a specific time zone may be used under clearing arrangements. 76 // Format: hhmm, where: hh hour, mm minute 77 // Values: 78 // hh '00' through '23' 79 // mm '00' through '59' 80 Time time.Time `json:"time"` 81 // OperatorNameInitials is the name or initials of the operator or clerk processing the item. 82 OperatorName string `json:"operatorName"` 83 // OperatorNumber is a number or code identifying the operator or clerk processing the item. 84 OperatorNumber string `json:"operatorNumber"` 85 // ManagerName is The name or initials of the manager or supervisor approving the transaction. 86 ManagerName string `json:"managerName"` 87 // ManagerNumber is a number or code identifying the manager or supervisor approving the transaction. 88 ManagerNumber string `json:"managerNumber"` 89 // EquipmentNumber is number or code of the equipment/system used to process this transaction. 90 EquipmentNumber string `json:"equipmentNumber"` 91 // EndorsementIndicator is an indicator to identify the type of electronic payee endorsement associated with 92 // this transaction. 93 // Values: 94 // 0: Endorsed in Blank–Instrument becomes payable to bearer 95 // 1: For Deposit Only 96 // 2: For Collection Only 97 // 3: Anomalous Endorsement–Endorsement made by person who is not holder of instrument 98 // 4: Restrictive Endorsement–Limiting to a particular person or situation 99 // 5: Guaranteed Endorsement–Deposit to the account of within named payee absence of endorsement guaranteed by 100 // the bank whose Routing Number appears in BankRoutingNumber 101 // 9: Other 102 EndorsementIndicator int `json:"endorsementIndicator"` 103 // UserField is a field used at the discretion of users of this record. 104 UserField string `json:"userField"` 105 // validator is composed for ImageCashLetter data validation 106 validator 107 // converters is composed for to golang Converters 108 converters 109 } 110 111 // NewUserPayeeEndorsement returns a new UserPayeeEndorsement with default values for non exported fields 112 func NewUserPayeeEndorsement() *UserPayeeEndorsement { 113 upe := &UserPayeeEndorsement{ 114 UserRecordFormatType: "001", 115 } 116 upe.setRecordType() 117 return upe 118 } 119 120 func (upe *UserPayeeEndorsement) setRecordType() { 121 if upe == nil { 122 return 123 } 124 upe.recordType = "68" 125 } 126 127 // Parse takes the input record string and parses the UserPayeeEndorsement values 128 func (upe *UserPayeeEndorsement) Parse(record string) { 129 if utf8.RuneCountInString(record) < 335 { 130 return 131 } 132 133 // Character position 1-2, Always "68" 134 upe.setRecordType() 135 // 03-03 136 upe.OwnerIdentifierIndicator = upe.parseNumField(record[2:3]) 137 // 04-12 138 upe.OwnerIdentifier = upe.parseStringField(record[3:12]) 139 // 13-32 140 upe.OwnerIdentifierModifier = upe.parseStringField(record[12:32]) 141 // 33-35 142 upe.UserRecordFormatType = "001" 143 // 36-38 144 upe.FormatTypeVersionLevel = upe.parseStringField(record[35:38]) 145 // 39-45 146 upe.LengthUserData = upe.parseStringField(record[38:45]) 147 // 46-95 148 upe.PayeeName = upe.parseStringField(record[45:95]) 149 // 96–103 150 upe.EndorsementDate = upe.parseYYYYMMDDDate(record[95:103]) 151 // 104–112 152 upe.BankRoutingNumber = upe.parseStringField(record[103:112]) 153 // 113–132 154 upe.BankAccountNumber = upe.parseStringField(record[112:132]) 155 // 133–152 156 upe.CustomerIdentifier = upe.parseStringField(record[132:152]) 157 // 153–202 158 upe.CustomerContactInformation = upe.parseStringField(record[152:202]) 159 // 203–210 160 upe.StoreMerchantProcessingSiteNumber = upe.parseStringField(record[202:210]) 161 // 211–235 162 upe.InternalControlSequenceNumber = upe.parseStringField(record[210:235]) 163 // 236–239 164 upe.Time = upe.parseSimpleTime(record[235:239]) 165 // 240–269 166 upe.OperatorName = upe.parseStringField(record[239:269]) 167 // 270–274 168 upe.OperatorNumber = upe.parseStringField(record[269:274]) 169 // 275–304 170 upe.ManagerName = upe.parseStringField(record[274:304]) 171 // 305–309 172 upe.ManagerNumber = upe.parseStringField(record[304:309]) 173 // 310–324 174 upe.EquipmentNumber = upe.parseStringField(record[309:324]) 175 // 325–325 176 upe.EndorsementIndicator = upe.parseNumField(record[324:325]) 177 // 326-335 178 upe.UserField = upe.parseStringField(record[325:335]) 179 } 180 181 func (upe *UserPayeeEndorsement) UnmarshalJSON(data []byte) error { 182 type Alias UserPayeeEndorsement 183 aux := struct { 184 *Alias 185 }{ 186 (*Alias)(upe), 187 } 188 if err := json.Unmarshal(data, &aux); err != nil { 189 return err 190 } 191 upe.setRecordType() 192 return nil 193 } 194 195 // String writes the UserPayeeEndorsement struct to a variable length string. 196 func (upe *UserPayeeEndorsement) String() string { 197 if upe == nil { 198 return "" 199 } 200 201 var buf strings.Builder 202 buf.Grow(335) 203 buf.WriteString(upe.recordType) 204 buf.WriteString(upe.OwnerIdentifierIndicatorField()) 205 buf.WriteString(upe.OwnerIdentifierField()) 206 buf.WriteString(upe.OwnerIdentifierModifierField()) 207 buf.WriteString(upe.UserRecordFormatTypeField()) 208 buf.WriteString(upe.FormatTypeVersionLevelField()) 209 buf.WriteString(upe.LengthUserDataField()) 210 buf.WriteString(upe.PayeeNameField()) 211 buf.WriteString(upe.EndorsementDateField()) 212 buf.WriteString(upe.BankRoutingNumberField()) 213 buf.WriteString(upe.BankAccountNumberField()) 214 buf.WriteString(upe.CustomerIdentifierField()) 215 buf.WriteString(upe.CustomerContactInformationField()) 216 buf.WriteString(upe.StoreMerchantProcessingSiteNumberField()) 217 buf.WriteString(upe.InternalControlSequenceNumberField()) 218 buf.WriteString(upe.TimeField()) 219 buf.WriteString(upe.OperatorNameField()) 220 buf.WriteString(upe.OperatorNumberField()) 221 buf.WriteString(upe.ManagerNameField()) 222 buf.WriteString(upe.ManagerNumberField()) 223 buf.WriteString(upe.EquipmentNumberField()) 224 buf.WriteString(upe.EndorsementIndicatorField()) 225 buf.WriteString(upe.UserFieldField()) 226 return buf.String() 227 } 228 229 // Validate performs imagecashletter format rule checks on the record and returns an error if not Validated 230 // The first error encountered is returned and stops the parsing. 231 func (upe *UserPayeeEndorsement) Validate() error { 232 if err := upe.fieldInclusion(); err != nil { 233 return err 234 } 235 if upe.recordType != "68" { 236 msg := fmt.Sprintf(msgRecordType, 68) 237 return &FieldError{FieldName: "recordType", Value: upe.recordType, Msg: msg} 238 } 239 if upe.UserRecordFormatType != "001" { 240 msg := fmt.Sprint(msgInvalid) 241 return &FieldError{FieldName: "UserRecordFormatType", Value: upe.UserRecordFormatType, Msg: msg} 242 } 243 if err := upe.isNumeric(upe.FormatTypeVersionLevel); err != nil { 244 return &FieldError{FieldName: "FormatTypeVersionLevel", 245 Value: upe.FormatTypeVersionLevel, Msg: msgNumeric} 246 } 247 if err := upe.validateOwnerFields(); err != nil { 248 return err 249 } 250 if upe.CustomerIdentifier != "" { 251 if err := upe.isAlphanumericSpecial(upe.CustomerIdentifier); err != nil { 252 return &FieldError{FieldName: "CustomerIdentifier", Value: upe.CustomerIdentifier, Msg: err.Error()} 253 } 254 } 255 if upe.CustomerContactInformation != "" { 256 if err := upe.isAlphanumericSpecial(upe.CustomerContactInformation); err != nil { 257 return &FieldError{FieldName: "CustomerContactInformation", 258 Value: upe.CustomerContactInformation, Msg: err.Error()} 259 } 260 } 261 if upe.StoreMerchantProcessingSiteNumber != "" { 262 if err := upe.isAlphanumericSpecial(upe.StoreMerchantProcessingSiteNumber); err != nil { 263 return &FieldError{FieldName: "StoreMerchantProcessingSiteNumber", 264 Value: upe.StoreMerchantProcessingSiteNumber, Msg: err.Error()} 265 } 266 } 267 if upe.InternalControlSequenceNumber != "" { 268 if err := upe.isAlphanumericSpecial(upe.InternalControlSequenceNumber); err != nil { 269 return &FieldError{FieldName: "InternalControlSequenceNumber", 270 Value: upe.InternalControlSequenceNumber, Msg: err.Error()} 271 } 272 } 273 if upe.EndorsementIndicatorField() != "" { 274 if err := upe.isEndorsementIndicator(upe.EndorsementIndicator); err != nil { 275 return &FieldError{FieldName: "EndorsementIndicator", 276 Value: upe.EndorsementIndicatorField(), Msg: err.Error()} 277 } 278 } 279 280 if upe.UserField != "" { 281 if err := upe.isAlphanumericSpecial(upe.UserField); err != nil { 282 return &FieldError{FieldName: "UserField", Value: upe.UserField, Msg: err.Error()} 283 } 284 } 285 286 if err := upe.validateNameNumberFields(); err != nil { 287 return err 288 } 289 return nil 290 291 } 292 293 func (upe *UserPayeeEndorsement) validateNameNumberFields() error { 294 if upe.PayeeName != "" { 295 if err := upe.isAlphanumericSpecial(upe.PayeeName); err != nil { 296 return &FieldError{FieldName: "PayeeName", Value: upe.PayeeName, Msg: err.Error()} 297 } 298 } 299 if upe.BankRoutingNumber != "" { 300 if err := upe.isNumeric(upe.BankRoutingNumber); err != nil { 301 return &FieldError{FieldName: "BankRoutingNumber", Value: upe.BankRoutingNumber, Msg: err.Error()} 302 } 303 } 304 if upe.BankAccountNumber != "" { 305 if err := upe.isAlphanumericSpecial(upe.BankAccountNumber); err != nil { 306 return &FieldError{FieldName: "BankAccountNumber", Value: upe.BankAccountNumber, Msg: err.Error()} 307 } 308 } 309 if upe.OperatorName != "" { 310 if err := upe.isAlphanumericSpecial(upe.OperatorName); err != nil { 311 return &FieldError{FieldName: "OperatorName", Value: upe.OperatorName, Msg: err.Error()} 312 } 313 } 314 if upe.OperatorNumber != "" { 315 if err := upe.isAlphanumericSpecial(upe.OperatorNumber); err != nil { 316 return &FieldError{FieldName: "OperatorNumber", Value: upe.OperatorNumber, Msg: err.Error()} 317 } 318 } 319 if upe.ManagerName != "" { 320 if err := upe.isAlphanumericSpecial(upe.ManagerName); err != nil { 321 return &FieldError{FieldName: "ManagerName", Value: upe.ManagerName, Msg: err.Error()} 322 } 323 } 324 if upe.ManagerNumber != "" { 325 if err := upe.isAlphanumericSpecial(upe.ManagerNumber); err != nil { 326 return &FieldError{FieldName: "ManagerNumber", Value: upe.ManagerNumber, Msg: err.Error()} 327 } 328 } 329 if upe.EquipmentNumber != "" { 330 if err := upe.isAlphanumericSpecial(upe.EquipmentNumber); err != nil { 331 return &FieldError{FieldName: "EquipmentNumber", Value: upe.EquipmentNumber, Msg: err.Error()} 332 } 333 } 334 335 return nil 336 } 337 338 func (upe *UserPayeeEndorsement) validateOwnerFields() error { 339 340 if err := upe.isOwnerIdentifierIndicator(upe.OwnerIdentifierIndicator); err != nil { 341 return &FieldError{FieldName: "OwnerIdentifierIndicator", 342 Value: upe.OwnerIdentifierIndicatorField(), Msg: err.Error()} 343 } 344 if upe.OwnerIdentifierModifier != "" { 345 if err := upe.isAlphanumericSpecial(upe.OwnerIdentifierModifier); err != nil { 346 return &FieldError{FieldName: "OwnerIdentifierModifier", Value: upe.OwnerIdentifierModifier, Msg: err.Error()} 347 } 348 } 349 350 switch upe.OwnerIdentifierIndicator { 351 case 0: 352 if upe.OwnerIdentifier != "" { 353 return &FieldError{FieldName: "OwnerIdentifier", Value: upe.OwnerIdentifier, Msg: msgInvalid} 354 } 355 case 1, 2, 3: 356 if err := upe.isNumeric(upe.OwnerIdentifier); err != nil { 357 return &FieldError{FieldName: "OwnerIdentifier", Value: upe.OwnerIdentifier, Msg: err.Error()} 358 } 359 case 4: 360 if err := upe.isAlphanumericSpecial(upe.OwnerIdentifier); err != nil { 361 return &FieldError{FieldName: "OwnerIdentifier", Value: upe.OwnerIdentifier, Msg: err.Error()} 362 } 363 default: 364 } 365 if upe.LengthUserData != "0000290" { 366 return &FieldError{FieldName: "LengthUserData", Value: upe.LengthUserData, Msg: msgInvalid} 367 } 368 return nil 369 } 370 371 // fieldInclusion validate mandatory fields are not default values. If fields are 372 // invalid the Electronic Exchange will be returned. 373 func (upe *UserPayeeEndorsement) fieldInclusion() error { 374 if upe.recordType == "" { 375 return &FieldError{FieldName: "recordType", 376 Value: upe.recordType, 377 Msg: msgFieldInclusion + ", did you use UserPayeeEndorsement()?"} 378 } 379 if upe.UserRecordFormatType == "" { 380 return &FieldError{FieldName: "UserRecordFormatType", 381 Value: upe.UserRecordFormatType, 382 Msg: msgFieldInclusion + ", did you use UserPayeeEndorsement()?"} 383 } 384 if upe.FormatTypeVersionLevel == "" { 385 return &FieldError{FieldName: "FormatTypeVersionLevel", 386 Value: upe.FormatTypeVersionLevel, 387 Msg: msgFieldInclusion + ", did you use UserPayeeEndorsement()?"} 388 } 389 if upe.LengthUserData == "" { 390 return &FieldError{FieldName: "LengthUserData", 391 Value: upe.LengthUserData, 392 Msg: msgFieldInclusion + ", did you use UserPayeeEndorsement()?"} 393 } 394 return nil 395 } 396 397 // OwnerIdentifierIndicatorField gets the OwnerIdentifierIndicator field 398 func (upe *UserPayeeEndorsement) OwnerIdentifierIndicatorField() string { 399 return upe.numericField(upe.OwnerIdentifierIndicator, 1) 400 } 401 402 // OwnerIdentifierField gets the OwnerIdentifier field 403 func (upe *UserPayeeEndorsement) OwnerIdentifierField() string { 404 return upe.alphaField(upe.OwnerIdentifier, 9) 405 } 406 407 // OwnerIdentifierModifierField gets the OwnerIdentifierModifier field 408 func (upe *UserPayeeEndorsement) OwnerIdentifierModifierField() string { 409 return upe.alphaField(upe.OwnerIdentifierModifier, 20) 410 } 411 412 // UserRecordFormatTypeField gets the UserRecordFormatType field 413 func (upe *UserPayeeEndorsement) UserRecordFormatTypeField() string { 414 return upe.alphaField(upe.UserRecordFormatType, 3) 415 } 416 417 // FormatTypeVersionLevelField gets the FormatTypeVersionLevel field 418 func (upe *UserPayeeEndorsement) FormatTypeVersionLevelField() string { 419 return upe.alphaField(upe.FormatTypeVersionLevel, 3) 420 } 421 422 // LengthUserDataField gets the LengthUserData field 423 func (upe *UserPayeeEndorsement) LengthUserDataField() string { 424 return upe.alphaField(upe.LengthUserData, 7) 425 } 426 427 // PayeeNameField gets the PayeeName field 428 func (upe *UserPayeeEndorsement) PayeeNameField() string { 429 return upe.alphaField(upe.PayeeName, 50) 430 } 431 432 // EndorsementDateField gets the EndorsementDate field 433 func (upe *UserPayeeEndorsement) EndorsementDateField() string { 434 return upe.formatYYYYMMDDDate(upe.EndorsementDate) 435 } 436 437 // BankRoutingNumberField gets the BankRoutingNumber field 438 func (upe *UserPayeeEndorsement) BankRoutingNumberField() string { 439 return upe.alphaField(upe.BankRoutingNumber, 9) 440 } 441 442 // BankAccountNumberField gets the BankAccountNumber field 443 func (upe *UserPayeeEndorsement) BankAccountNumberField() string { 444 return upe.alphaField(upe.BankAccountNumber, 20) 445 } 446 447 // CustomerIdentifierField gets the CustomerIdentifier field 448 func (upe *UserPayeeEndorsement) CustomerIdentifierField() string { 449 return upe.alphaField(upe.CustomerIdentifier, 20) 450 } 451 452 // CustomerContactInformationField gets the CustomerContactInformation field 453 func (upe *UserPayeeEndorsement) CustomerContactInformationField() string { 454 return upe.alphaField(upe.CustomerContactInformation, 50) 455 } 456 457 // StoreMerchantProcessingSiteNumberField gets the StoreMerchantProcessingSiteNumber field 458 func (upe *UserPayeeEndorsement) StoreMerchantProcessingSiteNumberField() string { 459 return upe.alphaField(upe.StoreMerchantProcessingSiteNumber, 8) 460 } 461 462 // InternalControlSequenceNumberField gets the InternalControlSequenceNumber field 463 func (upe *UserPayeeEndorsement) InternalControlSequenceNumberField() string { 464 return upe.alphaField(upe.InternalControlSequenceNumber, 25) 465 } 466 467 // TimeField gets the Time field 468 func (upe *UserPayeeEndorsement) TimeField() string { 469 return upe.formatSimpleTime(upe.Time) 470 } 471 472 // OperatorNameField gets the OperatorName field 473 func (upe *UserPayeeEndorsement) OperatorNameField() string { 474 return upe.alphaField(upe.OperatorName, 30) 475 } 476 477 // OperatorNumberField gets the OperatorNumber field 478 func (upe *UserPayeeEndorsement) OperatorNumberField() string { 479 return upe.alphaField(upe.OperatorNumber, 5) 480 } 481 482 // ManagerNameField gets the ManagerName field 483 func (upe *UserPayeeEndorsement) ManagerNameField() string { 484 return upe.alphaField(upe.ManagerName, 30) 485 } 486 487 // ManagerNumberField gets the ManagerNumber field 488 func (upe *UserPayeeEndorsement) ManagerNumberField() string { 489 return upe.alphaField(upe.ManagerNumber, 5) 490 } 491 492 // EquipmentNumberField gets the EquipmentNumber field 493 func (upe *UserPayeeEndorsement) EquipmentNumberField() string { 494 return upe.alphaField(upe.EquipmentNumber, 15) 495 } 496 497 // EndorsementIndicatorField gets the EndorsementIndicator field 498 func (upe *UserPayeeEndorsement) EndorsementIndicatorField() string { 499 return upe.numericField(upe.EndorsementIndicator, 1) 500 } 501 502 // UserFieldField gets the UserField field 503 func (upe *UserPayeeEndorsement) UserFieldField() string { 504 return upe.alphaField(upe.UserField, 10) 505 }