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  }