@@ -, +, @@ searching --- Koha/Patrons.pm | 60 ++++++++++++++ Koha/REST/V1/Patrons.pm | 47 +++++++++++ api/v1/swagger/paths/patrons.yaml | 81 +++++++++++++++++++ api/v1/swagger/swagger.yaml | 2 + .../prog/en/includes/patron-search.inc | 21 +++-- koha-tmpl/intranet-tmpl/prog/js/datatables.js | 7 +- 6 files changed, 210 insertions(+), 8 deletions(-) --- a/Koha/Patrons.pm +++ a/Koha/Patrons.pm @@ -41,6 +41,66 @@ Koha::Patron - Koha Patron Object class =cut + +=head3 lookup + +my $patrons = Koha::Patrons->lookup({ + search_term => $search_term, + search_field => $search_field, + search_type => $search_type +}); + +Parses a lookup request into a result set + +=cut + +sub lookup { + my ( $self, $params ) = @_; + + my $search_term = $params->{search_term}; + my $search_field = $params->{search_field}; + my $search_type = $params->{search_type}; + + my @terms = split( /\s/, $search_term ); + my @search_fields = split( /,/, $search_field ); + my $leading_wildcard = $search_type eq 'contains' ? '%' : ''; + + my $query = {}; + my @term_query; + foreach my $term (@terms) { + my @field_query = (); + foreach my $field (@search_fields) { + push @field_query, { "me." . $field => { like => $leading_wildcard . $term . '%' } }; + } + push @term_query, { -or => \@field_query }; + } + push @{ $query->{-or} }, { -and => \@term_query }; + + my $attributes_rs; + if ( C4::Context->preference("ExtendedPatronAttributes") ) { + my @attributes_query = (); + my $search_attribute_types = Koha::Patron::Attribute::Types->search( { staff_searchable => 1 } ); + foreach my $term (@terms) { + my @attributes_subquery = (); + while ( my $attribute = $search_attribute_types->next ) { + push @attributes_subquery, { + "me.code" => $attribute->code, + "me.attribute" => { like => $leading_wildcard . $term . '%' } + }; + } + push @attributes_query, \@attributes_subquery; + + } + $attributes_rs = Koha::Patron::Attributes->search( { -and => \@attributes_query } ); + } + + push @{ $query->{-or} }, { 'me.borrowernumber' => { -in => [ $attributes_rs->get_column('borrowernumber') ] } }; + + my $found = $self->search($query); + return $found; + +} + =head3 search_limited my $patrons = Koha::Patrons->search_limit( $params, $attributes ); --- a/Koha/REST/V1/Patrons.pm +++ a/Koha/REST/V1/Patrons.pm @@ -65,6 +65,53 @@ sub list { }; } + +=head3 lookup + +Controller function that handles searching for KOha patrons + +=cut + +sub lookup { + my $c = shift->openapi->valid_input or return; + + return try { + + my $query = {}; + + my $search_term = $c->param('search_term'); + my $search_field = $c->param('search_field'); + my $search_type = $c->param('search_type'); + $c->req->params->remove('search_term'); + $c->req->params->remove('search_field'); + $c->req->params->remove('search_type'); + $c->req->params->remove('q'); + + my $patrons = Koha::Patrons->lookup( + { + search_term => $search_term, + search_field => $search_field, + search_type => $search_type + } + ); + + my $restricted = $c->param('restricted'); + $c->req->params->remove('restricted'); + $query->{debarred} = { '!=' => undef } + if $restricted; + + my $patrons_rs = $patrons->search($query); + $patrons = $c->objects->search($patrons_rs); + + return $c->render( + status => 200, + openapi => $patrons + ); + } catch { + $c->unhandled_exception($_); + }; +} + =head3 get Controller function that handles retrieving a single Koha::Patron object --- a/api/v1/swagger/paths/patrons.yaml +++ a/api/v1/swagger/paths/patrons.yaml @@ -449,6 +449,87 @@ x-koha-authorization: permissions: borrowers: edit_borrowers +"/patrons/lookup": + get: + x-mojo-to: Patrons#lookup + operationId: lookupPatrons + tags: + - patrons + summary: Lookup patrons + produces: + - application/json + parameters: + - name: search_term + in: query + description: Term to use in searech + required: false + type: string + - name: search_field + in: query + description: Field in the patron record to search + required: false + type: string + - name: search_type + in: query + description: How to search terms, contains or starts with + required: false + type: string + enum: + - contains + - starts_with + - $ref: "../swagger.yaml#/parameters/match" + - $ref: "../swagger.yaml#/parameters/order_by" + - $ref: "../swagger.yaml#/parameters/page" + - $ref: "../swagger.yaml#/parameters/per_page" + - $ref: "../swagger.yaml#/parameters/q_param" + - $ref: "../swagger.yaml#/parameters/q_body" + - $ref: "../swagger.yaml#/parameters/request_id_header" + - name: x-koha-embed + in: header + required: false + description: Embed list sent as a request header + type: array + items: + type: string + enum: + - extended_attributes + - checkouts+count + - overdues+count + - account_balance + - library + collectionFormat: csv + responses: + "200": + description: A list of patrons + schema: + type: array + items: + $ref: "../swagger.yaml#/definitions/patron" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "500": + description: | + Internal server error. Possible `error_code` attribute values: + + * `internal_server_error` + schema: + $ref: "../swagger.yaml#/definitions/error" + "503": + description: Under maintenance + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + - borrowers: "edit_borrowers" + - tools: "label_creator" + - serials: "routing" + - acquisition: "order_manage" "/patrons/{patron_id}": get: x-mojo-to: Patrons#get --- a/api/v1/swagger/swagger.yaml +++ a/api/v1/swagger/swagger.yaml @@ -299,6 +299,8 @@ paths: $ref: ./paths/oauth.yaml#/~1oauth~1token /patrons: $ref: ./paths/patrons.yaml#/~1patrons + "/patrons/lookup": + $ref: ./paths/patrons.yaml#/~1patrons~1lookup "/patrons/{patron_id}": $ref: "./paths/patrons.yaml#/~1patrons~1{patron_id}" "/patrons/{patron_id}/account": --- a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-search.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-search.inc @@ -278,7 +278,8 @@ [% CASE 'erm_users' %] let patron_search_url = '/api/v1/erm/users'; [% CASE %] - let patron_search_url = '/api/v1/patrons'; + let patron_search_url = '/api/v1/patrons/lookup'; + let lookup = true; [% END %] $(document).ready(function(){ @@ -309,6 +310,18 @@ } }); + let search_type = $("#searchtype_filter").val() || "contains"; + let search_fields = $("#searchfieldstype_filter").val(); + let pattern = $("#search_patron_filter").val(); + let additional_data = {}; + if( lookup ){ + additional_data = { + "search_type": search_type, + "search_field": search_fields, + "search_term": pattern + }; + } + let additional_filters = { surname: function(){ let start_with = $("#firstletter_filter").val() @@ -318,10 +331,6 @@ "-and": function(){ let filters = []; - let search_type = $("#searchtype_filter").val() || "contains"; - let search_fields = $("#searchfieldstype_filter").val(); - let pattern = $("#search_patron_filter").val(); - filters = buildPatronSearchQuery( pattern, { @@ -659,7 +668,7 @@ }); }, [% END %] - }, typeof table_settings !== 'undefined' ? table_settings : null, 1, additional_filters); + }, typeof table_settings !== 'undefined' ? table_settings : null, 1, additional_filters, additional_data); $("#patron_search_form").on('submit', filter); $("#patron_search_form").on('submit', update_search_type); --- a/koha-tmpl/intranet-tmpl/prog/js/datatables.js +++ a/koha-tmpl/intranet-tmpl/prog/js/datatables.js @@ -507,6 +507,7 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) { function _dt_default_ajax (params){ let default_filters = params.default_filters; let options = params.options; + let additional_data = params.additional_data; if(!options.criteria || ['contains', 'starts_with', 'ends_with', 'exact'].indexOf(options.criteria.toLowerCase()) === -1) options.criteria = 'contains'; options.criteria = options.criteria.toLowerCase(); @@ -692,6 +693,7 @@ function _dt_default_ajax (params){ }); dataSet._order_by = orderArray.filter((v, i, a) => a.indexOf(v) === i).join(','); } + dataSet = { ...dataSet, ...additional_data }; return dataSet; } @@ -938,9 +940,10 @@ function _dt_add_filters(table_node, table_dt, filters_options = {}) { * available from the columns_settings template toolkit include * @param {Boolean} add_filters Add a filters row as the top row of the table * @param {Object} default_filters Add a set of default search filters to apply at table initialisation + * @param {Object} additional_data Add additional parameters to the ajax data * @return {Object} The dataTables instance */ - $.fn.kohaTable = function(options, table_settings, add_filters, default_filters) { + $.fn.kohaTable = function(options, table_settings, add_filters, default_filters, additional_data) { var settings = null; if(options) { @@ -963,7 +966,7 @@ function _dt_add_filters(table_node, table_dt, filters_options = {}) { 'language': { 'emptyTable': (options.emptyTable) ? options.emptyTable : __("No data available in table") }, - 'ajax': _dt_default_ajax({default_filters, options}), + 'ajax': _dt_default_ajax({default_filters, options,additional_data}), }, options); } --