Bugzilla – Attachment 93040 Details for
Bug 14697
Extend and enhance "Claims returned" lost status
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 14697: Extend and enhance "Claims returned" lost status
Bug-14697-Extend-and-enhance-Claims-returned-lost-.patch (text/plain), 72.83 KB, created by
ByWater Sandboxes
on 2019-09-20 19:26:32 UTC
(
hide
)
Description:
Bug 14697: Extend and enhance "Claims returned" lost status
Filename:
MIME Type:
Creator:
ByWater Sandboxes
Created:
2019-09-20 19:26:32 UTC
Size:
72.83 KB
patch
obsolete
>From 99a5a5923d7331a788c007e48d21d27e3f5666bb Mon Sep 17 00:00:00 2001 >From: Kyle M Hall <kyle@bywatersolutions.com> >Date: Wed, 13 Mar 2019 07:09:15 -0400 >Subject: [PATCH] Bug 14697: Extend and enhance "Claims returned" lost status > >This adds a "Claims returned" feature that extends and enhances the claims returned lost status > >Test Plan: >1) Create a "Claims Returned" lost value >2) Create some RETURN_CLAIM_RESOLUTION authorized values >3) Set ClaimReturnedLostValue >4) Set ClaimReturnedChargeFee >5) Set ClaimReturnedWarningThreshold >6) Create some checkouts >7) Claim some returns >8) Verify ClaimReturnedChargeFee works with all 3 options >9) Verify ClaimReturnedWarningThreshold shows a warning once the threshold has been exceeded >10) Edit notes on a claim >11) Resolve a claim >12) Delete a claim > >Signed-off-by: Andrew Fuerste-Henry <andrew@bywatersolutions.com> >--- > Koha/Checkout.pm | 45 ++- > Koha/Checkouts/ReturnClaim.pm | 65 ++++ > Koha/Checkouts/ReturnClaims.pm | 86 +++++ > Koha/Patron.pm | 12 + > Koha/REST/V1/ReturnClaims.pm | 212 ++++++++++++ > api/v1/swagger/definitions.json | 3 + > api/v1/swagger/definitions/return_claim.json | 90 ++++++ > api/v1/swagger/paths.json | 12 + > api/v1/swagger/paths/return_claims.json | 358 +++++++++++++++++++++ > installer/data/mysql/atomicupdate/bug_14697.perl | 2 +- > .../prog/en/includes/checkouts-table.inc | 71 ++++ > .../prog/en/includes/patron-return-claims.inc | 15 + > .../intranet-tmpl/prog/en/includes/strings.inc | 5 + > .../en/modules/admin/preferences/circulation.pref | 19 ++ > .../prog/en/modules/catalogue/moredetail.tt | 36 ++- > .../prog/en/modules/circ/circulation.tt | 35 ++ > .../prog/en/modules/members/moremember.tt | 24 ++ > koha-tmpl/intranet-tmpl/prog/js/checkouts.js | 274 +++++++++++++++- > svc/checkouts | 61 ++-- > svc/return_claims | 127 ++++++++ > t/db_dependent/api/v1/return_claims.t | 159 +++++++++ > 21 files changed, 1673 insertions(+), 38 deletions(-) > create mode 100644 Koha/Checkouts/ReturnClaim.pm > create mode 100644 Koha/Checkouts/ReturnClaims.pm > create mode 100644 Koha/REST/V1/ReturnClaims.pm > create mode 100644 api/v1/swagger/definitions/return_claim.json > create mode 100644 api/v1/swagger/paths/return_claims.json > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/patron-return-claims.inc > create mode 100755 svc/return_claims > create mode 100644 t/db_dependent/api/v1/return_claims.t > >diff --git a/Koha/Checkout.pm b/Koha/Checkout.pm >index b7a752cdc5..51aebcabba 100644 >--- a/Koha/Checkout.pm >+++ b/Koha/Checkout.pm >@@ -21,9 +21,10 @@ package Koha::Checkout; > use Modern::Perl; > > use Carp; >+use DateTime; > >+use Koha::Checkouts::ReturnClaims; > use Koha::Database; >-use DateTime; > use Koha::DateUtils; > use Koha::Items; > >@@ -88,6 +89,48 @@ sub patron { > return Koha::Patron->_new_from_dbic( $patron_rs ); > } > >+=head3 claim_returned >+ >+my $return_claim = $checkout->claim_returned(); >+ >+=cut >+ >+sub claim_returned { >+ my ( $self, $params ) = @_; >+ >+ my $notes = $params->{notes}; >+ my $charge_lost_fee = $params->{charge_lost_fee}; >+ my $created_by = $params->{created_by}; >+ >+ $created_by ||= C4::Context->userenv->{number} if C4::Context->userenv; >+ >+ my $claim = Koha::Checkouts::ReturnClaims->find( { issue_id => $self->id } ); >+ $claim ||= Koha::Checkouts::ReturnClaims->find( { old_issue_id => $self->id } ); >+ >+ $claim ||= Koha::Checkouts::ReturnClaim->new( >+ { >+ issue_id => $self->id, >+ itemnumber => $self->itemnumber, >+ borrowernumber => $self->borrowernumber, >+ notes => $notes, >+ created_on => dt_from_string, >+ created_by => $created_by, >+ } >+ )->store(); >+ >+ my $ClaimReturnedLostValue = C4::Context->preference('ClaimReturnedLostValue'); >+ C4::Items::ModItem( { itemlost => $ClaimReturnedLostValue }, undef, $self->itemnumber ); >+ >+ my $ClaimReturnedChargeFee = C4::Context->preference('ClaimReturnedChargeFee'); >+ $charge_lost_fee = >+ $ClaimReturnedChargeFee eq 'charge' ? 1 >+ : $ClaimReturnedChargeFee eq 'no_charge' ? 0 >+ : $charge_lost_fee; # $ClaimReturnedChargeFee eq 'ask' >+ C4::Circulation::LostItem( $self->itemnumber, 'claim_returned' ) if $charge_lost_fee; >+ >+ return $claim; >+} >+ > =head3 type > > =cut >diff --git a/Koha/Checkouts/ReturnClaim.pm b/Koha/Checkouts/ReturnClaim.pm >new file mode 100644 >index 0000000000..3fff2e6608 >--- /dev/null >+++ b/Koha/Checkouts/ReturnClaim.pm >@@ -0,0 +1,65 @@ >+package Koha::Checkouts::ReturnClaim; >+ >+# Copyright ByWater Solutions 2019 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use base qw(Koha::Object); >+ >+use Koha::Checkouts; >+use Koha::Old::Checkouts; >+ >+=head1 NAME >+ >+Koha::Checkouts::ReturnClaim - Koha ReturnClaim object class >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 checkout >+ >+=cut >+ >+sub checkout { >+ my ( $self ) = @_; >+ >+ my $issue = $self->_result->issue; >+ return Koha::Checkout->_new_from_dbic( $issue ) if $issue; >+ >+ my $old_issue = $self->_result->old_issue; >+ return Koha::Old::Checkout->_new_from_dbic( $old_issue ) if $old_issue; >+} >+ >+=head3 _type >+ >+=cut >+ >+sub _type { >+ return 'ReturnClaim'; >+} >+ >+=head1 AUTHOR >+ >+Kyle M Hall <kyle@bywatersolutions.com> >+ >+=cut >+ >+1; >diff --git a/Koha/Checkouts/ReturnClaims.pm b/Koha/Checkouts/ReturnClaims.pm >new file mode 100644 >index 0000000000..414416ba68 >--- /dev/null >+++ b/Koha/Checkouts/ReturnClaims.pm >@@ -0,0 +1,86 @@ >+package Koha::Checkouts::ReturnClaims; >+ >+# Copyright ByWater Solutions 2019 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Carp; >+ >+use Koha::Database; >+ >+use Koha::Checkouts::ReturnClaim; >+ >+use base qw(Koha::Objects); >+ >+=head1 NAME >+ >+Koha::Checkouts::ReturnClaims - Koha ReturnClaim object set class >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 unresolved >+ >+=cut >+ >+sub unresolved { >+ my ($self) = @_; >+ >+ my $results = $self->_resultset()->search_rs( { resolved_on => undef } ); >+ >+ return Koha::Checkouts::ReturnClaims->_new_from_dbic( $results ); >+} >+ >+=head3 resolved >+ >+=cut >+ >+sub resolved { >+ my ($self) = @_; >+ >+ my $results = $self->_resultset()->search_rs( { resolved_on => { '!=' => undef } } ); >+ >+ return Koha::Checkouts::ReturnClaims->_new_from_dbic( $results ); >+} >+ >+=head3 type >+ >+=cut >+ >+sub _type { >+ return 'ReturnClaim'; >+} >+ >+=head3 object_class >+ >+=cut >+ >+sub object_class { >+ return 'Koha::Checkouts::ReturnClaim'; >+} >+ >+=head1 AUTHOR >+ >+Kyle M Hall <kyle@bywatersolutions.com> >+ >+=cut >+ >+1; >diff --git a/Koha/Patron.pm b/Koha/Patron.pm >index 436120e0e6..1040c70179 100644 >--- a/Koha/Patron.pm >+++ b/Koha/Patron.pm >@@ -1072,6 +1072,18 @@ sub old_holds { > return Koha::Old::Holds->_new_from_dbic($old_holds_rs); > } > >+=head3 return_claims >+ >+my $return_claims = $patron->return_claims >+ >+=cut >+ >+sub return_claims { >+ my ($self) = @_; >+ my $return_claims = $self->_result->return_claims_borrowernumbers; >+ return Koha::Checkouts::ReturnClaims->_new_from_dbic( $return_claims ); >+} >+ > =head3 notice_email_address > > my $email = $patron->notice_email_address; >diff --git a/Koha/REST/V1/ReturnClaims.pm b/Koha/REST/V1/ReturnClaims.pm >new file mode 100644 >index 0000000000..b1c080f311 >--- /dev/null >+++ b/Koha/REST/V1/ReturnClaims.pm >@@ -0,0 +1,212 @@ >+package Koha::REST::V1::ReturnClaims; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use Koha::Checkouts::ReturnClaims; >+use Koha::Checkouts; >+use Koha::DateUtils qw( dt_from_string output_pref ); >+ >+=head1 NAME >+ >+Koha::REST::V1::ReturnClaims >+ >+=head2 Operations >+ >+=head3 claim_returned >+ >+Claim that a checked out item was returned. >+ >+=cut >+ >+sub claim_returned { >+ my $c = shift->openapi->valid_input or return; >+ my $input = $c->validation->output; >+ my $body = $c->validation->param('body'); >+ >+ my $itemnumber = $input->{item_id}; >+ my $charge_lost_fee = $body->{charge_lost_fee} ? 1 : 0; >+ my $created_by = $body->{created_by}; >+ my $notes = $body->{notes}; >+ >+ my $checkout = Koha::Checkouts->find( { itemnumber => $itemnumber } ); >+ >+ return $c->render( >+ openapi => { error => "Not found - Checkout not found" }, >+ status => 404 >+ ) unless $checkout; >+ >+ my $claim = Koha::Checkouts::ReturnClaims->find( >+ { >+ issue_id => $checkout->id >+ } >+ ); >+ return $c->render( >+ openapi => { error => "Bad request - claim exists" }, >+ status => 400 >+ ) if $claim; >+ >+ $claim = $checkout->claim_returned( >+ { >+ charge_lost_fee => $charge_lost_fee, >+ created_by => $created_by, >+ notes => $notes, >+ } >+ ); >+ >+ my $data = $claim->unblessed; >+ >+ my $c_dt = dt_from_string( $data->{created_on} ); >+ my $u_dt = dt_from_string( $data->{updated_on} ); >+ >+ $data->{created_on_formatted} = output_pref( { dt => $c_dt } ); >+ $data->{updated_on_formatted} = output_pref( { dt => $u_dt } ); >+ >+ $data->{created_on} = $c_dt->iso8601; >+ $data->{updated_on} = $u_dt->iso8601; >+ >+ return $c->render( openapi => $data, status => 200 ); >+} >+ >+=head3 update_notes >+ >+Update the notes of an existing claim >+ >+=cut >+ >+sub update_notes { >+ my $c = shift->openapi->valid_input or return; >+ my $input = $c->validation->output; >+ my $body = $c->validation->param('body'); >+ >+ my $id = $input->{claim_id}; >+ my $updated_by = $body->{updated_by}; >+ my $notes = $body->{notes}; >+ >+ $updated_by ||= >+ C4::Context->userenv ? C4::Context->userenv->{number} : undef; >+ >+ my $claim = Koha::Checkouts::ReturnClaims->find($id); >+ >+ return $c->render( >+ openapi => { error => "Not found - Claim not found" }, >+ status => 404 >+ ) unless $claim; >+ >+ $claim->set( >+ { >+ notes => $notes, >+ updated_by => $updated_by, >+ updated_on => dt_from_string(), >+ } >+ ); >+ $claim->store(); >+ >+ my $data = $claim->unblessed; >+ >+ my $c_dt = dt_from_string( $data->{created_on} ); >+ my $u_dt = dt_from_string( $data->{updated_on} ); >+ >+ $data->{created_on_formatted} = output_pref( { dt => $c_dt } ); >+ $data->{updated_on_formatted} = output_pref( { dt => $u_dt } ); >+ >+ $data->{created_on} = $c_dt->iso8601; >+ $data->{updated_on} = $u_dt->iso8601; >+ >+ return $c->render( openapi => $data, status => 200 ); >+} >+ >+=head3 resolve_claim >+ >+Marks a claim as resolved >+ >+=cut >+ >+sub resolve_claim { >+ my $c = shift->openapi->valid_input or return; >+ my $input = $c->validation->output; >+ my $body = $c->validation->param('body'); >+ >+ my $id = $input->{claim_id}; >+ my $resolved_by = $body->{updated_by}; >+ my $resolution = $body->{resolution}; >+ >+ $resolved_by ||= >+ C4::Context->userenv ? C4::Context->userenv->{number} : undef; >+ >+ my $claim = Koha::Checkouts::ReturnClaims->find($id); >+ >+ return $c->render( >+ openapi => { error => "Not found - Claim not found" }, >+ status => 404 >+ ) unless $claim; >+ >+ $claim->set( >+ { >+ resolution => $resolution, >+ resolved_by => $resolved_by, >+ resolved_on => dt_from_string(), >+ } >+ ); >+ $claim->store(); >+ >+ my $data = $claim->unblessed; >+ >+ my $c_dt = dt_from_string( $data->{created_on} ); >+ my $u_dt = dt_from_string( $data->{updated_on} ); >+ my $r_dt = dt_from_string( $data->{resolved_on} ); >+ >+ $data->{created_on_formatted} = output_pref( { dt => $c_dt } ); >+ $data->{updated_on_formatted} = output_pref( { dt => $u_dt } ); >+ $data->{resolved_on_formatted} = output_pref( { dt => $r_dt } ); >+ >+ $data->{created_on} = $c_dt->iso8601; >+ $data->{updated_on} = $u_dt->iso8601; >+ $data->{resolved_on} = $r_dt->iso8601; >+ >+ return $c->render( openapi => $data, status => 200 ); >+} >+ >+=head3 delete_claim >+ >+Deletes the claim from the database >+ >+=cut >+ >+sub delete_claim { >+ my $c = shift->openapi->valid_input or return; >+ my $input = $c->validation->output; >+ >+ my $id = $input->{claim_id}; >+ >+ my $claim = Koha::Checkouts::ReturnClaims->find($id); >+ >+ return $c->render( >+ openapi => { error => "Not found - Claim not found" }, >+ status => 404 >+ ) unless $claim; >+ >+ $claim->delete(); >+ >+ my $data = $claim->unblessed; >+ >+ return $c->render( openapi => $data, status => 200 ); >+} >+ >+1; >diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json >index 4ac42cf925..90374145a7 100644 >--- a/api/v1/swagger/definitions.json >+++ b/api/v1/swagger/definitions.json >@@ -43,5 +43,8 @@ > }, > "fund": { > "$ref": "definitions/fund.json" >+ }, >+ "return_claim": { >+ "$ref": "definitions/return_claim.json" > } > } >diff --git a/api/v1/swagger/definitions/return_claim.json b/api/v1/swagger/definitions/return_claim.json >new file mode 100644 >index 0000000000..8dc63ffae8 >--- /dev/null >+++ b/api/v1/swagger/definitions/return_claim.json >@@ -0,0 +1,90 @@ >+{ >+ "type": "object", >+ "properties": { >+ "id": { >+ "type": [ >+ "integer" >+ ], >+ "description": "internally assigned return claim identifier" >+ }, >+ "item_id": { >+ "type": [ >+ "integer" >+ ], >+ "description": "internal identifier of the claimed item" >+ }, >+ "issue_id": { >+ "type": [ >+ "integer", >+ "null" >+ ], >+ "description": "internal identifier of the claimed checkout if still checked out" >+ }, >+ "old_issue_id": { >+ "type": [ >+ "integer", >+ "null" >+ ], >+ "description": "internal identifier of the claimed checkout if not longer checked out" >+ }, >+ "patron_id": { >+ "$ref": "../x-primitives.json#/patron_id" >+ }, >+ "notes": { >+ "type": [ >+ "string", >+ "null" >+ ], >+ "description": "notes about this claim" >+ }, >+ "created_on": { >+ "type": [ >+ "string", >+ "null" >+ ], >+ "description": "date of claim creation" >+ }, >+ "created_by": { >+ "type": [ >+ "integer", >+ "null" >+ ], >+ "description": "patron id of librarian who made the claim" >+ }, >+ "updated_on": { >+ "type": [ >+ "string", >+ "null" >+ ], >+ "description": "date the claim was last updated" >+ }, >+ "updated_by": { >+ "type": [ >+ "integer", >+ "null" >+ ], >+ "description": "patron id of librarian who last updated the claim" >+ }, >+ "resolution": { >+ "type": [ >+ "string", >+ "null" >+ ], >+ "description": "code of resolution type for this claim" >+ }, >+ "resolved_on": { >+ "type": [ >+ "string", >+ "null" >+ ], >+ "description": "date the claim was resolved" >+ }, >+ "resolved_by": { >+ "type": [ >+ "integer", >+ "null" >+ ], >+ "description": "patron id of librarian who resolved this claim" >+ } >+ } >+} >diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json >index 946de0a6a4..4e11c9bf06 100644 >--- a/api/v1/swagger/paths.json >+++ b/api/v1/swagger/paths.json >@@ -73,5 +73,17 @@ > }, > "/public/patrons/{patron_id}/password": { > "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1password" >+ }, >+ "/return_claims/claim/{item_id}/": { >+ "$ref": "paths/return_claims.json#/~1return_claims~1claim~1{item_id}" >+ }, >+ "/return_claims/{claim_id}/notes": { >+ "$ref": "paths/return_claims.json#/~1return_claims~1{claim_id}~1notes" >+ }, >+ "/return_claims/{claim_id}/resolve": { >+ "$ref": "paths/return_claims.json#/~1return_claims~1{claim_id}~1resolve" >+ }, >+ "/return_claims/{claim_id}": { >+ "$ref": "paths/return_claims.json#/~1return_claims~1{claim_id}" > } > } >diff --git a/api/v1/swagger/paths/return_claims.json b/api/v1/swagger/paths/return_claims.json >new file mode 100644 >index 0000000000..0289857672 >--- /dev/null >+++ b/api/v1/swagger/paths/return_claims.json >@@ -0,0 +1,358 @@ >+{ >+ "/return_claims/claim/{item_id}": { >+ "post": { >+ "x-mojo-to": "ReturnClaims#claim_returned", >+ "operationId": "claimReturned", >+ "tags": [ >+ "claims", >+ "returned", >+ "return", >+ "claim" >+ ], >+ "parameters": [ >+ { >+ "name": "item_id", >+ "in": "path", >+ "required": true, >+ "description": "Itemnumber of item to claim as returned", >+ "type": "integer" >+ }, >+ { >+ "name": "body", >+ "in": "body", >+ "description": "A JSON object containing fields to modify", >+ "required": true, >+ "schema": { >+ "type": "object", >+ "properties": { >+ "notes": { >+ "description": "Notes about this return claim", >+ "type": "string" >+ }, >+ "created_by": { >+ "description": "User id for the librarian submitting this claim", >+ "type": "string" >+ }, >+ "charge_lost_fee": { >+ "description": "Charge a lost fee if true and Koha is set to allow a choice. Ignored otherwise.", >+ "type": "boolean" >+ } >+ } >+ } >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "Created claim", >+ "schema": { >+ "$ref": "../definitions.json#/return_claim" >+ } >+ }, >+ "400": { >+ "description": "Bad request", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "401": { >+ "description": "Authentication required", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Checkout not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "500": { >+ "description": "Internal server error", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "503": { >+ "description": "Under maintenance", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "circulate": "circulate_remaining_permissions" >+ } >+ } >+ } >+ }, >+ "/return_claims/{claim_id}/notes": { >+ "put": { >+ "x-mojo-to": "ReturnClaims#update_notes", >+ "operationId": "updateClaimNotes", >+ "tags": [ >+ "claims", >+ "returned", >+ "return", >+ "claim", >+ "notes" >+ ], >+ "parameters": [ >+ { >+ "name": "claim_id", >+ "in": "path", >+ "required": true, >+ "description": "Unique identifier for the claim whose notes are to be updated", >+ "type": "integer" >+ }, >+ { >+ "name": "body", >+ "in": "body", >+ "description": "A JSON object containing fields to modify", >+ "required": true, >+ "schema": { >+ "type": "object", >+ "properties": { >+ "notes": { >+ "description": "Notes about this return claim", >+ "type": "string" >+ }, >+ "updated_by": { >+ "description": "User id for the librarian updating the claim notes", >+ "type": "string" >+ } >+ } >+ } >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "Claim notes updated", >+ "schema": { >+ "$ref": "../definitions.json#/return_claim" >+ } >+ }, >+ "400": { >+ "description": "Bad request", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "401": { >+ "description": "Authentication required", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Claim not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "500": { >+ "description": "Internal server error", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "503": { >+ "description": "Under maintenance", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "circulate": "circulate_remaining_permissions" >+ } >+ } >+ } >+ }, >+ "/return_claims/{claim_id}": { >+ "delete": { >+ "x-mojo-to": "ReturnClaims#delete_claim", >+ "operationId": "deletedClaim", >+ "tags": [ >+ "claims", >+ "returned", >+ "return", >+ "claim", >+ "delete" >+ ], >+ "parameters": [ >+ { >+ "name": "claim_id", >+ "in": "path", >+ "required": true, >+ "description": "Unique identifier for the claim to be deleted", >+ "type": "integer" >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "Claim deleted", >+ "schema": { >+ "$ref": "../definitions.json#/return_claim" >+ } >+ }, >+ "400": { >+ "description": "Bad request", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "401": { >+ "description": "Authentication required", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Claim not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "500": { >+ "description": "Internal server error", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "503": { >+ "description": "Under maintenance", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "circulate": "circulate_remaining_permissions" >+ } >+ } >+ } >+ }, >+ "/return_claims/{claim_id}/resolve": { >+ "put": { >+ "x-mojo-to": "ReturnClaims#resolve_claim", >+ "operationId": "updateClaimResolve", >+ "tags": [ >+ "claims", >+ "returned", >+ "return", >+ "claim", >+ "notes" >+ ], >+ "parameters": [ >+ { >+ "name": "claim_id", >+ "in": "path", >+ "required": true, >+ "description": "Unique identifier for the claim to be resolved", >+ "type": "integer" >+ }, >+ { >+ "name": "body", >+ "in": "body", >+ "description": "A JSON object containing fields to modify", >+ "required": true, >+ "schema": { >+ "type": "object", >+ "properties": { >+ "resolution": { >+ "description": "The RETURN_CLAIM_RESOLUTION code to be used to resolve the calim", >+ "type": "string" >+ }, >+ "resolved_by": { >+ "description": "User id for the librarian resolving the claim", >+ "type": "string" >+ } >+ } >+ } >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "Claim resolved", >+ "schema": { >+ "$ref": "../definitions.json#/return_claim" >+ } >+ }, >+ "400": { >+ "description": "Bad request", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "401": { >+ "description": "Authentication required", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Claim not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "500": { >+ "description": "Internal server error", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "503": { >+ "description": "Under maintenance", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "circulate": "circulate_remaining_permissions" >+ } >+ } >+ } >+ } >+} >diff --git a/installer/data/mysql/atomicupdate/bug_14697.perl b/installer/data/mysql/atomicupdate/bug_14697.perl >index 243b3b65de..f1302e68db 100644 >--- a/installer/data/mysql/atomicupdate/bug_14697.perl >+++ b/installer/data/mysql/atomicupdate/bug_14697.perl >@@ -30,7 +30,7 @@ if( CheckVersion( $DBversion ) ) { > } > > $dbh->do(q{ >- INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES >+ INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES > ('ClaimReturnedChargeFee', 'ask', 'ask|charge|no_charge', 'Controls whether or not a lost item fee is charged for return claims', 'Choice'), > ('ClaimReturnedLostValue', '', '', 'Sets the LOST AV value that represents "Claims returned" as a lost value', 'Free'), > ('ClaimReturnedWarningThreshold', '', '', 'Sets the number of return claims past which the librarian will be warned the patron has many return claims', 'Integer'); >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc >index 1276e70ab1..f1bc60ff4b 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table.inc >@@ -28,6 +28,7 @@ > <th scope="col">Price</th> > <th scope="col">Renew <p class="column-tool"><a href="#" id="CheckAllRenewals">select all</a> | <a href="#" id="UncheckAllRenewals">none</a></p></th> > <th scope="col">Check in <p class="column-tool"><a href="#" id="CheckAllCheckins">select all</a> | <a href="#" id="UncheckAllCheckins">none</a></p></th> >+ <th scope="col">Return claims</th> > <th scope="col">Export <p class="column-tool"><a href="#" id="CheckAllExports">select all</a> | <a href="#" id="UncheckAllExports">none</a></p></th> > </tr> > </thead> >@@ -83,3 +84,73 @@ > <p>Patron has nothing checked out.</p> > [% END %] > </div> >+ >+<!-- Claims Returned Modal --> >+<div class="modal fade" id="claims-returned-modal" tabindex="-1" role="dialog" aria-labelledby="claims-returned-modal-label"> >+ <div class="modal-dialog" role="document"> >+ <div class="modal-content"> >+ <div class="modal-header"> >+ <h4 class="modal-title" id="claims-returned-modal-label">Claim returned</h4> >+ </div> >+ <div class="modal-body"> >+ >+ <div class="form-group"> >+ <label for="claims-returned-notes" class="control-label">Notes</label> >+ <div> >+ <textarea id="claims-returned-notes" class="form-control" rows="3"></textarea> >+ </div> >+ </div> >+ >+ [% IF Koha.Preference('ClaimReturnedChargeFee') == 'ask' %] >+ <div class="form-group"> >+ <div class="checkbox"> >+ <label for="claims-returned-charge-lost-fee"> >+ <input id="claims-returned-charge-lost-fee" type="checkbox" value="1"> >+ Charge lost fee >+ </label> >+ </div> >+ </div> >+ [% END %] >+ >+ <input type="hidden" id="claims-returned-itemnumber" /> >+ </div> >+ <div class="modal-footer"> >+ <button id="claims-returned-modal-btn-submit" type="button" class="btn btn-primary"><i class="fa fa-exclamation-circle"></i> Make claim</button> >+ <button class="btn btn-default deny cancel" href="#" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times"></i> Cancel</button> >+ </div> >+ </div> >+ </div> >+</div> >+ >+<!-- Resolve Return Claim Modal --> >+<div class="modal fade" id="claims-returned-resolved-modal" tabindex="-1" role="dialog" aria-labelledby="claims-returned-resolved-modal-label"> >+ <div class="modal-dialog" role="document"> >+ <div class="modal-content"> >+ <div class="modal-header"> >+ <h4 class="modal-title" id="claims-returned-resolved-modal-label">Resolve return claim</h4> >+ </div> >+ <div class="modal-body"> >+ >+ <div class="form-group"> >+ <label for="claims-returned-resolved-code">Resolution</label> >+ [% SET resolutions = AuthorisedValues.GetAuthValueDropbox('RETURN_CLAIM_RESOLUTION') %] >+ <select class="form-control" id="claims-returned-resolved-modal-resolved-code"> >+ [% FOREACH r IN resolutions %] >+ <option value="[% r.authorised_value | html %]">[% r.lib | html %]</option> >+ [% END %] >+ </select> >+ </div> >+ >+ <input type="hidden" id="claims-returned-resolved-modal-id"/> >+ </div> >+ <div class="modal-footer"> >+ <button id="claims-returned-resolved-modal-btn-submit" type="button" class="btn btn-primary"> >+ <i id="claims-returned-resolved-modal-btn-submit-icon" class="fa fa-exclamation-circle"></i> >+ <i id="claims-returned-resolved-modal-btn-submit-spinner" class="fa fa-spinner fa-pulse fa-fw" style="display:none"></i> >+ Resolve claim >+ </button> >+ <button class="btn btn-default deny cancel" href="#" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times"></i> Cancel</button> >+ </div> >+ </div> >+ </div> >+</div> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/patron-return-claims.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-return-claims.inc >new file mode 100644 >index 0000000000..63f1dad303 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/patron-return-claims.inc >@@ -0,0 +1,15 @@ >+<div id="return-claims"> >+ <table id="return-claims-table" class="table table-bordered table-striped"> >+ <thead> >+ <tr> >+ <th class="return-claim-id">Claim ID</th> >+ <th class="return-claim-record-title anti-the">Title</th> >+ <th class="return-claim-notes">Notes</th> >+ <th class="return-claim-created-on">Created on</th> >+ <th class="return-claim-updated-on">Updated on</th> >+ <th class="return-claim-resolution">Resolution</th> >+ <th class="return-claim-actions"> </th> >+ </tr> >+ </thead> >+ </table> >+</div> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc >index eb7bfda139..781f7ae245 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc >@@ -5,6 +5,10 @@ > var NOT_RENEWABLE_RESTRICTION = _("Not allowed: patron restricted"); > var CIRCULATION_RENEWED_DUE = _("Renewed, due:"); > var CIRCULATION_RENEW_FAILED = _("Renew failed:"); >+ var RETURN_CLAIMED = _("Return claimed"); >+ var RETURN_CLAIMED_FAILURE = _("Unable to claim as returned"); >+ var RETURN_CLAIMED_MAKE = _("Claim returned"); >+ var RETURN_CLAIMED_NOTES = _("Notes about return claim"); > var NOT_CHECKED_OUT = _("not checked out"); > var TOO_MANY_RENEWALS = _("too many renewals"); > var ON_RESERVE = _("on hold"); >@@ -46,4 +50,5 @@ > var CURRENT = _(" (current) "); > var MSG_NO_ITEMTYPE = _("No itemtype"); > var MSG_CHECKOUTS_BY_ITEMTYPE = _("Number of checkouts by item type"); >+ var CONFIRM_DELETE_RETURN_CLAIM = _("Are you sure you want to delete this return claim?"); > </script> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref >index 199809f139..2e9e0ba1ac 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref >@@ -1080,3 +1080,22 @@ Circulation: > pages: Pages > chapters: Chapters > - >+ Return Claims: >+ - >+ - When marking a checkout as "claims returned", >+ - pref: ClaimReturnedChargeFee >+ default: ask >+ choices: >+ ask: ask if a lost fee should be charged >+ charge: charge a lost fee >+ no_charge: don't charge a lost fee >+ - . >+ - >+ - Use the LOST authorised value >+ - pref: ClaimReturnedLostValue >+ - to represent returns claims >+ - >+ - Warn librarians that a patron has excessive return cliams if the patron has claimed the return of more than >+ - pref: ClaimReturnedWarningThreshold >+ class: integer >+ - items. >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >index 777f86667b..0e42df20c2 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/moredetail.tt >@@ -100,22 +100,30 @@ > <li><span class="label">Lost status:</span> > [% IF ( CAN_user_circulate ) %] > <form action="updateitem.pl" method="post"> >- <input type="hidden" name="biblionumber" value="[% ITEM_DAT.biblionumber | html %]" /> >- <input type="hidden" name="biblioitemnumber" value="[% ITEM_DAT.biblioitemnumber | html %]" /> >- <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber | html %]" /> >- <select name="itemlost" > >- <option value="">Choose</option> >- [% FOREACH itemlostloo IN itemlostloop %] >- [% IF itemlostloo.authorised_value == ITEM_DAT.itemlost %] >- <option value="[% itemlostloo.authorised_value | html %]" selected="selected">[% itemlostloo.lib | html %]</option> >+ <input type="hidden" name="biblionumber" value="[% ITEM_DAT.biblionumber | html %]" /> >+ <input type="hidden" name="biblioitemnumber" value="[% ITEM_DAT.biblioitemnumber | html %]" /> >+ <input type="hidden" name="itemnumber" value="[% ITEM_DAT.itemnumber | html %]" /> >+ <select name="itemlost" > >+ <option value="">Choose</option> >+ [% FOREACH itemlostloo IN itemlostloop %] >+ [% IF itemlostloo.authorised_value == ITEM_DAT.itemlost %] >+ <option value="[% itemlostloo.authorised_value | html %]" selected="selected">[% itemlostloo.lib | html %]</option> >+ [% ELSE %] >+ <option value="[% itemlostloo.authorised_value | html %]">[% itemlostloo.lib | html %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ <input type="hidden" name="withdrawn" value="[% ITEM_DAT.withdrawn | html %]" /> >+ <input type="hidden" name="damaged" value="[% ITEM_DAT.damaged | html %]" /> >+ >+ [% SET ClaimReturnedLostValue = Koha.Preference('ClaimReturnedLostValue') %] >+ [% IF ClaimReturnedLostValue && ITEM_DAT.itemlost == ClaimReturnedLostValue %] >+ <input type="submit" name="submit" class="submit" value="Set status" disabled="disabled"/> >+ <p class="help-block">Item has been claimed as returned.</p> > [% ELSE %] >- <option value="[% itemlostloo.authorised_value | html %]">[% itemlostloo.lib | html %]</option> >+ <input type="submit" name="submit" class="submit" value="Set status" /> > [% END %] >- [% END %] >- </select> >- <input type="hidden" name="withdrawn" value="[% ITEM_DAT.withdrawn | html %]" /> >- <input type="hidden" name="damaged" value="[% ITEM_DAT.damaged | html %]" /> >- <input type="submit" name="submit" class="submit" value="Set status" /></form> >+ </form> > [% ELSE %] > [% FOREACH itemlostloo IN itemlostloop %] > [% IF ( itemlostloo.selected ) %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt >index 5fb4889bed..d8df89b125 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt >@@ -728,6 +728,12 @@ > <li><span class="circ-hlt">Overdues: Patron has ITEMS OVERDUE.</span> <a href="#checkouts">See highlighted items below</a></li> > [% END %] > >+ [% SET ClaimReturnedWarningThreshold = Koha.Preference('ClaimReturnedWarningThreshold') %] >+ [% SET return_claims = patron.return_claims %] >+ [% IF return_claims.count %] >+ <li><span class="circ-hlt return-claims">Return claims: Patron has [% return_claims.count | html %] RETURN CLAIMS.</span> >+ [% END %] >+ > [% IF ( charges ) %] > [% INCLUDE 'blocked-fines.inc' fines = chargesamount %] > [% END %] >@@ -841,6 +847,30 @@ > </li> > [% END %] > >+ <li> >+ [% IF ( patron.return_claims.count ) %] >+ <a href="#return-claims" id="return-claims-tab"> >+ <span id="return-claims-count-resolved">[% patron.return_claims.resolved.count | html %]</span> >+ / >+ <span id="return-claims-count-unresolved">[% patron.return_claims.unresolved.count | html %]</span> >+ Claim(s) >+ </a> >+ [% ELSE %] >+ <a href="#return-claims" id="return-claims-tab"> >+ <span id="return-claims-count-resolved">0</span> >+ / >+ <span id="return-claims-count-unresolved">0</span> >+ Claim(s) >+ </a> >+ [% END %] >+ </li> >+ >+ [% IF Koha.Preference('ArticleRequests') %] >+ <li> >+ <a href="#article-requests" id="article-requests-tab"> [% patron.article_requests_current.count | html %] Article requests</a> >+ </li> >+ [% END %] >+ > <li><a id="debarments-tab-link" href="#reldebarments">[% debarments.count | html %] Restrictions</a></li> > > [% SET enrollments = patron.get_club_enrollments(1) %] >@@ -924,6 +954,8 @@ > [% END # /IF holds_count %] > </div> <!-- /#reserves --> > >+ [% INCLUDE 'patron-return-claims.inc' %] >+ > [% IF Koha.Preference('ArticleRequests') %] > [% INCLUDE 'patron-article-requests.inc' %] > [% END %] >@@ -991,6 +1023,9 @@ > [% Asset.js("js/circ-patron-search-results.js") | $raw %] > <script type="text/javascript"> > /* Set some variable needed in circulation.js */ >+ var ClaimReturnedLostValue = "[% Koha.Preference('ClaimReturnedLostValue') | html %]"; >+ var ClaimReturnedChargeFee = "[% Koha.Preference('ClaimReturnedChargeFee') | html %]"; >+ var ClaimReturnedWarningThreshold = "[% Koha.Preference('ClaimReturnedWarningThreshold') | html %]"; > var MSG_DT_LOADING_RECORDS = _("Loading... you may continue scanning."); > var interface = "[% interface | html %]"; > var theme = "[% theme | html %]"; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt >index 4a046b614b..25a1be55ae 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt >@@ -723,6 +723,25 @@ > <a href="#article-requests" id="article-requests-tab"> [% patron.article_requests_current.count | html %] Article requests</a> > </li> > [% END %] >+ >+ <li> >+ [% IF ( patron.return_claims.count ) %] >+ <a href="#return-claims" id="return-claims-tab"> >+ <span id="return-claims-count-resolved">[% patron.return_claims.resolved.count | html %]</span> >+ / >+ <span id="return-claims-count-unresolved">[% patron.return_claims.unresolved.count | html %]</span> >+ Claim(s) >+ </a> >+ [% ELSE %] >+ <a href="#return-claims" id="return-claims-tab"> >+ <span id="return-claims-count-resolved">0</span> >+ / >+ <span id="return-claims-count-unresolved">0</span> >+ Claim(s) >+ </a> >+ [% END %] >+ </li> >+ > <li> > <a id="debarments-tab-link" href="#reldebarments">[% debarments.size | html %] Restrictions</a> > </li> >@@ -815,6 +834,8 @@ > </div> [% # /div#reserves %] > [% END %] > >+ [% INCLUDE 'patron-return-claims.inc' %] >+ > [% IF Koha.Preference('ArticleRequests') %] > [% INCLUDE 'patron-article-requests.inc' %] > [% END %] >@@ -847,6 +868,9 @@ > [% Asset.js("js/messaging-preference-form.js") | $raw %] > <script> > /* Set some variable needed in circulation.js */ >+ var ClaimReturnedLostValue = "[% Koha.Preference('ClaimReturnedLostValue') | html %]"; >+ var ClaimReturnedChargeFee = "[% Koha.Preference('ClaimReturnedChargeFee') | html %]"; >+ var ClaimReturnedWarningThreshold = "[% Koha.Preference('ClaimReturnedWarningThreshold') | html %]"; > var interface = "[% interface | html %]"; > var theme = "[% theme | html %]"; > var borrowernumber = "[% patron.borrowernumber | html %]"; >diff --git a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js >index da69276e4a..06fd7f432c 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/checkouts.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/checkouts.js >@@ -268,7 +268,9 @@ $(document).ready(function() { > > due = "<span id='date_due_" + oObj.itemnumber + "' class='date_due'>" + due + "</span>"; > >- if ( oObj.lost ) { >+ if ( oObj.lost && oObj.claims_returned ) { >+ due += "<span class='lost claims_returned'>" + oObj.lost.escapeHtml() + "</span>"; >+ } else if ( oObj.lost ) { > due += "<span class='lost'>" + oObj.lost.escapeHtml() + "</span>"; > } > >@@ -539,6 +541,20 @@ $(document).ready(function() { > } > }, > { >+ "bVisible": ClaimReturnedLostValue ? true : false, >+ "bSortable": false, >+ "mDataProp": function ( oObj ) { >+ let content = ""; >+ >+ if ( oObj.return_claim_id ) { >+ content = `<span class="badge">${oObj.return_claim_created_on_formatted}</span>`; >+ } else { >+ content = `<a class="btn btn-default btn-xs claim-returned-btn" data-itemnumber="${oObj.itemnumber}"><i class="fa fa-exclamation-circle"></i> ${RETURN_CLAIMED_MAKE}</a>`; >+ } >+ return content; >+ } >+ }, >+ { > "bVisible": exports_enabled == 1 ? true : false, > "bSortable": false, > "mDataProp": function ( oObj ) { >@@ -808,4 +824,260 @@ $(document).ready(function() { > } > } ).prop('checked', false); > } >+ >+ // Handle return claims >+ $(document).on("click", '.claim-returned-btn', function(e){ >+ e.preventDefault(); >+ itemnumber = $(this).data('itemnumber'); >+ >+ $('#claims-returned-itemnumber').val(itemnumber); >+ $('#claims-returned-notes').val(""); >+ $('#claims-returned-charge-lost-fee').attr('checked', false) >+ $('#claims-returned-modal').modal() >+ }); >+ $(document).on("click", '#claims-returned-modal-btn-submit', function(e){ >+ let itemnumber = $('#claims-returned-itemnumber').val(); >+ let notes = $('#claims-returned-notes').val(); >+ let fee = $('#claims-returned-charge-lost-fee').attr('checked') ? true : false; >+ >+ $('#claims-returned-modal').modal('hide') >+ >+ $(`.claim-returned-btn[data-itemnumber='${itemnumber}']`).replaceWith(`<img id='return_claim_spinner_${itemnumber}' src='${interface}/${theme}/img/spinner-small.gif' />`); >+ >+ params = { >+ notes: notes, >+ charge_lost_fee: fee, >+ created_by: $.cookie("lastborrowernumber") >+ }; >+ >+ $.post( `/api/v1/return_claims/claim/${itemnumber}`, JSON.stringify(params), function( data ) { >+ >+ id = "#return_claim_spinner_" + data.itemnumber; >+ >+ let content = ""; >+ if ( data.id ) { >+ console.log(data); >+ content = `<span class="badge">${data.created_on_formatted}</span>`; >+ $(id).parent().parent().addClass('ok'); >+ } else { >+ content = RETURN_CLAIMED_FAILURE; >+ $(id).parent().parent().addClass('warn'); >+ } >+ >+ $(id).replaceWith( content ); >+ >+ refreshReturnClaimsTable(); >+ }, "json") >+ >+ }); >+ >+ >+ // Don't load return claims table unless it is clicked on >+ var returnClaimsTable; >+ $("#return-claims-tab").click( function() { >+ refreshReturnClaimsTable(); >+ }); >+ >+ function refreshReturnClaimsTable(){ >+ loadReturnClaimsTable(); >+ $("#return-claims-table").DataTable().ajax.reload(); >+ } >+ function loadReturnClaimsTable() { >+ if ( ! returnClaimsTable ) { >+ returnClaimsTable = $("#return-claims-table").dataTable({ >+ "bAutoWidth": false, >+ "sDom": "rt", >+ "aaSorting": [], >+ "aoColumns": [ >+ { >+ "mDataProp": "id", >+ "bVisible": false, >+ }, >+ { >+ "mDataProp": function ( oObj ) { >+ let title = `<a class="return-claim-title strong" href="/cgi-bin/koha/circ/request-rcticle.pl?biblionumber=[% rc.checkout.item.biblionumber | html %]"> >+ ${oObj.title} >+ ${oObj.enumchron || ""} >+ </a>`; >+ if ( oObj.author ) { >+ title += `by ${oObj.author}`; >+ } >+ title += `<a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=${oObj.biblionumber}&itemnumber=${oObj.itemnumber}">${oObj.barcode}</a>`; >+ >+ return title; >+ } >+ }, >+ { >+ "sClass": "return-claim-notes-td", >+ "mDataProp": function ( oObj ) { >+ return ` >+ <span id="return-claim-notes-static-${oObj.id}" class="return-claim-notes" data-return-claim-id="${oObj.id}">${oObj.notes}</span> >+ <i style="float:right" class="fa fa-pencil-square-o" title="Double click to edit"></i> >+ `; >+ } >+ }, >+ { >+ "mDataProp": "created_on", >+ }, >+ { >+ "mDataProp": "updated_on", >+ }, >+ { >+ "mDataProp": function ( oObj ) { >+ if ( ! oObj.resolution ) return ""; >+ >+ let desc = `<strong>${oObj.resolution_data.lib}</strong> on <i>${oObj.resolved_on_formatted}</i>`; >+ if (oObj.resolved_by_data) desc += ` by <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=${oObj.resolved_by_data.borrowernumber}">${oObj.resolved_by_data.firstname || ""} ${oObj.resolved_by_data.surname || ""}</a>`; >+ return desc; >+ } >+ }, >+ { >+ "mDataProp": function ( oObj ) { >+ let delete_html = oObj.resolved_on >+ ? `<li><a href="#" class="return-claim-tools-delete" data-return-claim-id="${oObj.id}"><i class="fa fa-trash"></i> Delete</a></li>` >+ : ""; >+ let resolve_html = ! oObj.resolution >+ ? `<li><a href="#" class="return-claim-tools-resolve" data-return-claim-id="${oObj.id}"><i class="fa fa-check-square"></i> Resolve</a></li>` >+ : ""; >+ >+ return ` >+ <div class="btn-group"> >+ <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> >+ Actions <span class="caret"></span> >+ </button> >+ <ul class="dropdown-menu"> >+ <li><a href="#" class="return-claim-tools-editnotes" data-return-claim-id="${oObj.id}"><i class="fa fa-edit"></i> Edit notes</a></li> >+ ${resolve_html} >+ ${delete_html} >+ </ul> >+ </div> >+ `; >+ } >+ }, >+ ], >+ "bPaginate": false, >+ "bProcessing": true, >+ "bServerSide": false, >+ "sAjaxSource": '/cgi-bin/koha/svc/return_claims', >+ "fnServerData": function ( sSource, aoData, fnCallback ) { >+ aoData.push( { "name": "borrowernumber", "value": borrowernumber } ); >+ >+ $.getJSON( sSource, aoData, function (json) { >+ let resolved = json.resolved; >+ let unresolved = json.unresolved; >+ >+ $('#return-claims-count-resolved').text(resolved); >+ $('#return-claims-count-unresolved').text(unresolved); >+ >+ fnCallback(json) >+ } ); >+ }, >+ }); >+ } >+ } >+ >+ $('body').on('click', '.return-claim-tools-editnotes', function() { >+ let id = $(this).data('return-claim-id'); >+ $(`#return-claim-notes-static-${id}`).parent().dblclick(); >+ }); >+ $('body').on('dblclick', '.return-claim-notes-td', function() { >+ let elt = $(this).children('.return-claim-notes'); >+ let id = elt.data('return-claim-id'); >+ if ( $(`#return-claim-notes-editor-textarea-${id}`).length == 0 ) { >+ let note = elt.text(); >+ let editor = ` >+ <span id="return-claim-notes-editor-${id}"> >+ <textarea id="return-claim-notes-editor-textarea-${id}">${note}</textarea> >+ <br/> >+ <a class="btn btn-default btn-xs claim-returned-notes-editor-submit" data-return-claim-id="${id}"><i class="fa fa-save"></i> Update</a> >+ <a class="claim-returned-notes-editor-cancel" data-return-claim-id="${id}" href="#">Cancel</a> >+ </span> >+ `; >+ elt.hide(); >+ $(editor).insertAfter( elt ); >+ } >+ }); >+ >+ $('body').on('click', '.claim-returned-notes-editor-submit', function(){ >+ let id = $(this).data('return-claim-id'); >+ let notes = $(`#return-claim-notes-editor-textarea-${id}`).val(); >+ >+ let params = { >+ notes: notes, >+ updated_by: $.cookie("lastborrowernumber") >+ }; >+ >+ $(this).parent().remove(); >+ >+ $.ajax({ >+ url: `/api/v1/return_claims/${id}/notes`, >+ type: 'PUT', >+ data: JSON.stringify(params), >+ success: function( data ) { >+ let notes = $(`#return-claim-notes-static-${id}`); >+ notes.text(data.notes); >+ notes.show(); >+ }, >+ contentType: "json" >+ }); >+ }); >+ >+ $('body').on('click', '.claim-returned-notes-editor-cancel', function(){ >+ let id = $(this).data('return-claim-id'); >+ $(this).parent().remove(); >+ $(`#return-claim-notes-static-${id}`).show(); >+ }); >+ >+ // Hanld return claim deletion >+ $('body').on('click', '.return-claim-tools-delete', function() { >+ let confirmed = confirm(CONFIRM_DELETE_RETURN_CLAIM); >+ if ( confirmed ) { >+ let id = $(this).data('return-claim-id'); >+ >+ $.ajax({ >+ url: `/api/v1/return_claims/${id}`, >+ type: 'DELETE', >+ success: function( data ) { >+ refreshReturnClaimsTable(); >+ } >+ }); >+ } >+ }); >+ >+ // Handle return claim resolution >+ $('body').on('click', '.return-claim-tools-resolve', function() { >+ let id = $(this).data('return-claim-id'); >+ >+ $('#claims-returned-resolved-modal-id').val(id); >+ $('#claims-returned-resolved-modal').modal() >+ }); >+ >+ $(document).on('click', '#claims-returned-resolved-modal-btn-submit', function(e) { >+ let resolution = $('#claims-returned-resolved-modal-resolved-code').val(); >+ let id = $('#claims-returned-resolved-modal-id').val(); >+ >+ $('#claims-returned-resolved-modal-btn-submit-spinner').show(); >+ $('#claims-returned-resolved-modal-btn-submit-icon').hide(); >+ >+ params = { >+ resolution: resolution, >+ updated_by: $.cookie("lastborrowernumber"), >+ }; >+ >+ $.ajax({ >+ url: `/api/v1/return_claims/${id}/resolve`, >+ type: 'PUT', >+ data: JSON.stringify(params), >+ success: function( data ) { >+ $('#claims-returned-resolved-modal-btn-submit-spinner').hide(); >+ $('#claims-returned-resolved-modal-btn-submit-icon').show(); >+ $('#claims-returned-resolved-modal').modal('hide') >+ >+ refreshReturnClaimsTable(); >+ }, >+ contentType: "json" >+ }); >+ >+ }); >+ > }); >diff --git a/svc/checkouts b/svc/checkouts >index 892eab84fe..053b589843 100755 >--- a/svc/checkouts >+++ b/svc/checkouts >@@ -64,28 +64,28 @@ print $input->header( -type => 'text/plain', -charset => 'UTF-8' ); > my @parameters; > my $sql = ' > SELECT >- issuedate, >- date_due, >- date_due < now() as date_due_overdue, >+ issues.issuedate, >+ issues.date_due, >+ issues.date_due < now() as date_due_overdue, > issues.timestamp, > >- onsite_checkout, >+ issues.onsite_checkout, > >- biblionumber, >+ biblio.biblionumber, > biblio.title, > biblio.subtitle, > biblio.medium, > biblio.part_number, > biblio.part_name, >- author, >+ biblio.author, > >- itemnumber, >- barcode, >+ items.itemnumber, >+ items.barcode, > branches2.branchname AS homebranch, >- itemnotes, >- itemnotes_nonpublic, >- itemcallnumber, >- replacementprice, >+ items.itemnotes, >+ items.itemnotes_nonpublic, >+ items.itemcallnumber, >+ items.replacementprice, > > issues.branchcode, > branches.branchname, >@@ -95,17 +95,23 @@ my $sql = ' > > items.ccode AS collection, > >- borrowernumber, >- surname, >- firstname, >- cardnumber, >+ borrowers.borrowernumber, >+ borrowers.surname, >+ borrowers.firstname, >+ borrowers.cardnumber, > >- itemlost, >- damaged, >- location, >+ items.itemlost, >+ items.damaged, >+ items.location, > items.enumchron, > >- DATEDIFF( issuedate, CURRENT_DATE() ) AS not_issued_today >+ DATEDIFF( issues.issuedate, CURRENT_DATE() ) AS not_issued_today, >+ >+ return_claims.id AS return_claim_id, >+ return_claims.notes AS return_claim_notes, >+ return_claims.created_on AS return_claim_created_on, >+ return_claims.updated_on AS return_claim_updated_on >+ > FROM issues > LEFT JOIN items USING ( itemnumber ) > LEFT JOIN biblio USING ( biblionumber ) >@@ -113,7 +119,8 @@ my $sql = ' > LEFT JOIN borrowers USING ( borrowernumber ) > LEFT JOIN branches ON ( issues.branchcode = branches.branchcode ) > LEFT JOIN branches branches2 ON ( items.homebranch = branches2.branchcode ) >- WHERE borrowernumber >+ LEFT JOIN return_claims USING ( issue_id ) >+ WHERE issues.borrowernumber > '; > > if ( @borrowernumber == 1 ) { >@@ -131,6 +138,7 @@ my $sth = $dbh->prepare($sql); > $sth->execute(@parameters); > > my $item_level_itypes = C4::Context->preference('item-level_itypes'); >+my $claims_returned_lost_value = C4::Context->preference('ClaimReturnedLostValue'); > > my @checkouts_today; > my @checkouts_previous; >@@ -165,9 +173,11 @@ while ( my $c = $sth->fetchrow_hashref() ) { > $collection = $av->count ? $av->next->lib : ''; > } > my $lost; >+ my $claims_returned; > if ( $c->{itemlost} ) { > my $av = Koha::AuthorisedValues->search({ category => 'LOST', authorised_value => $c->{itemlost} }); > $lost = $av->count ? $av->next->lib : ''; >+ $claims_returned = $c->{itemlost} eq $claims_returned_lost_value; > } > my $damaged; > if ( $c->{damaged} ) { >@@ -212,6 +222,14 @@ while ( my $c = $sth->fetchrow_hashref() ) { > renewals_count => $renewals_count, > renewals_allowed => $renewals_allowed, > renewals_remaining => $renewals_remaining, >+ >+ return_claim_id => $c->{return_claim_id}, >+ return_claim_notes => $c->{return_claim_notes}, >+ return_claim_created_on => $c->{return_claim_created_on}, >+ return_claim_updated_on => $c->{return_claim_updated_on}, >+ return_claim_created_on_formatted => $c->{return_claim_created_on} ? output_pref({ dt => dt_from_string( $c->{return_claim_created_on} ) }) : undef, >+ return_claim_updated_on_formatted => $c->{return_claim_updated_on} ? output_pref({ dt => dt_from_string( $c->{return_claim_updated_on} ) }) : undef, >+ > issuedate_formatted => output_pref( > { > dt => dt_from_string( $c->{issuedate} ), >@@ -225,6 +243,7 @@ while ( my $c = $sth->fetchrow_hashref() ) { > } > ), > lost => $lost, >+ claims_returned => $claims_returned, > damaged => $damaged, > borrower => { > surname => $c->{surname}, >diff --git a/svc/return_claims b/svc/return_claims >new file mode 100755 >index 0000000000..e10d5f174e >--- /dev/null >+++ b/svc/return_claims >@@ -0,0 +1,127 @@ >+#!/usr/bin/perl >+ >+# Copyright 2019 ByWater Solutions >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use CGI; >+use JSON qw(to_json); >+ >+use C4::Auth qw(check_cookie_auth haspermission get_session); >+use C4::Context; >+ >+use Koha::AuthorisedValues; >+use Koha::DateUtils; >+use Koha::Patrons; >+ >+my $input = new CGI; >+ >+my ( $auth_status, $sessionID ) = >+ check_cookie_auth( $input->cookie('CGISESSID') ); >+ >+my $session = get_session($sessionID); >+my $userid = $session->param('id'); >+ >+unless ( >+ haspermission( >+ $userid, { circulate => 'circulate_remaining_permissions' } >+ ) >+ || haspermission( $userid, { borrowers => 'edit_borrowers' } ) >+ ) >+{ >+ exit 0; >+} >+ >+my @sort_columns = qw/title notes created_on updated_on/; >+ >+my $borrowernumber = $input->param('borrowernumber'); >+my $offset = $input->param('iDisplayStart'); >+my $results_per_page = $input->param('iDisplayLength') || -1; >+ >+my $sorting_column = $input->param('iSortCol_0') || q{}; >+$sorting_column = >+ ( $sorting_column && $sort_columns[$sorting_column] ) >+ ? $sort_columns[$sorting_column] >+ : 'created_on'; >+ >+my $sorting_direction = $input->param('sSortDir_0') || q{}; >+$sorting_direction = $sorting_direction eq 'asc' ? 'asc' : 'desc'; >+ >+$results_per_page = undef if ( $results_per_page == -1 ); >+ >+binmode STDOUT, ":encoding(UTF-8)"; >+print $input->header( -type => 'text/plain', -charset => 'UTF-8' ); >+ >+my $sql = qq{ >+ SELECT >+ return_claims.*, >+ >+ biblio.biblionumber, >+ biblio.title, >+ biblio.author, >+ >+ items.enumchron, >+ items.barcode >+ FROM return_claims >+ LEFT JOIN items USING ( itemnumber ) >+ LEFT JOIN biblio USING ( biblionumber ) >+ LEFT JOIN biblioitems USING ( biblionumber ) >+ WHERE return_claims.borrowernumber = ? >+ ORDER BY $sorting_column $sorting_direction >+}; >+ >+my $dbh = C4::Context->dbh(); >+my $sth = $dbh->prepare($sql); >+$sth->execute($borrowernumber); >+ >+my $resolved = 0; >+my $unresolved = 0; >+my @return_claims; >+while ( my $claim = $sth->fetchrow_hashref() ) { >+ $claim->{created_on_formatted} = output_pref( { dt => dt_from_string( $claim->{created_on} ) } ) if $claim->{created_on}; >+ $claim->{updated_on_formatted} = output_pref( { dt => dt_from_string( $claim->{updated_on} ) } ) if $claim->{updated_on}; >+ $claim->{resolved_on_formatted} = output_pref( { dt => dt_from_string( $claim->{resolved_on} ) } ) if $claim->{resolved_on}; >+ >+ my $patron = $claim->{resolved_by} ? Koha::Patrons->find( $claim->{resolved_by} ) : undef; >+ $claim->{resolved_by_data} = $patron->unblessed if $patron; >+ >+ my $resolution = $claim->{resolution} >+ ? Koha::AuthorisedValues->find( >+ { >+ category => 'RETURN_CLAIM_RESOLUTION', >+ authorised_value => $claim->{resolution}, >+ } >+ ) >+ : undef; >+ $claim->{resolution_data} = $resolution->unblessed if $resolution; >+ >+ $claim->{resolved_on} ? $resolved++ : $unresolved++; >+ >+ push( @return_claims, $claim ); >+} >+ >+my $data = { >+ iTotalRecords => scalar @return_claims, >+ iTotalDisplayRecords => scalar @return_claims, >+ sEcho => $input->param('sEcho') || undef, >+ aaData => \@return_claims, >+ resolved => $resolved, >+ unresolved => $unresolved >+}; >+ >+print to_json($data); >diff --git a/t/db_dependent/api/v1/return_claims.t b/t/db_dependent/api/v1/return_claims.t >new file mode 100644 >index 0000000000..f377ccd277 >--- /dev/null >+++ b/t/db_dependent/api/v1/return_claims.t >@@ -0,0 +1,159 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 25; >+use Test::MockModule; >+use Test::Mojo; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+ >+use DateTime; >+ >+use C4::Context; >+use C4::Circulation; >+ >+use Koha::Checkouts::ReturnClaims; >+use Koha::Database; >+use Koha::DateUtils; >+ >+my $schema = Koha::Database->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+$schema->storage->txn_begin; >+ >+my $dbh = C4::Context->dbh; >+ >+my $librarian = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 1 } >+ } >+); >+my $password = 'thePassword123'; >+$librarian->set_password( { password => $password, skip_validation => 1 } ); >+my $userid = $librarian->userid; >+ >+my $patron = $builder->build_object( >+ { >+ class => 'Koha::Patrons', >+ value => { flags => 0 } >+ } >+); >+my $unauth_password = 'thePassword000'; >+$patron->set_password( >+ { password => $unauth_password, skip_validattion => 1 } ); >+my $unauth_userid = $patron->userid; >+my $patron_id = $patron->borrowernumber; >+ >+my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode}; >+my $module = new Test::MockModule('C4::Context'); >+$module->mock( 'userenv', sub { { branch => $branchcode } } ); >+ >+my $item1 = $builder->build_sample_item; >+my $itemnumber1 = $item1->itemnumber; >+ >+my $date_due = DateTime->now->add( weeks => 2 ); >+my $issue1 = >+ C4::Circulation::AddIssue( $patron->unblessed, $item1->barcode, $date_due ); >+ >+t::lib::Mocks::mock_preference( 'ClaimReturnedChargeFee', 'ask' ); >+t::lib::Mocks::mock_preference( 'ClaimReturnedLostValue', '99' ); >+ >+# Test creating a return claim >+## Invalid id >+$t->post_ok( >+ "//$userid:$password@/api/v1/return_claims/claim/1" => json => { >+ charge_lost_fee => Mojo::JSON->false, >+ created_by => $librarian->id, >+ notes => "This is a test note." >+ } >+)->status_is(404); >+ >+## Valid id >+$t->post_ok( >+ "//$userid:$password@/api/v1/return_claims/claim/$itemnumber1" => json => { >+ charge_lost_fee => Mojo::JSON->false, >+ created_by => $librarian->id, >+ notes => "This is a test note." >+ } >+)->status_is(200); >+my $claim_id = $t->tx->res->json->{id}; >+ >+## Duplicate id >+$t->post_ok( >+ "//$userid:$password@/api/v1/return_claims/claim/$itemnumber1" => json => { >+ charge_lost_fee => Mojo::JSON->false, >+ created_by => $librarian->id, >+ notes => "This is a test note." >+ } >+)->status_is(400); >+ >+# Test editing a claim note >+## Valid claim id >+$t->put_ok( >+ "//$userid:$password@/api/v1/return_claims/$claim_id/notes" => json => { >+ notes => "This is a different test note.", >+ updated_by => $librarian->id, >+ } >+)->status_is(200); >+my $claim = Koha::Checkouts::ReturnClaims->find($claim_id); >+is( $claim->notes, "This is a different test note." ); >+is( $claim->updated_by, $librarian->id ); >+ok( $claim->updated_on ); >+ >+## Bad claim id >+$t->put_ok( >+ "//$userid:$password@/api/v1/return_claims/99999999999/notes" => json => { >+ notes => "This is a different test note.", >+ updated_by => $librarian->id, >+ } >+)->status_is(404); >+ >+# Resolve a claim >+## Valid claim id >+$t->put_ok( >+ "//$userid:$password@/api/v1/return_claims/$claim_id/resolve" => json => { >+ resolved_by => $librarian->id, >+ resolution => "FOUNDINLIB", >+ } >+)->status_is(200); >+$claim = Koha::Checkouts::ReturnClaims->find($claim_id); >+is( $claim->resolution, "FOUNDINLIB" ); >+is( $claim->updated_by, $librarian->id ); >+ok( $claim->resolved_on ); >+ >+## Invalid claim id >+$t->put_ok( >+ "//$userid:$password@/api/v1/return_claims/999999999999/resolve" => json => { >+ resolved_by => $librarian->id, >+ resolution => "FOUNDINLIB", >+ } >+)->status_is(404); >+ >+# Test deleting a return claim >+$t = $t->delete_ok("//$userid:$password@/api/v1/return_claims/$claim_id") >+ ->status_is(200); >+$claim = Koha::Checkouts::ReturnClaims->find($claim_id); >+isnt( $claim, "Return claim was deleted" ); >+ >+$t->delete_ok("//$userid:$password@/api/v1/return_claims/$claim_id") >+ ->status_is(404); >-- >2.11.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 14697
:
92407
|
92408
|
92409
|
92410
|
92962
|
92963
|
92964
|
92965
|
93005
|
93006
|
93007
|
93008
|
93012
|
93013
|
93014
|
93015
|
93017
|
93038
|
93039
|
93040
|
93041
|
93042
|
93061
|
93062
|
93063
|
93064
|
93065
|
93952
|
93953
|
93954
|
93955
|
93956
|
93957
|
93959
|
94842
|
94843
|
94844
|
94845
|
94846
|
94847
|
94848
|
94849
|
94850
|
94851
|
94852
|
94853
|
94854
|
94857
|
94877
|
94878