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  }