2018-04-25 13:16:36 +02:00
package applicationservice
import (
2018-06-01 10:39:46 +02:00
"context"
"encoding/xml"
"fmt"
"html"
"math/rand"
"strings"
"time"
2018-04-25 13:16:36 +02:00
"bitbucket.org/nemt/nemt-portal-api/application/entitymapping"
2018-06-01 10:39:46 +02:00
"bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi"
2018-04-25 13:16:36 +02:00
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/domain/entity"
"bitbucket.org/nemt/nemt-portal-api/domain/service"
2018-06-01 10:39:46 +02:00
"bitbucket.org/nemt/nemt-portal-api/infra/config"
2018-04-25 13:16:36 +02:00
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
2018-06-01 10:39:46 +02:00
"bitbucket.org/nemt/nemt-portal-api/server/validation"
"googlemaps.github.io/maps"
2018-04-25 13:16:36 +02:00
)
// userService holds methods to user application service
type userService struct {
svc * service . Service
mapEntity * entitymapping . Mapper
2018-06-01 10:39:46 +02:00
bcbsi * bcbsi . Service
cfg * config . Config
2018-04-25 13:16:36 +02:00
}
// newUserService returns a userService instance
2018-06-01 10:39:46 +02:00
func newUserService ( svc * service . Service , mapper * entitymapping . Mapper , bcbsi * bcbsi . Service , cfg * config . Config ) * userService {
2018-04-25 13:16:36 +02:00
return & userService {
svc : svc ,
mapEntity : mapper ,
2018-06-01 10:39:46 +02:00
bcbsi : bcbsi ,
cfg : cfg ,
2018-04-25 13:16:36 +02:00
}
}
// GetAll returns a list of users
func ( s * userService ) GetAll ( quantity int64 , page int64 ) ( retVal [ ] viewmodel . User , err error ) {
users , err := s . svc . Users . GetAll ( )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModelSlice ( users ) , nil
}
// GetByID returns a specific user by its ID
func ( s * userService ) GetByID ( id int64 ) ( retVal viewmodel . User , err error ) {
user , err := s . svc . Users . GetByID ( id )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModel ( user ) , nil
}
// GetByID returns a specific user by its ID
func ( s * userService ) GetByUUID ( uuid string , profile string ) ( retVal viewmodel . User , err error ) {
user , err := s . svc . Users . GetByUUID ( uuid , profile )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModel ( user ) , nil
}
2018-05-25 09:12:42 +02:00
// GetByID returns a specific user by its ID
func ( s * userService ) GetByMemberID ( memberID string ) ( retVal viewmodel . User , err error ) {
user , err := s . svc . Users . GetByMemberID ( memberID )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModel ( user ) , nil
}
2018-05-30 16:52:57 +02:00
// GetByEmail returns a specific user by its email
func ( s * userService ) GetByEmail ( email string ) ( retVal viewmodel . User , err error ) {
user , err := s . svc . Users . GetByEmail ( email )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModel ( user ) , nil
}
2018-04-25 13:16:36 +02:00
// Login returns a specific user by email and pass
func ( s * userService ) FullLogin ( loginType string , key string , pass string , profile string ) ( retVal viewmodel . User , err error ) {
user , err := s . svc . Users . FullLogin ( loginType , key , pass , profile )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModel ( user ) , nil
}
// Login returns a specific user by email and pass
func ( s * userService ) Login ( email string , pass string ) ( retVal viewmodel . User , err error ) {
user , err := s . svc . Users . Login ( email , pass )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModel ( user ) , nil
}
2018-05-03 07:55:09 +02:00
func ( s * userService ) Create ( user viewmodel . User , author viewmodel . User ) ( retVal viewmodel . User , err error ) {
2018-04-25 13:16:36 +02:00
entity := s . mapEntity . User . ToUserEntity ( user )
2018-05-03 07:55:09 +02:00
enAuthor := s . mapEntity . User . ToUserEntity ( author )
2018-04-25 13:16:36 +02:00
for i , _ := range entity . Organizations {
2018-05-03 07:55:09 +02:00
entity . Organizations [ i ] , err = s . svc . Organization . GetByUUID ( entity . Organizations [ i ] . UUID , enAuthor )
2018-04-25 13:16:36 +02:00
if err != nil {
return retVal , err
}
}
entity , err = s . svc . Users . Create ( entity )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModel ( entity ) , nil
}
2018-05-03 07:55:09 +02:00
func ( s * userService ) CreateBulk ( users [ ] viewmodel . User , author viewmodel . User ) ( retVal [ ] viewmodel . User , err error ) {
2018-04-25 13:16:36 +02:00
entities := s . mapEntity . User . ToUserEntitySlice ( users )
2018-05-03 07:55:09 +02:00
enAuthor := s . mapEntity . User . ToUserEntity ( author )
2018-04-25 13:16:36 +02:00
organizations := make ( [ ] entity . Organization , 0 )
for i , _ := range entities {
if i == 0 {
for o , _ := range entities [ i ] . Organizations {
2018-05-03 07:55:09 +02:00
org , err := s . svc . Organization . GetByUUID ( entities [ i ] . Organizations [ o ] . UUID , enAuthor )
2018-04-25 13:16:36 +02:00
if err != nil {
return nil , err
}
organizations = append ( organizations , org )
}
}
entities [ i ] . Organizations = organizations
}
entities , err = s . svc . Users . CreateBulk ( entities )
if err != nil {
return nil , errors . Wrap ( err )
}
return s . mapEntity . User . ToUserModelSlice ( entities ) , nil
}
// GetUsersByProfile returns a list of users by profile
func ( s * userService ) GetUsersByProfile ( profile string ) ( retVal [ ] viewmodel . User , err error ) {
users , err := s . svc . Users . GetUsersByProfile ( profile )
if err != nil {
return retVal , errors . Wrap ( err )
}
list := s . mapEntity . User . ToUserModelSlice ( users )
return list , nil
}
func ( s * userService ) RemoveAddress ( addressUUID string ) error {
return s . svc . Users . RemoveAddress ( addressUUID )
}
2018-05-25 09:12:42 +02:00
func ( s * userService ) UpdateLogin ( user viewmodel . User ) error {
eUser := s . mapEntity . User . ToUserEntity ( user )
return s . svc . Users . UpdateLogin ( eUser )
}
2018-04-25 13:16:36 +02:00
func ( s * userService ) SaveAddress ( address viewmodel . Address ) ( retVal viewmodel . Address , err error ) {
entity := s . mapEntity . Address . ToAddressEntity ( address )
entity , err = s . svc . Users . SaveAddress ( entity )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . Address . ToAddressModel ( entity ) , err
}
func ( s * userService ) GetContactType ( ) ( retVal [ ] viewmodel . ContactType , err error ) {
entity , err := s . svc . Users . GetContactType ( )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToContactTypeModelSlice ( entity ) , nil
}
2018-05-11 09:07:54 +02:00
func ( s * userService ) SaveContact ( contact viewmodel . Contact ) ( retVal viewmodel . Contact , err error ) {
entity := s . mapEntity . User . ToContactEntity ( contact )
entity , err = s . svc . Users . SaveContact ( entity )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToContactModel ( entity ) , err
}
func ( s * userService ) RemoveContact ( contact viewmodel . Contact ) ( retVal viewmodel . Contact , err error ) {
entity := s . mapEntity . User . ToContactEntity ( contact )
entity , err = s . svc . Users . RemoveContact ( entity )
if err != nil {
return retVal , errors . Wrap ( err )
}
return s . mapEntity . User . ToContactModel ( entity ) , err
}
2018-06-01 10:39:46 +02:00
func ( s * userService ) rangeIn ( low , hi int ) string {
result := low + rand . Intn ( hi - low )
return fmt . Sprintf ( "%v" , result )
}
func ( s * userService ) generatePassword ( n int ) string {
const (
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)
return s . stringWithCharset ( n , charset )
}
func ( s * userService ) stringWithCharset ( length int , charset string ) string {
b := make ( [ ] byte , length )
var seededRand * rand . Rand = rand . New (
rand . NewSource ( time . Now ( ) . UnixNano ( ) ) )
for i := range b {
b [ i ] = charset [ seededRand . Intn ( len ( charset ) ) ]
}
return string ( b )
}
func ( s * userService ) CheckAndCreateMember ( user viewmodel . User , provider viewmodel . ProviderResp , authorUser viewmodel . User ) ( viewmodel . User , error ) {
if validationErrors := validation . ValidateEligibility ( & user ) ; len ( validationErrors ) > 0 {
return viewmodel . User { } , & viewmodel . ValidationError { Message : "Eligibility validation failed" , Errors : validationErrors }
}
entityUser := s . mapEntity . User . ToUserEntity ( user )
entityAuthorUser := s . mapEntity . User . ToUserEntity ( authorUser )
entityProvider := s . mapEntity . Provider . ToProviderRespEntity ( provider )
newEmail := entityUser . Email
newPhoneNumber := entityUser . PhoneNumber
var err error
if entityUser . UUID != "" {
entityUser , err = s . svc . Users . GetByUUID ( entityUser . UUID , "US" )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error finding user by UUID: %s" , err . Error ( ) ) }
}
} else {
entityUser , err = s . svc . Users . GetByMemberID ( entityUser . Member )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error finding user by Subscriber ID: %s" , err . Error ( ) ) }
}
}
var eligibility viewmodel . Eligibility
loc , _ := time . LoadLocation ( "America/Chicago" )
eligibility . TrackingID = s . rangeIn ( 1000000 , 9999999 )
eligibility . ServiceInfo . DateOfService = time . Now ( ) . In ( loc )
eligibility . ServiceInfo . ServiceTypeCodes = [ ] string { "30" }
if entityProvider . InternalID == "" && entityProvider . ProviderUUID != "" {
entityProvider , err = s . svc . Provider . GetByUUID ( entityProvider . ProviderUUID , entityAuthorUser )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error finding provider by UUID: %s" , err . Error ( ) ) }
}
} else {
entityProvider , err = s . svc . Provider . GetByNPI ( entityProvider . InternalID , entityAuthorUser )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error finding provider by NPI: %s" , err . Error ( ) ) }
}
}
if entityProvider . InternalID == "" {
return viewmodel . User { } , & viewmodel . ValidationError { Message : "Provider not found" }
} else {
eligibility . Provider . ProviderNPI = entityProvider . InternalID
eligibility . Provider . ProviderName = entityProvider . OrganizatioName
}
plan , err := s . svc . Plans . GetByAlphaPrefix ( entityUser . Member [ : 3 ] )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error finding Plan: %s" , err . Error ( ) ) }
}
eligibility . Payer . PayerID = fmt . Sprintf ( "%v" , plan . PayerID )
eligibility . Payer . PayerName = plan . PayerName
eligibility . Subscriber . SubscriberID = entityUser . Member
eligibility . Subscriber . PatientType = entityUser . Type
eligibility . Subscriber . Name . First = strings . Split ( entityUser . Name , " " ) [ 0 ]
eligibility . Subscriber . Name . Last = strings . Split ( entityUser . Name , " " ) [ 1 ]
eligibility . Subscriber . DemographicInfo . DateOfBirth = entityUser . BirthDate
eligibility . Subscriber . DemographicInfo . Gender = entityUser . Gender
ret , err := s . bcbsi . BXE . CheckEligibility ( eligibility )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error checking eligibility: %s" , err . Error ( ) ) }
}
if ret . QueryResult . QueryResultSummary . BenefitsFound {
xmlString := html . UnescapeString ( ret . QueryResult . HIPPA271 . T271 )
xmlReader := strings . NewReader ( xmlString )
var f viewmodel . Interchange271
err = xml . NewDecoder ( xmlReader ) . Decode ( & f )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error unmarshaling 271 response: %s" , err . Error ( ) ) }
}
header := f . Division . HealthCareEligibilityResponse . LoopHL0030 [ 0 ] . HL_0460 [ 0 ] . HL_0890 [ 0 ] . NM1_0920 [ 0 ] . N3_0950
body := f . Division . HealthCareEligibilityResponse . LoopHL0030 [ 0 ] . HL_0460 [ 0 ] . HL_0890 [ 0 ] . NM1_0920 [ 0 ] . N4_0960
zipCode := strings . TrimSpace ( body . N403 )
if len ( strings . TrimSpace ( zipCode ) ) > 5 {
zipCode = strings . TrimSpace ( zipCode ) [ : 5 ]
}
if ! s . cfg . App . DisableZipValidation {
_ , err = s . svc . Zipcodes . GetByParticipatingZipcode ( zipCode )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Member's Home zipcode, %s, is not currently eligible for participation in this program" , zipCode ) }
}
}
if entityUser . ID == 0 {
profile , err := s . svc . Profile . GetByKey ( "US" )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error getting user profile: %s" , err . Error ( ) ) }
}
entityUser . Profiles = append ( entityUser . Profiles , profile )
if len ( entityUser . PhoneNumber ) == 10 && ! strings . Contains ( entityUser . PhoneNumber , "+1" ) {
phoneNumber := fmt . Sprintf ( "+1%s" , entityUser . PhoneNumber )
entityUser . PhoneNumber = phoneNumber
}
entityUser . Test = false
entityUser , err = s . svc . Users . Create ( entityUser )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error creating user: %s" , err . Error ( ) ) }
}
} else {
if len ( newEmail ) > 0 && newEmail != entityUser . Email {
entityUser . Email = newEmail
}
if len ( newPhoneNumber ) > 0 && len ( newPhoneNumber ) == 10 && ! strings . Contains ( newPhoneNumber , "+1" ) {
newPhoneNumber = fmt . Sprintf ( "+1%s" , newPhoneNumber )
}
if len ( newPhoneNumber ) > 0 && len ( newPhoneNumber ) == 12 && strings . Contains ( newPhoneNumber , "+1" ) {
entityUser . PhoneNumber = newPhoneNumber
}
err = s . svc . Users . UpdateLogin ( entityUser )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error updating login data: %s" , err . Error ( ) ) }
}
}
address := entity . Address { }
address . AddressType = entity . Params {
Key : "home" ,
Name : "Home" ,
}
address . Name = fmt . Sprintf ( "%s, %s" , header . N301 , body . N401 )
address . Address = fmt . Sprintf ( "%s, %s (%s)" , header . N301 , body . N401 , zipCode )
address . CreatedUser = entityAuthorUser
address . User = entityUser
address . Origin = entity . Params {
Key : "provider" ,
Name : "Provider" ,
}
googleMapsAPI , err := maps . NewClient ( maps . WithClientIDAndSignature ( "gme-bluecrossandblue1" , "msqgD-jdqCyR0M_1u5C1HION5iI=" ) )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error instanciating google maps: %s" , err . Error ( ) ) }
}
r := & maps . GeocodingRequest {
Address : address . Address + " " + body . N402 + ", " + zipCode ,
}
result , err := googleMapsAPI . Geocode ( context . Background ( ) , r )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error getting geocode info: %s" , err . Error ( ) ) }
}
if len ( result ) > 0 {
address . Latitude = result [ 0 ] . Geometry . Location . Lat
address . Longitude = result [ 0 ] . Geometry . Location . Lng
}
if len ( user . Addresses ) > 0 {
for _ , a := range user . Addresses {
if a . AddressType == "home" {
err := s . svc . Users . RemoveAddress ( a . UUID )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error removing old address: %s" , err . Error ( ) ) }
}
}
}
}
address , err = s . svc . Users . SaveAddress ( address )
if err != nil {
return viewmodel . User { } , & viewmodel . ValidationError { Message : fmt . Sprintf ( "Error saving new address: %s" , err . Error ( ) ) }
}
entityUser . Addresses = append ( entityUser . Addresses , address )
return s . mapEntity . User . ToUserModel ( entityUser ) , nil
} else {
return viewmodel . User { } , & viewmodel . ValidationError { Message : "No benefits found for the member" }
}
return viewmodel . User { } , nil
}