github.com/companieshouse/insolvency-api@v0.0.0-20231024103413-440c973d9e9b/service/practitioner_service.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "net/http" 6 "regexp" 7 "strings" 8 9 "github.com/companieshouse/chs.go/log" 10 "github.com/companieshouse/insolvency-api/constants" 11 "github.com/companieshouse/insolvency-api/dao" 12 "github.com/companieshouse/insolvency-api/models" 13 "github.com/companieshouse/insolvency-api/utils" 14 ) 15 16 // ValidatePractitionerDetails checks that the incoming practitioner details are valid 17 func ValidatePractitionerDetails(svc dao.Service, transactionID string, practitioner models.PractitionerRequest) (string, error) { 18 var errs []string 19 20 // Check that either the telephone number or email field are populated 21 if practitioner.TelephoneNumber == "" && practitioner.Email == "" { 22 errs = append(errs, "either telephone_number or email are required") 23 } 24 25 // Set allowed regexp for telephone number 26 telephoneNumberRuleRegexString := `^\d+$` 27 telephoneNumberRegex := regexp.MustCompile(telephoneNumberRuleRegexString) 28 29 // Check that telephone number starts with 0 and only contains digits 30 if practitioner.TelephoneNumber != "" && (!strings.HasPrefix(practitioner.TelephoneNumber, "0") || !telephoneNumberRegex.MatchString(practitioner.TelephoneNumber)) { 31 errs = append(errs, "telephone_number must start with 0 and contain only numeric characters") 32 } 33 34 // Check that telephone number is the correct length 35 if practitioner.TelephoneNumber != "" && !((len(practitioner.TelephoneNumber) == 10) || (len(practitioner.TelephoneNumber) == 11)) { 36 errs = append(errs, "telephone_number must be 10 or 11 digits long") 37 } 38 39 // Check that telephone number does not contain spaces 40 if practitioner.TelephoneNumber != "" && strings.Contains(practitioner.TelephoneNumber, " ") { 41 errs = append(errs, "telephone_number must not contain spaces") 42 } 43 44 // Set allowed naming conventions for names 45 nameRuleRegexString := `^[a-zA-ZàáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ð ,.'-]+$` 46 nameRuleRegex := regexp.MustCompile(nameRuleRegexString) 47 48 // Check that the first name matches naming conventions 49 if !nameRuleRegex.MatchString(practitioner.FirstName) { 50 errs = append(errs, "the first name contains a character which is not allowed") 51 } 52 53 // Check that the last name matches naming conventions 54 if !nameRuleRegex.MatchString(practitioner.LastName) { 55 errs = append(errs, "the last name contains a character which is not allowed") 56 } 57 58 // Get insolvency case from DB 59 insolvencyCase, err := svc.GetInsolvencyResource(transactionID) 60 if err != nil { 61 log.Error(fmt.Errorf("error getting insolvency case from DB: [%s]", err)) 62 return "", err 63 } 64 65 // Check if insolvency case is of type CVL and practitioner role is of type final liquidator 66 if insolvencyCase.Data.CaseType == constants.CVL.String() && practitioner.Role != constants.FinalLiquidator.String() { 67 errs = append(errs, fmt.Sprintf("the practitioner role must be "+constants.FinalLiquidator.String()+" because the insolvency case for transaction ID [%s] is of type "+constants.CVL.String(), transactionID)) 68 } 69 70 return strings.Join(errs, ", "), nil 71 } 72 73 // ValidateAppointmentDetails checks that the incoming appointment details are valid 74 func ValidateAppointmentDetails(svc dao.Service, appointment models.PractitionerAppointment, transactionID string, practitionerID string, req *http.Request) (string, error) { 75 var errs []string 76 77 // Check if practitioner is already appointed 78 practitionerResources, err := svc.GetPractitionerResources(transactionID) 79 if err != nil { 80 err = fmt.Errorf("error getting pracititioner resources from DB: [%s]", err) 81 log.ErrorR(req, err) 82 return "", err 83 } 84 for _, practitioner := range practitionerResources { 85 if practitioner.ID == practitionerID && practitioner.Appointment != nil && practitioner.Appointment.AppointedOn != "" { 86 msg := fmt.Sprintf("practitioner ID [%s] already appointed to transaction ID [%s]", practitionerID, transactionID) 87 log.Info(msg) 88 errs = append(errs, msg) 89 } 90 } 91 92 // Check if appointment date supplied is in the future or before company was incorporated 93 insolvencyResource, err := svc.GetInsolvencyResource(transactionID) 94 if err != nil { 95 err = fmt.Errorf("error getting insolvency resource from DB: [%s]", err) 96 log.ErrorR(req, err) 97 return "", err 98 } 99 // Retrieve company incorporation date 100 incorporatedOn, err := GetCompanyIncorporatedOn(insolvencyResource.Data.CompanyNumber, req) 101 if err != nil { 102 err = fmt.Errorf("error getting company details from DB: [%s]", err) 103 log.ErrorR(req, err) 104 return "", err 105 } 106 107 ok, err := utils.IsDateBetweenIncorporationAndNow(appointment.AppointedOn, incorporatedOn) 108 if err != nil { 109 err = fmt.Errorf("error parsing date: [%s]", err) 110 log.ErrorR(req, err) 111 return "", err 112 } 113 if !ok { 114 errs = append(errs, fmt.Sprintf("appointed_on [%s] should not be in the future or before the company was incorporated", appointment.AppointedOn)) 115 } 116 117 // Check if appointment date supplied is different from stored appointment dates in DB 118 for _, practitioner := range practitionerResources { 119 if practitioner.Appointment != nil && practitioner.Appointment.AppointedOn != "" && practitioner.Appointment.AppointedOn != appointment.AppointedOn { 120 errs = append(errs, fmt.Sprintf("appointed_on [%s] differs from practitioner ID [%s] who was appointed on [%s]", appointment.AppointedOn, practitioner.ID, practitioner.Appointment.AppointedOn)) 121 } 122 } 123 124 // Check that a CVL case is only made by creditors 125 if appointment.MadeBy != "" { 126 if insolvencyResource.Data.CaseType == constants.CVL.String() && appointment.MadeBy != constants.Creditors.String() { 127 errs = append(errs, fmt.Sprintf("made_by cannot be [%s] for insolvency case of type CVL", appointment.MadeBy)) 128 } 129 } 130 131 return strings.Join(errs, ", "), nil 132 }