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  }