This adds a "Claims returned" feature that extends and enhances the claims returned lost status.
To use this feature, a new LOST status to represent an item claimed as returned needs to be created.
The value of this LOST authorised value should be set in the new syspref ClaimReturnedLostValue.
Setting this system preference turns on the feature.
Once the feature is enabled, you should be able to mark checked out items as return claims from the
checkout and patron details pages, and also modify them from the new claims tab on those pages.
Returning a claimed item will notify the librarian that the item in question has a claim on it.
Setting the ClaimReturnedWarningThreshold will add an alert to make librarians aware that this
patron has many return claims on the patron's record.
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
Sponsored-by: North Central Regional Library System
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Andrew Fuerste-Henry <andrew@bywatersolutions.com>
Signed-off-by: Lisette Scheer <lisetteslatah@gmail.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
override_high_holds => $override_high_holds,
nopermission => scalar $query->param('nopermission'),
autoswitched => $autoswitched,
+ logged_in_user => $logged_in_user,
);
output_html_with_http_headers $query, $cookie, $template->output;
elsif ( $code eq 'DataCorrupted' ) {
$err{data_corrupted} = 1;
}
- else {
+ elsif ( $code eq 'ReturnClaims' ) {
+ $template->param( ReturnClaims => $messages->{ReturnClaims} );
+ } else {
die "Unknown error code $code"; # note we need all the (empty) elsif's above, or we die.
# This forces the issue of staying in sync w/ Circulation.pm
}
<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>
<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>
--- /dev/null
+<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>
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");
var MSG_NO_ITEMTYPE = _("No itemtype");
var MSG_CHECKOUTS_BY_ITEMTYPE = _("Number of checkouts by item type");
var PATRON_NOTE = _("Patron note");
+ var CONFIRM_DELETE_RETURN_CLAIM = _("Are you sure you want to delete this return claim?");
</script>
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.
<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="hidden" name="op" value="set_lost" />
+ <input type="submit" name="submit" class="submit" value="Set status" /></form>
[% END %]
- [% END %]
- </select>
- <input type="hidden" name="op" value="set_lost" />
- <input type="submit" name="submit" class="submit" value="Set status" /></form>
+ </form>
[% ELSE %]
[% FOREACH itemlostloo IN itemlostloop %]
[% IF ( itemlostloo.selected ) %]
<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 %]
</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) %]
[% END # /IF holds_count %]
</div> <!-- /#reserves -->
+ [% INCLUDE 'patron-return-claims.inc' %]
+
[% IF Koha.Preference('ArticleRequests') %]
[% INCLUDE 'patron-article-requests.inc' %]
[% END %]
[% Asset.js("js/circ-patron-search-results.js") | $raw %]
<script type="text/javascript">
/* Set some variable needed in circulation.js */
+ var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]";
+ 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 %]";
</div>
[% END %]
+ <!-- Item has return claim(s) -->
+ [% IF ( ReturnClaims ) %]
+ <div class="dialog alert return-claim">
+ <h3>
+ This item has been claimed as returned by:
+ <ul>
+ [% FOREACH rc IN ReturnClaims %]
+ [% SET patron = rc.patron %]
+ <li>
+ <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% patron.borrowernumber | uri %]">
+ [% patron.firstname | html %] [% patron.surname | html %]
+ </a>
+ </li>
+ [% END %]
+ </ul>
+ </h3>
+ </div>
+ [% END %]
+
<!-- Patron has waiting holds -->
[% IF ( waiting_holds ) %]
<div id="awaiting-pickup" class="dialog message">
<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>
</div> [% # /div#reserves %]
[% END %]
+ [% INCLUDE 'patron-return-claims.inc' %]
+
[% IF Koha.Preference('ArticleRequests') %]
[% INCLUDE 'patron-article-requests.inc' %]
[% END %]
[% Asset.js("js/messaging-preference-form.js") | $raw %]
<script>
/* Set some variable needed in circulation.js */
+ var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]";
+ 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 %]";
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>";
}
}
},
{
+ "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 ) {
}
} ).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 = {
+ item_id: itemnumber,
+ notes: notes,
+ charge_lost_fee: fee,
+ created_by: logged_in_user_borrowernumber,
+ };
+
+ $.post( '/api/v1/return_claims', JSON.stringify(params), function( data ) {
+
+ id = "#return_claim_spinner_" + data.item_id;
+
+ let created_on = new Date(data.created_on);
+
+ let content = "";
+ if ( data.claim_id ) {
+ content = `<span class="badge">${created_on.toLocaleDateString()}</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": function ( oObj ) {
+ let created_on = new Date( oObj.created_on );
+ return created_on.toLocaleDateString();
+ }
+ },
+ {
+ "mDataProp": function ( oObj ) {
+ let updated_on = new Date( oObj.updated_on );
+ return updated_on.toLocaleDateString();
+ }
+ },
+ {
+ "mDataProp": function ( oObj ) {
+ if ( ! oObj.resolution ) return "";
+
+ let desc = `<strong>${oObj.resolution_data.lib}</strong> on <i>${oObj.resolved_on}</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: logged_in_user_borrowernumber
+ };
+
+ $(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: logged_in_user_borrowernumber
+ };
+
+ $.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"
+ });
+
+ });
+
});
housebound_role => scalar $patron->housebound_role,
relatives_issues_count => $relatives_issues_count,
relatives_borrowernumbers => \@relatives,
+ logged_in_user => $logged_in_user,
);
output_html_with_http_headers $input, $cookie, $template->output;
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,
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 )
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 ) {
$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;
$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} ) {
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} ),
}
),
lost => $lost,
+ claims_returned => $claims_returned,
damaged => $damaged,
borrower => {
surname => $c->{surname},
--- /dev/null
+#!/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);