github.com/moov-io/imagecashletter@v0.10.1/bundleControl.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 "unicode/utf8" 12 ) 13 14 // Errors specific to a BundleControl Record 15 16 // BundleControl Record 17 type BundleControl struct { 18 // ID is a client defined string used as a reference to this record. 19 ID string `json:"id"` 20 // RecordType defines the type of record. 21 recordType string 22 // BundleItemsCount identifies the total number of items within the bundle. 23 BundleItemsCount int `json:"bundleitemsCount"` 24 // BundleTotalAmount identifies the total amount of item amounts within the bundle. 25 BundleTotalAmount int `json:"bundleTotalAmount"` 26 // MICRValidTotalAmount identifies the total amount of all CheckDetail Records within the bundle which 27 // contains 1 in the MICRValidIndicator . 28 MICRValidTotalAmount int `json:"micrValidTotalAmount"` 29 // BundleImagesCount identifies the total number of Image ViewDetail Records within the bundle. 30 BundleImagesCount int `json:"bundleImagesCount"` 31 // UserField is used at the discretion of users of the standard. 32 UserField string `json:"userField"` 33 // CreditTotalIndicator identifies a code that indicates whether Credits Items are included in the totals. 34 // If so they will be included in Items CashLetterItemsCount, CashLetterTotalAmount and 35 // CashLetterImagesCount. 36 // Values: 37 // 0: Credit Items are not included in totals 38 // 1: Credit Items are included in totals 39 CreditTotalIndicator int `json:"creditTotalIndicator"` 40 // reserved is a field reserved for future use. Reserved should be blank. 41 reserved string 42 // validator is composed for image cash letter data validation 43 validator 44 // converters is composed for image cash letter to golang Converters 45 converters 46 } 47 48 // NewBundleControl returns a new BundleControl with default values for non exported fields 49 func NewBundleControl() *BundleControl { 50 bc := &BundleControl{} 51 bc.setRecordType() 52 return bc 53 } 54 55 func (bc *BundleControl) setRecordType() { 56 if bc == nil { 57 return 58 } 59 bc.recordType = "70" 60 bc.reserved = " " 61 } 62 63 // Parse takes the input record string and parses the BundleControl values 64 func (bc *BundleControl) Parse(record string) { 65 if utf8.RuneCountInString(record) < 56 { 66 return 67 } 68 69 // Character position 1-2, Always "70" 70 bc.setRecordType() 71 // 03-06 72 bc.BundleItemsCount = bc.parseNumField(record[2:6]) 73 // 07-18 74 bc.BundleTotalAmount = bc.parseNumField(record[6:18]) 75 // 19-30 76 bc.MICRValidTotalAmount = bc.parseNumField(record[18:30]) 77 // 31-35 78 bc.BundleImagesCount = bc.parseNumField(record[30:35]) 79 // 36-55 80 bc.UserField = bc.parseStringField(record[35:55]) 81 // 56-56 82 bc.CreditTotalIndicator = bc.parseNumField(record[55:56]) 83 // 57-80 84 bc.reserved = " " 85 86 } 87 88 func (bc *BundleControl) UnmarshalJSON(data []byte) error { 89 type Alias BundleControl 90 aux := struct { 91 *Alias 92 }{ 93 (*Alias)(bc), 94 } 95 if err := json.Unmarshal(data, &aux); err != nil { 96 return err 97 } 98 bc.setRecordType() 99 return nil 100 } 101 102 // String writes the BundleControl struct to a string. 103 func (bc *BundleControl) String() string { 104 if bc == nil { 105 return "" 106 } 107 108 var buf strings.Builder 109 buf.Grow(80) 110 buf.WriteString(bc.recordType) 111 buf.WriteString(bc.BundleItemsCountField()) 112 buf.WriteString(bc.BundleTotalAmountField()) 113 buf.WriteString(bc.MICRValidTotalAmountField()) 114 buf.WriteString(bc.BundleImagesCountField()) 115 buf.WriteString(bc.UserFieldField()) 116 buf.WriteString(bc.CreditTotalIndicatorField()) 117 buf.WriteString(bc.reservedField()) 118 return buf.String() 119 } 120 121 // Validate performs image cash letter format rule checks on the record and returns an error if not Validated 122 // The first error encountered is returned and stops the parsing. 123 func (bc *BundleControl) Validate() error { 124 if err := bc.fieldInclusion(); err != nil { 125 return err 126 } 127 if bc.recordType != "70" { 128 msg := fmt.Sprintf(msgRecordType, 70) 129 return &FieldError{FieldName: "recordType", Value: bc.recordType, Msg: msg} 130 } 131 if err := bc.isAlphanumericSpecial(bc.UserField); err != nil { 132 return &FieldError{FieldName: "UserField", Value: bc.UserField, Msg: err.Error()} 133 } 134 if bc.CreditTotalIndicatorField() != "" { 135 if err := bc.isCreditTotalIndicator(bc.CreditTotalIndicator); err != nil { 136 return &FieldError{FieldName: "CreditTotalIndicator", Value: bc.CreditTotalIndicatorField(), Msg: err.Error()} 137 } 138 } 139 return nil 140 } 141 142 // fieldInclusion validate mandatory fields are not default values. If fields are 143 // invalid the Electronic Exchange will be returned. 144 func (bc *BundleControl) fieldInclusion() error { 145 if bc.recordType == "" { 146 return &FieldError{FieldName: "recordType", 147 Value: bc.recordType, 148 Msg: msgFieldInclusion + ", did you use BundleControl()?"} 149 } 150 if bc.BundleItemsCount == 0 { 151 return &FieldError{FieldName: "BundleItemsCount", 152 Value: bc.BundleItemsCountField(), 153 Msg: msgFieldInclusion + ", did you use BundleControl()?"} 154 } 155 if bc.BundleTotalAmount == 0 { 156 return &FieldError{FieldName: "BundleTotalAmount", 157 Value: bc.BundleTotalAmountField(), 158 Msg: msgFieldInclusion + ", did you use BundleControl()?"} 159 } 160 return nil 161 } 162 163 // BundleItemsCountField gets a string of the BundleItemsCount zero padded 164 func (bc *BundleControl) BundleItemsCountField() string { 165 return bc.numericField(bc.BundleItemsCount, 4) 166 } 167 168 // BundleTotalAmountField gets a string of the BundleTotalAmount zero padded 169 func (bc *BundleControl) BundleTotalAmountField() string { 170 return bc.numericField(bc.BundleTotalAmount, 12) 171 } 172 173 // MICRValidTotalAmountField gets a string of the MICRValidTotalAmount zero padded 174 func (bc *BundleControl) MICRValidTotalAmountField() string { 175 return bc.numericField(bc.MICRValidTotalAmount, 12) 176 } 177 178 // BundleImagesCountField gets a string of the BundleImagesCount zero padded 179 func (bc *BundleControl) BundleImagesCountField() string { 180 return bc.numericField(bc.BundleImagesCount, 5) 181 } 182 183 // UserFieldField gets the UserField field 184 func (bc *BundleControl) UserFieldField() string { 185 return bc.alphaField(bc.UserField, 20) 186 } 187 188 // CreditTotalIndicatorField gets a string of the CreditTotalIndicator field 189 func (bc *BundleControl) CreditTotalIndicatorField() string { 190 return bc.numericField(bc.CreditTotalIndicator, 1) 191 } 192 193 // reservedField gets reserved - blank space 194 func (bc *BundleControl) reservedField() string { 195 return bc.alphaField(bc.reserved, 24) 196 }