package LIMS::Controller::Patient; use Moose; BEGIN { extends 'LIMS::Base'; } with ( 'LIMS::Controller::Roles::Misc', 'LIMS::Controller::Roles::SessionStore', 'LIMS::Controller::Roles::PatientDemographics', # Mini-Spine service ); __PACKAGE__->meta->make_immutable(inline_constructor => 0); use LIMS::Local::PAS; use LIMS::Local::Utils; use Data::Dumper; #------------------------------------------------------------------------------- sub default : Startrunmode { my $self = shift; $self->_debug_path($self->get_current_runmode); my $errs = shift; $self->stash( errs => $errs ); # for debugging in tmpl return $self->forbidden() unless $self->user_can('register'); $self->js_validation_profile('js_new_patient'); # if redirected from empty registration search, retrieve saved form # params from session & load into query: $self->_load_saved_form_params; return $self->tt_process($errs); } #------------------------------------------------------------------------------- sub add_new : Runmode { my $self = shift; my $rm = $self->get_current_runmode; $self->_debug_path($rm); return $self->forbidden() unless $self->user_can('register'); my $dfv = $self->check_rm( 'default', $self->validate('new_patient') ) || return $self->dfv_error_page; my $patient = $dfv->valid; # $self->debug( $patient ); # create dt object from patient year, month & day vals: $patient->{dob} = LIMS::Local::Utils::to_datetime($patient); # $self->debug( $patient ); # check new patient details for potential duplicates and/or PDS mismatches # returns true if OK, otherwise sets appropriate tt_params for default.tt: my $check_ok = $self->_check_new_patient($patient); # warn Dumper $check_ok; unless ($check_ok) { my $pds_return_codes = $self->get_yaml_file('pds_return_codes'); $self->tt_params( pds_codes => $pds_return_codes ); my $html = $self->render_view('patient/default.tt'); # for is_code_ref() return $self->fill_form($html); } # insert data to patients/patient_cases and return patient_cases.id (last_insert_id): my $case_id = $self->model('Patient')->create_new_patient($patient) || return $self->error('no patient_case id value returned to ' . $rm); return $self->redirect( $self->query->url.'/request/add_new/'.$case_id ); } #------------------------------------------------------------------------------- sub add_new_location : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); my $patient_id = $self->param('id') || return $self->error('no patient id passed to '.$self->get_current_runmode); my $dfv = $self->check_rm( 'select_patient', $self->validate('new_location') ) || return $self->dfv_error_page; my $data = $dfv->valid; # $self->debug($data); my %patient_case_data = ( patient_id => $patient_id, referral_source_id => $data->{referral_source_id}, unit_number => $data->{unit_number}, ); my $patient_case = $self->model('PatientCase')->new_patient_case(\%patient_case_data); return $self->redirect( $self->query->url . '/request/add_new/' . $patient_case->id ); } #------------------------------------------------------------------------------- sub select_patient : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); my $errs = shift; return $self->forbidden() unless $self->user_can('register'); my $patient_id = $self->param('id') || return $self->error('no patient id passed to '.$self->get_current_runmode); my $patient = $self->model('Patient')->get_patient_demographics($patient_id); $self->js_validation_profile('new_location'); my $patient_cases = $self->model('PatientCase')->get_cases_by_patient_id($patient_id); if ($self->cfg('settings')->{pas_address}) { # do PAS lookup (if local patient): my $pas_query = $self->_pas_query($patient_id); # $self->debug($pas_query); $self->tt_params( pas_query => $pas_query ); # switched off, replaced by PDS } { # do PDS lookup: my $pds_query = $self->_personal_demographic_service($patient); $self->tt_params( pds_query => $pds_query ); } { # previous clinical trials: my %h = ( patient_id => $patient_id ); my $trials = $self->model('ClinicalTrial')->get_patient_trials(\%h); $self->tt_params( clinical_trials => $trials ); } my $pds_return_codes = $self->get_yaml_file('pds_return_codes'); $self->tt_params( patient_cases => $patient_cases, pds_codes => $pds_return_codes, patient => $patient, ); return $self->render_view($self->tt_template_name, $errs); } #------------------------------------------------------------------------------- sub edit_patient : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); my $errs = shift; # $self->stash(errs => $errs); return $self->forbidden() unless $self->user_can('edit_pid'); my $patient_id = $self->param('id') || return $self->error('no id passed to '.$self->get_current_runmode); # request_id (optionally) passed as 2nd token (or in form if resubmitted after dfv failure) my $request_id = $self->param('Id') || $self->query->param('request_id') || ''; if ($request_id) { my $request = $self->model('Request')->get_request($request_id); $self->tt_params( request => $request ); } $self->js_validation_profile('js_edit_patient'); my $patient = $self->model('Patient')->get_patient($patient_id); $self->tt_params( patient => $patient ); if ($self->cfg('settings')->{pas_address}) { # PAS query (replaced by PDS): # vars only supplied by PAS search if insufficient details supplied: my $vars = $self->query->Vars(); # $self->debug($vars); my $pas_query = (%$vars) ? [ $vars ] # template expects array of hashrefs : $self->_pas_query($patient_id); $self->tt_params( pas_query => $pas_query ); # $self->debug($pas_query); } { # do PDS lookup: my $pds_query = $self->_personal_demographic_service($patient); my $pds_return_codes = $self->get_yaml_file('pds_return_codes'); $self->tt_params( pds_query => $pds_query, pds_codes => $pds_return_codes, ); } { # error codes: my $error_codes = $self->model('ErrorCode')->get_error_code_assignment('patient'); $self->tt_params( error_codes => $error_codes, ); } { # get any similar patient entries: my $cases = $self->model('Patient')->get_similar_patients($patient); my $similar_entries = $self->extract_patients_and_locations($cases); $self->tt_params( similar_entries => $similar_entries ); } { # has patient id been used already (for delete function): my $count = $self->model('Patient')->patient_request_count($patient_id); $self->tt_params( count => $count ); } return $self->render_view($self->tt_template_name, $errs); } #------------------------------------------------------------------------------- sub pds_query : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); my $patient_id = $self->param('id') || $self->query->param('_patient_id'); if ($patient_id) { # warn $patient_id; my $data = $self->model('Patient')->get_patient($patient_id); $self->tt_params( patient => $data ); # warn Dumper $data; } else { # no match in patients table - re-use submitted form params: my $vars = $self->query->Vars; # warn Dumper $vars; # freeze vars in session for transfer to registration form if no PAS match: unless ($vars->{_is_pds_search}) { # ie return from pas search - don't want these vals $self->session->param( patient_search_params => { %$vars } ); # needs to be in assoc. array format 1st } # get yr, month, day from dob if supplied (otherwise expect separate vars): if ( my $dob = $vars->{dob} ) { # convert to ymd format for Utils::to_datetime(): @{$vars}{qw(year month day)} = split '-', $dob; } my %patient = map +($_ => $vars->{$_}), qw(last_name first_name nhs_number unit_number); $patient{dob} = LIMS::Local::Utils::to_datetime($vars); # .tt requires DT $self->tt_params( patient => \%patient ); # warn Dumper \%patient; =begin # PDS doesn't use unit numbers: my @unit_numbers; if ( my $unit_number = $self->query->param('unit_number') ) { push @unit_numbers, $unit_number; # only if exists, or get [ '' ] in tt } $self->tt_params( unit_numbers => \@unit_numbers ); =cut # clear all query params to prevent fill_form() loading into form fields: # $self->query->delete_all(); # not required if not using fill_form() } return $self->render_view($self->tt_template_name); } #------------------------------------------------------------------------------- sub do_pds_search : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); my %data = $self->query->Vars; # warn Dumper \%data; # in hash context, or can't modify dob key if ( grep { ! $data{$_} } qw(last_name gender dob) ) { # warn 'insufficient vars'; $self->flash( error => $self->messages('demographics')->{pds_insufficient_vars} ); return $self->forward('pds_query'); } # dob -> datetime: if ( my $dob = $data{dob} ) { my $dt = DateTime::Format::MySQL->parse_date($dob); # warn Dumper $dt; $data{dob} = $dt; } # format post-code (PDS error unless WWd(d) dWW format): if ( my $post_code = $data{post_code} ) { $data{post_code} = LIMS::Local::Utils::format_postcode($post_code); } # warn \%data; my $pds_query = $self->_personal_demographic_service(\%data); # warn Dumper $pds_query; my $pds_return_codes = $self->get_yaml_file('pds_return_codes'); $self->tt_params( pds_query => $pds_query, pds_codes => $pds_return_codes, ); return $self->forward('pds_query'); } #------------------------------------------------------------------------------- # request to edit patient during registration process; sets hidden flag to alter # redirect destination: sub register_edit_patient : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); $self->tt_params( registration_edit => 1 ); # flag for tmpl return $self->forward('edit_patient'); # use forward() so tt_template_name() works } #------------------------------------------------------------------------------- sub edit_case : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); return $self->forbidden() unless $self->user_can('edit_pid'); my $case_id = $self->param('id') || return $self->error('no id passed to '.$self->get_current_runmode); $self->js_validation_profile('patient_case'); my $case = $self->model('PatientCase')->retrieve_patient_data($case_id); # has entry been used already (for delete function): my $requests_count = $self->model('Request')->get_patient_case_requests_count($case_id); $self->tt_params( case => $case, count => $requests_count, ); return $self->tt_process; } #------------------------------------------------------------------------------- sub update_case : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); return $self->forbidden() unless $self->user_can('edit_pid'); my $case_id = $self->param('id') || return $self->error('no id passed to '.$self->get_current_runmode); my $dfv = $self->check_rm( 'default', $self->validate('patient_case') ) || return $self->dfv_error_page; # get form params as hashref: my $data = $dfv->valid; my %args = ( data => $data, case_id => $case_id, ); my $rtn = $self->model('PatientCase')->update_patient_case(\%args); return $rtn ? $self->error($rtn) : $self->redirect( $self->query->url . '/request/add_new/' . $case_id ); } #------------------------------------------------------------------------------- sub delete_case : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); return $self->forbidden() unless $self->user_can('delete_record'); my $q = $self->query; my $case_id = $self->param('id') || return $self->error('no id passed to '.$self->get_current_runmode); my $case = $self->model('PatientCase')->get_patient_case($case_id) || return $self->error(sprintf q!case id '%s' not found in %s!, $case_id, $self->get_current_runmode); # check not already been used (initial check done in edit_case): if ( $self->model('Request')->get_patient_case_requests_count($case_id) ) { $self->flash( error => $self->messages('registration')->{cannot_delete_case} ); return $self->redirect( $q->url . '/patient/edit_case/' . $case_id); } # need confirmation to delete record: if ( $q->param('confirm_delete') ) { my $redirect_url = $q->url.'/patient/select_patient/'.$case->patient_id; # successful delete (or no such record) returns true: my $successful_delete = $self->model('PatientCase')->delete_patient_case($case_id); return $successful_delete ? $self->redirect( $redirect_url ) : $self->error('Sorry - delete failed, I\'ve no idea why.'); } # just return template with form: else { $self->tt_params( case => $case ); return $self->tt_process; } } #------------------------------------------------------------------------------- sub delete_patient : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); return $self->forbidden() unless $self->user_can('delete_record'); my $q = $self->query; my $patient_id = $self->param('id') || return $self->error('no id passed to '.$self->get_current_runmode); my $patient = $self->model('Patient')->get_patient($patient_id) || return $self->error(sprintf q!patient id '%s' not found in %s!, $patient_id, $self->get_current_runmode); # check patient not attached to other record(s) (initial check done in edit_patient): if ( $self->model('Patient')->patient_request_count($patient_id) ) { $self->flash( error => $self->messages('registration')->{cannot_delete_patient} ); return $self->redirect( $q->url . '/patient/edit_patient/' . $patient_id); } # need confirmation to delete record: if ( $q->param('confirm_delete') ) { # successful delete (or no such record) returns true: my $successful_delete = $self->model('Patient')->delete_patient($patient_id); return $successful_delete ? $self->redirect( $q->url.'/register' ) : $self->error('Sorry - delete failed, I\'ve no idea why.'); } # just return template with form: else { $self->tt_params( patient => $patient ); return $self->tt_process; } } #------------------------------------------------------------------------------- sub update_patient : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); return $self->forbidden() unless $self->user_can('edit_pid'); my $q = $self->query; my $patient_id = $self->param('id') || return $self->error('no id passed to '.$self->get_current_runmode); # hidden field if edited from a request page: my $request_id = $q->param('request_id'); # put $patient_id into params() for $self->validate('edit_patient') profile: $self->query->param(_record_id => $patient_id); my $dfv = $self->check_rm( 'edit_patient', $self->validate('edit_patient') ); # need to (re)set template flag(s) first if validation failed: if (! $dfv) { map { # warn $q->param($_); $self->tt_params( $_ => $q->param($_) ); } grep $q->param($_), qw(registration_edit request_id); return $self->dfv_error_page; } # get form params as hashref: my $data = $dfv->valid; # $self->debug( $data ); # create DoB datetime object if day, month & year passed: $data->{dob} = LIMS::Local::Utils::to_datetime($data); # add patient.id to data so record updated if it's an edit: $data->{id} = $patient_id; # now we have the ability to change only 1 record, need to check NHS no if # submitted doesn't already exist (can't change single record with an NHS no): unless ( $self->_check_nhs_number_usage($data) ) { # returns 1 if OK my $str = '/patient/edit_patient/' . join '/', $patient_id, $request_id; return $self->redirect( $self->query->url . $str ); # flash msg set in sub } { # do PDS lookup - returns 0 if PDS verification failed; 1 if it passes or not required: my $result = $self->_demographics_verification($data); return $self->forward('edit_patient') if not $result; # only necessary on failure } # validations, etc ok - update record: my $rtn = $self->model('Patient')->update_patient($data); # warn Dumper $rtn; # $rtn = { error => $db->error (if any); success => number of rows updated } if ($rtn->{error}) { return $self->error($rtn->{error}); } else { # update succeeded: my $messages = $self->messages('request_edit'); my $i = $rtn->{success}; # number of rows updated my @flash = $i # success can be 0, or an INT ? ( info => sprintf $messages->{edit_success}, $i ) : ( warning => $messages->{edit_failed} ); $self->flash( @flash ); my $redirect = $q->param('registration_edit') ? # redirect to registration page for $patient_id: "/register/patient_search?patient_id=$patient_id" : # redirect to specific request, or to general search page: $request_id ? "/search/=/$request_id" : '/search'; return $self->redirect( $q->url . $redirect ); } } # ------------------------------------------------------------------------------ sub patient_notes : Runmode { my $self = shift; $self->_debug_path($self->get_current_runmode); return $self->forbidden() unless $self->user_can(['report','modify_results']); my $request_id = $self->param('id') || return $self->error('no request_id passed to '.$self->get_current_runmode); my $patient_id = $self->param('Id') || return $self->error('no patient_id passed to '.$self->get_current_runmode); # only one param passed: my $str = $self->query->param('patient_notes'); my %args = ( patient_id => $patient_id, detail => $str, ); my $rtn = $self->model('Patient')->update_patient_notes(\%args); return $self->error($rtn) if $rtn; $self->flash( info => $self->messages('action')->{edit_success} ); return $self->redirect( $self->query->param('fwd_to') ); } #------------------------------------------------------------------------------- # check nhs number if submitted AND editing 1 patient only, returns 0 if NHS no # already exists, otherwise returns 1: sub _check_nhs_number_usage { my ($self, $data) = @_; # only applies to single record change with an NHS number: return 1 unless ( $data->{this_record_only} && $data->{nhs_number} ); my $nhs_number = $data->{nhs_number}; if ( $self->model('Patient')->get_patient_from_nhs_number($nhs_number) ) { $self->flash(error => $self->messages('request_edit')->{nhs_conflict}); return 0; # blocks update } return 1; } #------------------------------------------------------------------------------- # checks against PDS (unless already done it, or PDS isn't config'd eg *.t, or # anon patient)- only returns 0 if verification _fails_ (not if it isn't done): sub _demographics_verification { my ($self, $data) = @_; my $can_skip_pds = ( $data->{use_patient_id} || # selected existing patient entry ! $self->cfg('settings')->{pds_proxy} ) || # PDS switched off grep $self->query->param($_), qw(_pid_confirmed _skip_pds); # already done it unless ( $can_skip_pds ) { my $pds_query = $self->_personal_demographic_service($data); # need to check pds return is hashref (not error str) before deref'ing: my $is_verified = ( $pds_query && ref $pds_query eq 'HASH' && $pds_query->{demographics_verified} ); # return 0 if verification attempted but fails: unless ( $is_verified ) { # flag to tt to allow override: $self->tt_params( is_patient_edit => 1 ); # to allow '_pid_confirmed' return 0; # will trigger forward to patient_edit }; } return 1; # caller only cares if verification fails in previous block } #------------------------------------------------------------------------------- # if redirected from empty registration search, retrieve saved form params from # session & load into query: sub _load_saved_form_params { my $self = shift; $self->_debug_path; my $patient_search_params = # will be empty if not arrived via empty registration search $self->session->param('patient_search_params') || return; foreach (keys %$patient_search_params) { # $self->debug($patient_search_params->{$_}); $self->query->param( $_ => $patient_search_params->{$_} ); } # now clear patient_search_params hahref $self->session->clear('patient_search_params'); } # checks new patient data for potential duplicate or PAS mismatch, # returns false if potential problem, otherwise true: #------------------------------------------------------------------------------- sub _check_new_patient { my $self = shift; $self->_debug_path; my $patient = shift; # hashref (with dob = DT) # form flags to confirm & override checks: my $local_confirmed = $self->query->param('_local_confirmed'); my $pid_confirmed = $self->query->param('_pid_confirmed'); # do PDS query first - if OK then do a local db check for duplicates - can # only do local check *after* PDS, so can skip PDS if local_confirmed: if ( not $pid_confirmed and not $local_confirmed ) { if ( my $pds_query = $self->_personal_demographic_service($patient) ) { # warn Dumper $pds_query; $self->tt_params( is_new_patient => 1, # flag for tt to enforce confirmation in case of pds diffs pds_query => $pds_query, patient => $patient, ); return 0; } } # check for potential duplicate unit_number and/or nhs_number: if ( not $local_confirmed ) { my $maybe_duplicate = $self->model('PatientCase')->validate_patient_case($patient); if (@$maybe_duplicate) { # warn 'maybe_duplicate'; $self->tt_params( maybe_duplicate => $maybe_duplicate ); return 0; } } # OK, patient validated against PDS and HILIS4 patient table: return 1; } #------------------------------------------------------------------------------- # returns true if 1 or more locations = local in patient_cases object: sub _is_local_patient { my $self = shift; $self->_debug_path; my $patient_data = shift; # arrayref if PatientCase obj, or hashref of patient data # get local prefix from settings or return if empty: my $local_prefix = $self->cfg('settings')->{local_prefix} || return 0; # warn $local_prefix; if (ref $patient_data eq 'HASH') { my $ref_src_id = $patient_data->{referral_source_id}; my $location = $self->model('ReferralSource')->get_referral_source($ref_src_id); # returns true if organisation_code matches local prefix: return $location->organisation_code =~ /\A($local_prefix)/; } else { # returns true if at least 1 organisation_code matches local prefix: return grep { $_->referral_source->organisation_code =~ /\A($local_prefix)/ } @$patient_data; } } =begin nd Function: _pas_query() Called by to check patient demographic data submitted either via selection of previous patient match or directly through Validate button. Uses to query the PAS interface. Returns a ref to array of hashrefs (if matching patient data found on PAS) or string containing error status ($c->config->{msg}) if not. =cut #------------------------------------------------------------------------------- sub _pas_query { my ($self, $patient_id) = @_; $self->_debug_path; my @pas_config_settings = qw(pas_address pas_username pas_pwd); my $cfg = $self->cfg('settings'); # need all required PAS config settings, or return: return 0 if grep ! $cfg->{$_}, @pas_config_settings; my $patient_cases # arrayref = $self->model('PatientCase')->get_cases_by_patient_id($patient_id); # needs to be local patient for PAS lookup: return 0 unless $self->_is_local_patient($patient_cases); # variable: $pas_excluded_names # list of last_names excluded from PAS search (eg HIV's) # my $pas_excluded_names = join '|', @{ $self->cfg('pas_excluded_names') }; # return if # skip PAS lookup if: # last_name matches proscribed entry(s) in config->pas_excluded_names, # $patient->{last_name} =~ /\A($pas_excluded_names)/ || # eg HIV's # form carries 'pas_checked' flag: # $params_ref->{pas_checked}; # already been there my %patient; if (ref $patient_cases eq 'ARRAY') { # extract 1st record (all same patient) from $patient_data: my $patient_case = $patient_cases->[0]; # create patient hashref for PAS query: %patient = map { $_ => $patient_case->patient->$_; } $patient_case->patient->meta->column_names; # get local unit_number(s) as arrayref from referral(s): my $local_unit_numbers = $self->_get_local_unit_numbers($patient_cases); # include unit_numbers arrayref: $patient{unit_number} = $local_unit_numbers; # $self->debug($local_unit_numbers); # dob needs to be in yyyy-mm-dd format: if ( my $dob = $patient_case->patient->dob) { $patient{dob} = $dob->ymd; # $self->debug(\%patient); } # PAS considers FORENAME as first_name + middle_name: $patient{first_name} = join ' ', map $patient_case->patient->$_, grep $patient_case->patient->$_, qw(first_name middle_name); } elsif (ref $patient_cases eq 'HASH') { # does this ever happen ?? %patient = %$patient_cases; # $self->debug(%patient); # TODO: dob needs to be ymd: $patient{dob} = $patient{dob}->ymd if $patient{dob}; } else { return $self->error('_pas_query() called with unexpected $patient_data format'); } my %pas_config = map +($_ => $cfg->{$_}), @pas_config_settings; # $self->debug(\%pas_config); my %args = ( patient => \%patient, config => \%pas_config, messages => $self->messages('demographics'), ); # warn Dumper \%args; my $pas = LIMS::Local::PAS->new(\%args); my $q = $pas->query; # warn Dumper $q; # ref to array of hashrefs or string return $q; } #------------------------------------------------------------------------------- sub _personal_demographic_service { my ($self, $patient) = @_; $self->_debug_path; # hashref (with DoB = DT) return 0 if $self->query->param('_skip_pds'); # _skip_pds is tt flag my $config = $self->cfg('settings'); # pds_proxy only used by dev server, but also used as on/off switch; # env flag PDS_LOOKUPS allows pds queries even if not in production mode: return 0 unless $config->{pds_proxy} && ( $config->{is_in_production_mode} || $ENV{PDS_LOOKUPS} ); my $data = ref $patient eq 'LIMS::DB::Patient' # get_pds_data() expects hashref with DT object: ? $patient->as_tree(deflate => 0) # $patient = select_patient() object : $patient; # $patient = add_new() hashref # skip anon patients, certain trials, etc: if ( my $cfg = $self->get_yaml_file('pds_exempt_names') ) { # warn Dumper $cfg; my $first_names = $cfg->{first_name}; # warn Dumper $first_names; my $last_names = $cfg->{last_name}; # warn Dumper $last_names; # set flag for tt to skip pds on subsequent submissions & return 0: if ( ( grep $data->{first_name} =~ qr(\A$_\Z)i, @$first_names ) || ( grep $data->{last_name} =~ qr(\A$_\Z)i, @$last_names ) ) { $self->tt_params( is_pds_exempt => 1 ); return 0; } } my $result = $self->get_pds_data($data); # warn Dumper $result; return $result; } #------------------------------------------------------------------------------- # parses $patient_cases array(ref) for local unit_numbers, as patient referral # from multiple locations may include remote location unit_number(s) which match # a (different) local patient but are not valid for local PAS lookup: sub _get_local_unit_numbers { my $self = shift; $self->_debug_path; my $patient_cases = shift; my $local_prefix = $self->cfg('settings')->{local_prefix}; # create hash of local unit_numbers (excluding default val): my %local_unit_numbers = map { $_->unit_number, 1 } grep { # skip null (or default) unit_numbers: $_->unit_number ne $_->meta->column('unit_number')->default, } grep { $_->referral_source->organisation_code =~ /\A($local_prefix)/ } @$patient_cases; # $self->debug(\%local_unit_numbers); # return arrayref to list of unique unit_numbers: return [ sort keys %local_unit_numbers ]; } 1; __END__ =begin # done in LIMS::Valiadate now _validate_patient_data { my $self = shift; my $nhsno = $self->query->param('nhs_number'); # return profile as mixture of new_patient & # patient_search_data validation profiles: my $new_patient = $self->validate('new_patient'); my $patient_search = $self->validate('patient_search_data'); # add nhs_number constraint_method to patient_search_data: $patient_search->{constraint_methods}->{nhs_number} = sub { return LIMS::Local::Utils::check_nhsno( $nhsno ); }; my %profile = ( required => $new_patient->{required}, optional => $new_patient->{optional}, dependency_groups => $patient_search->{dependency_groups}, constraint_methods => $patient_search->{constraint_methods}, ); $profile{require_some} = { patient_id => [ 1, qw(nhs_number unit_number) ], }; # $self->debug(\%profile); return \%profile; } =cut #------------------------------------------------------------------------------- =begin # direct entry from patient/select_patient.tt now new_location : Runmode { my $self = shift; my $errs = shift; my $patient_id = $self->param('id') || return $self->error('no id passed to '.$self->get_current_runmode); $self->js_validation_profile('new_location'); my $patient = $self->model('Patient')->get_patient($patient_id); $self->tt_params( patient => $patient, ); return $self->tt_process($errs); } =cut