github.com/moov-io/imagecashletter@v0.10.1/cashLetterControl.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 // Errors specific to a CashLetterControl Record 16 17 // CashLetterControl Record 18 type CashLetterControl struct { 19 // ID is a client defined string used as a reference to this record. 20 ID string `json:"id"` 21 // RecordType defines the type of record. 22 recordType string 23 // CashLetterBundleCount identifies the total number of bundles within the cash letter. 24 CashLetterBundleCount int `json:"cashLetterBundleCount"` 25 // CashLetterItemsCount identifies the total number of items within the cash letter. 26 CashLetterItemsCount int `json:"cashLetterItemsCount"` 27 // CashLetterTotalAmount identifies the total dollar value of all item amounts within the cash letter. 28 CashLetterTotalAmount int `json:"cashLetterTotalAmount"` 29 // CashLetterImagesCount identifies the total number of ImageViewDetail(s) within the CashLetter. 30 CashLetterImagesCount int `json:"cashLetterImagesCount"` 31 // ECEInstitutionName identifies the short name of the institution that creates the CashLetterControl. 32 ECEInstitutionName string `json:"eceInstitutionName"` 33 // SettlementDate identifies the date that the institution that creates the cash letter expects settlement. 34 // Format: YYYYMMDD, where: YYYY year, MM month, DD day 35 // Values: 36 // YYYY 1993 through 9999 37 // MM 01 through 12 38 // DD 01 through 31 39 SettlementDate time.Time `json:"settlementDate"` 40 // CreditTotalIndicator identifies a code that indicates whether Credits Items are included in the totals. 41 // If so they will be included in Items CashLetterItemsCount, CashLetterTotalAmount and CashLetterImagesCount. 42 // Values: 43 // 0: Credit Items are not included in totals 44 // 1: Credit Items are included in totals 45 CreditTotalIndicator int `json:"creditTotalIndicator"` 46 // reserved is a field reserved for future use. Reserved should be blank. 47 reserved string 48 // validator is composed for imagecashletter data validation 49 validator 50 // converters is composed for imagecashletter to golang Converters 51 converters 52 } 53 54 // NewCashLetterControl returns a new CashLetterControl with default values for non exported fields 55 func NewCashLetterControl() *CashLetterControl { 56 clc := &CashLetterControl{} 57 clc.setRecordType() 58 return clc 59 } 60 61 func (clc *CashLetterControl) setRecordType() { 62 if clc == nil { 63 return 64 } 65 66 clc.recordType = "90" 67 if clc.SettlementDate.IsZero() { 68 clc.SettlementDate = time.Now() 69 } 70 clc.reserved = " " 71 } 72 73 // Parse takes the input record string and parses the CashLetterControl values 74 func (clc *CashLetterControl) Parse(record string) { 75 if utf8.RuneCountInString(record) != 80 { 76 return 77 } 78 79 // Character position 1-2, Always "90" 80 clc.setRecordType() 81 // 03-08 82 clc.CashLetterBundleCount = clc.parseNumField(record[2:8]) 83 // 09-16 84 clc.CashLetterItemsCount = clc.parseNumField(record[8:16]) 85 // 17-30 86 clc.CashLetterTotalAmount = clc.parseNumField(record[16:30]) 87 // 31-39 88 clc.CashLetterImagesCount = clc.parseNumField(record[30:39]) 89 // 40-57 90 clc.ECEInstitutionName = clc.parseStringField(record[39:57]) 91 // 58-65 92 clc.SettlementDate = clc.parseYYYYMMDDDate(record[57:65]) 93 // 66-66 94 clc.CreditTotalIndicator = clc.parseNumField(record[65:66]) 95 // 67-80 96 clc.reserved = " " 97 } 98 99 func (clc *CashLetterControl) UnmarshalJSON(data []byte) error { 100 type Alias CashLetterControl 101 aux := struct { 102 *Alias 103 }{ 104 (*Alias)(clc), 105 } 106 if err := json.Unmarshal(data, &aux); err != nil { 107 return err 108 } 109 clc.setRecordType() 110 return nil 111 } 112 113 // String writes the CashLetterControl struct to a string. 114 func (clc *CashLetterControl) String() string { 115 if clc == nil { 116 return "" 117 } 118 119 var buf strings.Builder 120 buf.Grow(80) 121 buf.WriteString(clc.recordType) 122 buf.WriteString(clc.CashLetterBundleCountField()) 123 buf.WriteString(clc.CashLetterItemsCountField()) 124 buf.WriteString(clc.CashLetterTotalAmountField()) 125 buf.WriteString(clc.CashLetterImagesCountField()) 126 buf.WriteString(clc.ECEInstitutionNameField()) 127 buf.WriteString(clc.SettlementDateField()) 128 buf.WriteString(clc.CreditTotalIndicatorField()) 129 buf.WriteString(clc.reservedField()) 130 return buf.String() 131 } 132 133 // Validate performs imagecashletter format rule checks on the record and returns an error if not Validated 134 // The first error encountered is returned and stops the parsing. 135 func (clc *CashLetterControl) Validate() error { 136 if err := clc.fieldInclusion(); err != nil { 137 return err 138 } 139 if clc.recordType != "90" { 140 msg := fmt.Sprintf(msgRecordType, 90) 141 return &FieldError{FieldName: "recordType", Value: clc.recordType, Msg: msg} 142 } 143 if err := clc.isAlphanumericSpecial(clc.ECEInstitutionName); err != nil { 144 return &FieldError{FieldName: "ECEInstitutionName", Value: clc.ECEInstitutionName, Msg: err.Error()} 145 } 146 if clc.CreditTotalIndicatorField() != "" { 147 if err := clc.isCreditTotalIndicator(clc.CreditTotalIndicator); err != nil { 148 return &FieldError{FieldName: "CreditTotalIndicator", Value: clc.CreditTotalIndicatorField(), Msg: err.Error()} 149 } 150 } 151 return nil 152 } 153 154 // fieldInclusion validate mandatory fields are not default values. If fields are 155 // invalid the Electronic Exchange will be returned. 156 func (clc *CashLetterControl) fieldInclusion() error { 157 if clc.recordType == "" { 158 return &FieldError{FieldName: "recordType", 159 Value: clc.recordType, 160 Msg: msgFieldInclusion + ", did you use CashLetterControl()?"} 161 } 162 if clc.CashLetterItemsCount == 0 { 163 return &FieldError{FieldName: "CashLetterItemsCount", 164 Value: clc.CashLetterItemsCountField(), 165 Msg: msgFieldInclusion + ", did you use CashLetterControl()?"} 166 } 167 if clc.CashLetterTotalAmount == 0 { 168 return &FieldError{FieldName: "CashLetterTotalAmount", 169 Value: clc.CashLetterTotalAmountField(), 170 Msg: msgFieldInclusion + ", did you use CashLetterControl()?"} 171 } 172 173 // optional field - if present, year must be between 1993 and 9999 174 if date := clc.SettlementDate; !date.IsZero() { 175 if date.Year() < 1993 || date.Year() > 9999 { 176 return &FieldError{FieldName: "SettlementDate", 177 Value: clc.SettlementDateField(), Msg: msgInvalidDate + ": year must be between 1993 and 9999"} 178 } 179 } 180 181 return nil 182 } 183 184 // CashLetterBundleCountField gets a string of the CashLetterBundleCount zero padded 185 func (clc *CashLetterControl) CashLetterBundleCountField() string { 186 return clc.numericField(clc.CashLetterBundleCount, 6) 187 } 188 189 // CashLetterItemsCountField gets a string of the CashLetterItemsCount zero padded 190 func (clc *CashLetterControl) CashLetterItemsCountField() string { 191 return clc.numericField(clc.CashLetterItemsCount, 8) 192 } 193 194 // CashLetterTotalAmountField gets a string of the CashLetterTotalAmount zero padded 195 func (clc *CashLetterControl) CashLetterTotalAmountField() string { 196 return clc.numericField(clc.CashLetterTotalAmount, 14) 197 } 198 199 // CashLetterImagesCountField gets a string of the CashLetterImagesCount zero padded 200 func (clc *CashLetterControl) CashLetterImagesCountField() string { 201 return clc.numericField(clc.CashLetterImagesCount, 9) 202 } 203 204 // ECEInstitutionNameField gets the ECEInstitutionName field 205 func (clc *CashLetterControl) ECEInstitutionNameField() string { 206 return clc.alphaField(clc.ECEInstitutionName, 18) 207 } 208 209 // SettlementDateField gets the SettlementDate in YYYYMMDD format 210 func (clc *CashLetterControl) SettlementDateField() string { 211 return clc.formatYYYYMMDDDate(clc.SettlementDate) 212 } 213 214 // CreditTotalIndicatorField gets a string of the CreditTotalIndicator field 215 func (clc *CashLetterControl) CreditTotalIndicatorField() string { 216 return clc.numericField(clc.CreditTotalIndicator, 1) 217 } 218 219 // reservedField gets reserved - blank space 220 func (clc *CashLetterControl) reservedField() string { 221 return clc.alphaField(clc.reserved, 14) 222 } 223 224 func isReturnCollectionType(code string) bool { 225 if code == "03" || code == "04" || code == "05" || code == "06" { 226 return true 227 } 228 return false 229 }