Skip to content

Commit

Permalink
updated ssn and birth
Browse files Browse the repository at this point in the history
  • Loading branch information
mfdeveloper508 committed Apr 12, 2023
1 parent db4f86c commit 16fc29e
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 32 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ test/fuzz-reader/corpus/*.tar.gz
go-licenser*.tar.gz
go-licenser

openapi-generator*.jar
openapi-generator*.jar

/vendor/
6 changes: 0 additions & 6 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,6 @@ components:
- dateAccountInformation
- surname
- firstName
- socialSecurityNumber
- dateBirth
- ecoaCode
- firstLineAddress
- state
Expand Down Expand Up @@ -693,8 +691,6 @@ components:
- segmentIdentifier
- surname
- firstName
- socialSecurityNumber
- dateBirth
- ecoaCode
J2Segment:
properties:
Expand Down Expand Up @@ -760,8 +756,6 @@ components:
- segmentIdentifier
- surname
- firstName
- socialSecurityNumber
- dateBirth
- ecoaCode
- firstLineAddress
- city
Expand Down
22 changes: 11 additions & 11 deletions pkg/file/file_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ func (f *fileInstance) String(isNewLine bool) string {
}

// Bytes return raw byte array
func (r *fileInstance) Bytes() []byte {
return []byte(r.String(false))
func (f *fileInstance) Bytes() []byte {
return []byte(f.String(false))
}

// UnmarshalJSON parses a JSON blob
Expand Down Expand Up @@ -437,29 +437,29 @@ func (f *fileInstance) generatorTrailer() (*lib.TrailerInformation, error) {
trailer.TotalBaseRecords = len(f.Bases)
trailer.BlockCount = len(f.Bases) + 2
for _, base := range f.Bases {
base, ok := base.(*lib.BaseSegment)
if !ok && base.Validate() != nil {
return nil, utils.NewErrInvalidSegment(base.Name())
baseSegment, ok := base.(*lib.BaseSegment)
if !ok && baseSegment.Validate() != nil {
return nil, utils.NewErrInvalidSegment(baseSegment.Name())
}

if isValidSocialSecurityNumber(base.SocialSecurityNumber) {
if isValidSocialSecurityNumber(baseSegment.SocialSecurityNumber) {
trailer.TotalSocialNumbersAllSegments++
trailer.TotalSocialNumbersBaseSegments++
}

if !base.DateBirth.IsZero() {
if !baseSegment.DateBirth.IsZero() {
trailer.TotalDatesBirthAllSegments++
trailer.TotalDatesBirthBaseSegments++
}

if base.ECOACode == lib.ECOACodeZ {
if baseSegment.ECOACode == lib.ECOACodeZ {
trailer.TotalECOACodeZ++
}
if base.TelephoneNumber > 0 {
if baseSegment.TelephoneNumber > 0 {
trailer.TotalTelephoneNumbersAllSegments++
}
f.statisticAccountStatus(base.AccountStatus, trailer)
f.statisticBase(base, trailer)
f.statisticAccountStatus(baseSegment.AccountStatus, trailer)
f.statisticBase(baseSegment, trailer)
}

return trailer, nil
Expand Down
52 changes: 50 additions & 2 deletions pkg/lib/base_segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ type BaseSegment struct {
// If the Social Security Number is not reported, the Date of Birth is required to be reported.
// Do not report Individual Tax Identification Numbers (ITINs) in this field. ITINs do not prove identity outside the tax system and should not be offered or accepted as identification for non-tax purposes, per the Social Security Administration.
// Do not report Credit Profile Numbers (CPNs) in this field. The CPN should not be used for credit reporting purposes and does not replace the Social Security Number.
SocialSecurityNumber int `json:"socialSecurityNumber" validate:"required"`
SocialSecurityNumber int `json:"socialSecurityNumber,omitempty"`

// Report the full Date of Birth of the primary consumer, including the month, day and year.
// Reporting of this information is required as the Date of Birth greatly enhances accuracy in matching to the correct consumer.
Expand All @@ -393,7 +393,7 @@ type BaseSegment struct {
// Notes: If the Date of Birth is not reported, the Social Security Number is required to be reported.
// When reporting Authorized Users (ECOA Code 3), the full Date of Birth (MMDDYYYY) must be reported for all newly-added Authorized Users on all pre-existing and newly-opened accounts, even if the Social Security Number is reported.
// Do not report accounts of consumers who are too young to enter into a binding contract.
DateBirth utils.Time `json:"dateBirth" validate:"required"`
DateBirth utils.Time `json:"dateBirth,omitempty"`

// Contains the telephone number of the primary consumer (Area Code + 7 digits).
TelephoneNumber int64 `json:"telephoneNumber"`
Expand Down Expand Up @@ -805,13 +805,15 @@ func (r *BaseSegment) UnmarshalJSON(data []byte) error {
// customized field validation functions
// function name should be "Validate" + field name

// validation of identification number
func (r *BaseSegment) ValidateIdentificationNumber() error {
if validFilledString(r.IdentificationNumber) {
return utils.NewErrInvalidValueOfField("identification number", "base segment")
}
return nil
}

// validation of portfolio type
func (r *BaseSegment) ValidatePortfolioType() error {
switch r.PortfolioType {
case PortfolioTypeCredit, PortfolioTypeInstallment, PortfolioTypeMortgage, PortfolioTypeOpen, PortfolioTypeRevolving:
Expand All @@ -820,6 +822,7 @@ func (r *BaseSegment) ValidatePortfolioType() error {
return utils.NewErrInvalidValueOfField("portfolio type", "base segment")
}

// validation of terms duration
func (r *BaseSegment) ValidateTermsDuration() error {
switch r.TermsDuration {
case TermsDurationCredit, TermsDurationOpen, TermsDurationRevolving:
Expand All @@ -832,6 +835,7 @@ func (r *BaseSegment) ValidateTermsDuration() error {
return nil
}

// validation of terms frequency
func (r *BaseSegment) ValidateTermsFrequency() error {
switch r.TermsFrequency {
case TermsFrequencyDeferred, TermsFrequencyPayment, TermsFrequencyWeekly, TermsFrequencyBiweekly,
Expand All @@ -842,6 +846,7 @@ func (r *BaseSegment) ValidateTermsFrequency() error {
return utils.NewErrInvalidValueOfField("terms frequency", "base segment")
}

// validation of payment rating
func (r *BaseSegment) ValidatePaymentRating() error {
switch r.AccountStatus {
case AccountStatus05, AccountStatus13, AccountStatus65, AccountStatus88, AccountStatus89, AccountStatus94, AccountStatus95:
Expand All @@ -859,6 +864,7 @@ func (r *BaseSegment) ValidatePaymentRating() error {
return utils.NewErrInvalidValueOfField("payment rating", "base segment")
}

// validation of payment history profile
func (r *BaseSegment) ValidatePaymentHistoryProfile() error {
if len(r.PaymentHistoryProfile) != 24 {
return utils.NewErrInvalidValueOfField("payment history profile", "base segment")
Expand All @@ -877,6 +883,7 @@ func (r *BaseSegment) ValidatePaymentHistoryProfile() error {
return nil
}

// validation of interest type indicator
func (r *BaseSegment) ValidateInterestTypeIndicator() error {
switch r.InterestTypeIndicator {
case InterestIndicatorFixed, InterestIndicatorVariable, "":
Expand All @@ -885,13 +892,30 @@ func (r *BaseSegment) ValidateInterestTypeIndicator() error {
return utils.NewErrInvalidValueOfField("interest type indicator", "base segment")
}

// validation of telephone number
func (r *BaseSegment) ValidateTelephoneNumber() error {
if err := r.isPhoneNumber(r.TelephoneNumber, "base segment"); err != nil {
return err
}
return nil
}

// validation of social security number
func (r *BaseSegment) ValidateSocialSecurityNumber() error {
if r.SocialSecurityNumber == 0 && r.DateBirth.IsZero() {
return utils.NewErrInvalidValueOfField("social security number", "base segment")
}
return nil
}

// validation of date of birth
func (r *BaseSegment) ValidateDateBirth() error {
if r.SocialSecurityNumber == 0 && r.DateBirth.IsZero() {
return utils.NewErrInvalidValueOfField("date birth", "base segment")
}
return nil
}

// Name returns name of packed base segment
func (r *PackedBaseSegment) Name() string {
return PackedBaseSegmentName
Expand Down Expand Up @@ -1230,13 +1254,15 @@ func (r *PackedBaseSegment) UnmarshalJSON(data []byte) error {
// customized field validation functions
// function name should be "Validate" + field name

// validation of identification number
func (r *PackedBaseSegment) ValidateIdentificationNumber() error {
if validFilledString(r.IdentificationNumber) {
return utils.NewErrInvalidValueOfField("identification number", "packed base segment")
}
return nil
}

// validation of portfolio type
func (r *PackedBaseSegment) ValidatePortfolioType() error {
switch r.PortfolioType {
case PortfolioTypeCredit, PortfolioTypeInstallment, PortfolioTypeMortgage, PortfolioTypeOpen, PortfolioTypeRevolving:
Expand All @@ -1245,6 +1271,7 @@ func (r *PackedBaseSegment) ValidatePortfolioType() error {
return utils.NewErrInvalidValueOfField("portfolio type", "packed base segment")
}

// validation of terms duration
func (r *PackedBaseSegment) ValidateTermsDuration() error {
switch r.TermsDuration {
case TermsDurationCredit, TermsDurationOpen, TermsDurationRevolving:
Expand All @@ -1257,6 +1284,7 @@ func (r *PackedBaseSegment) ValidateTermsDuration() error {
return nil
}

// validation of terms frequency
func (r *PackedBaseSegment) ValidateTermsFrequency() error {
switch r.TermsFrequency {
case TermsFrequencyDeferred, TermsFrequencyPayment, TermsFrequencyWeekly, TermsFrequencyBiweekly,
Expand All @@ -1267,6 +1295,7 @@ func (r *PackedBaseSegment) ValidateTermsFrequency() error {
return utils.NewErrInvalidValueOfField("terms frequency", "packed base segment")
}

// validation of payment rating
func (r *PackedBaseSegment) ValidatePaymentRating() error {
switch r.AccountStatus {
case AccountStatus05, AccountStatus13, AccountStatus65, AccountStatus88, AccountStatus89, AccountStatus94, AccountStatus95:
Expand All @@ -1284,6 +1313,7 @@ func (r *PackedBaseSegment) ValidatePaymentRating() error {
return utils.NewErrInvalidValueOfField("payment rating", "packed base segment")
}

// validation of payment history profile
func (r *PackedBaseSegment) ValidatePaymentHistoryProfile() error {
if len(r.PaymentHistoryProfile) != 24 {
return utils.NewErrInvalidValueOfField("payment history profile", "packed base segment")
Expand All @@ -1302,6 +1332,7 @@ func (r *PackedBaseSegment) ValidatePaymentHistoryProfile() error {
return nil
}

// validation of interest type indicator
func (r *PackedBaseSegment) ValidateInterestTypeIndicator() error {
switch r.InterestTypeIndicator {
case InterestIndicatorFixed, InterestIndicatorVariable, "":
Expand All @@ -1310,13 +1341,30 @@ func (r *PackedBaseSegment) ValidateInterestTypeIndicator() error {
return utils.NewErrInvalidValueOfField("interest type indicator", "packed base segment")
}

// validation of telephone number
func (r *PackedBaseSegment) ValidateTelephoneNumber() error {
if err := r.isPhoneNumber(r.TelephoneNumber, "packed base segment"); err != nil {
return err
}
return nil
}

// validation of social security number
func (r *PackedBaseSegment) ValidateSocialSecurityNumber() error {
if r.SocialSecurityNumber == 0 && r.DateBirth.IsZero() {
return utils.NewErrInvalidValueOfField("social security number", "base segment")
}
return nil
}

// validation of date of birth
func (r *PackedBaseSegment) ValidateDateBirth() error {
if r.SocialSecurityNumber == 0 && r.DateBirth.IsZero() {
return utils.NewErrInvalidValueOfField("date birth", "base segment")
}
return nil
}

func readApplicableSegments(record []byte, f Record) (int, error) {
var segment Segment
offset := 0
Expand Down
58 changes: 58 additions & 0 deletions pkg/lib/base_segment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"testing"

"gopkg.in/check.v1"

"github.com/moov-io/metro2/pkg/utils"
)

func TestBaseSegmentErr(t *testing.T) {
Expand Down Expand Up @@ -368,3 +370,59 @@ func (t *SegmentTest) TestPackedBaseSegmentJson(c *check.C) {
c.Assert(0, check.Equals, bytes.Compare(segment.Bytes(), t.samplePackedBaseSegment))
c.Assert(segment.Name(), check.Equals, PackedBaseSegmentName)
}

func (t *SegmentTest) TestBaseSegmentWithSocialSecurityNumber(c *check.C) {
segment := &BaseSegment{}
_, err := segment.Parse(t.sampleBaseSegment)
c.Assert(err, check.IsNil)

segment.SocialSecurityNumber = 0
err = segment.Validate()
c.Assert(err, check.Equals, nil)

segment.DateBirth = utils.Time{}
err = segment.Validate()
c.Assert(err, check.Not(check.IsNil))
}

func (t *SegmentTest) TestBaseSegmentWithDateBirth(c *check.C) {
segment := &BaseSegment{}
_, err := segment.Parse(t.sampleBaseSegment)
c.Assert(err, check.IsNil)

segment.DateBirth = utils.Time{}
err = segment.Validate()
c.Assert(err, check.Equals, nil)

segment.SocialSecurityNumber = 0
err = segment.Validate()
c.Assert(err, check.Not(check.IsNil))
}

func (t *SegmentTest) TestPackedBaseSegmentWithSocialSecurityNumber(c *check.C) {
segment := &PackedBaseSegment{}
_, err := segment.Parse(t.samplePackedBaseSegment)
c.Assert(err, check.IsNil)

segment.SocialSecurityNumber = 0
err = segment.Validate()
c.Assert(err, check.Equals, nil)

segment.DateBirth = utils.Time{}
err = segment.Validate()
c.Assert(err, check.Not(check.IsNil))
}

func (t *SegmentTest) TestPackedBaseSegmentWithDateBirth(c *check.C) {
segment := &PackedBaseSegment{}
_, err := segment.Parse(t.samplePackedBaseSegment)
c.Assert(err, check.IsNil)

segment.DateBirth = utils.Time{}
err = segment.Validate()
c.Assert(err, check.Equals, nil)

segment.SocialSecurityNumber = 0
err = segment.Validate()
c.Assert(err, check.Not(check.IsNil))
}
20 changes: 18 additions & 2 deletions pkg/lib/j1_segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type J1Segment struct {
// per the Social Security Administration.
// Do not report Credit Profile Numbers (CPNs) in this field.
// The CPN should not be used for credit reporting purposes and does not replace the Social Security Number.
SocialSecurityNumber int `json:"socialSecurityNumber" validate:"required"`
SocialSecurityNumber int `json:"socialSecurityNumber,omitempty"`

// Report the full Date of Birth of the associated consumer, including the month, day and year.
// Reporting of this information is required as the Date of Birth greatly enhances accuracy in matching to the correct consumer.
Expand All @@ -79,7 +79,7 @@ type J1Segment struct {
// When reporting Authorized Users (ECOA Code 3), the full Date of Birth (MMDDYYYY) must be reported for all newly-added
// Authorized Users on all pre-existing and newly-opened accounts, even if the Social Security Number is reported.
// Do not report accounts of consumers who are too young to enter into a binding contract.
DateBirth utils.Time `json:"dateBirth" validate:"required"`
DateBirth utils.Time `json:"dateBirth,omitempty"`

// Contains the telephone number of the associated consumer (Area Code + 7 digits).
TelephoneNumber int64 `json:"telephoneNumber"`
Expand Down Expand Up @@ -172,3 +172,19 @@ func (s *J1Segment) ValidateTelephoneNumber() error {
}
return nil
}

// validation of social security number
func (r *J1Segment) ValidateSocialSecurityNumber() error {
if r.SocialSecurityNumber == 0 && r.DateBirth.IsZero() {
return utils.NewErrInvalidValueOfField("social security number", "base segment")
}
return nil
}

// validation of date of birth
func (r *J1Segment) ValidateDateBirth() error {
if r.SocialSecurityNumber == 0 && r.DateBirth.IsZero() {
return utils.NewErrInvalidValueOfField("date birth", "base segment")
}
return nil
}
Loading

0 comments on commit 16fc29e

Please sign in to comment.