Bug 42443: Convert transfers to receive page to ajax datatable
Updates circ/transferstoreceive.pl to use the new REST API with an ajax DataTable to display transfer to receive. Test Plan: 1) Apply this patch 2) Restart all the things! 3) Set an item in transit to your logged in library ( check it in at another branch so it is sent home, or place a hold to trigger a transfer ) 4) Place a hold on a second transferred item, and a recall on a third 5) Browse to /cgi-bin/koha/circ/transferstoreceive.pl 6) Note the "On hold for" column shows the hold patron, the recall patron or "None" 7) Set TransfersMaxDaysWarning low and note the "days late" badge appears 8) Click "Cancel transfer" and note the transfer is cancelled 9) A library with no incoming transfers will get "No transfers to receive" Signed-off-by: Alex Carver [Acerock7] <alex@rcls.org> Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk> Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io> Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
parent
4fc5617615
commit
560d7b7ff7
|
|
@ -2087,6 +2087,26 @@ modules:
|
||||||
cannot_be_toggled: 1
|
cannot_be_toggled: 1
|
||||||
cannot_be_modified: 1
|
cannot_be_modified: 1
|
||||||
|
|
||||||
|
transferstoreceive:
|
||||||
|
transferstoreceive:
|
||||||
|
columns:
|
||||||
|
-
|
||||||
|
columnname: sentfrom
|
||||||
|
-
|
||||||
|
columnname: datesent
|
||||||
|
-
|
||||||
|
columnname: title
|
||||||
|
-
|
||||||
|
columnname: holdfor
|
||||||
|
-
|
||||||
|
columnname: homelibrary
|
||||||
|
-
|
||||||
|
columnname: callnumber
|
||||||
|
-
|
||||||
|
columnname: action
|
||||||
|
cannot_be_toggled: 1
|
||||||
|
cannot_be_modified: 1
|
||||||
|
|
||||||
view_holdsqueue:
|
view_holdsqueue:
|
||||||
holds-table:
|
holds-table:
|
||||||
default_sort_order: 3
|
default_sort_order: 3
|
||||||
|
|
|
||||||
|
|
@ -22,22 +22,11 @@ use CGI qw ( -utf8 );
|
||||||
use C4::Context;
|
use C4::Context;
|
||||||
use C4::Output qw( output_html_with_http_headers );
|
use C4::Output qw( output_html_with_http_headers );
|
||||||
use C4::Auth qw( get_template_and_user );
|
use C4::Auth qw( get_template_and_user );
|
||||||
use C4::Circulation qw( GetTransfersFromTo );
|
|
||||||
use C4::Members;
|
|
||||||
use Date::Calc qw( Add_Delta_Days Date_to_Days Today );
|
|
||||||
|
|
||||||
use C4::Reserves;
|
|
||||||
use Koha::Items;
|
|
||||||
use Koha::ItemTypes;
|
|
||||||
use Koha::Libraries;
|
|
||||||
use Koha::DateUtils qw( dt_from_string );
|
use Koha::DateUtils qw( dt_from_string );
|
||||||
use Koha::BiblioFrameworks;
|
|
||||||
use Koha::Patrons;
|
|
||||||
|
|
||||||
my $input = CGI->new;
|
my $input = CGI->new;
|
||||||
my $itemnumber = $input->param('itemnumber');
|
|
||||||
|
|
||||||
my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user(
|
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
|
||||||
{
|
{
|
||||||
template_name => "circ/transferstoreceive.tt",
|
template_name => "circ/transferstoreceive.tt",
|
||||||
query => $input,
|
query => $input,
|
||||||
|
|
@ -46,96 +35,10 @@ my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
# set the userenv branch
|
|
||||||
my $default = C4::Context->userenv->{'branch'};
|
|
||||||
|
|
||||||
# get the all the branches for reference
|
|
||||||
my $libraries = Koha::Libraries->search( {}, { order_by => 'branchname' } );
|
|
||||||
my @branchesloop;
|
|
||||||
my $latetransfers;
|
|
||||||
while ( my $library = $libraries->next ) {
|
|
||||||
my @transferloop;
|
|
||||||
my %branchloop;
|
|
||||||
my @gettransfers = GetTransfersFromTo( $library->branchcode, $default );
|
|
||||||
|
|
||||||
if (@gettransfers) {
|
|
||||||
$branchloop{'branchname'} = $library->branchname;
|
|
||||||
$branchloop{'branchcode'} = $library->branchcode;
|
|
||||||
foreach my $num (@gettransfers) {
|
|
||||||
my %getransf;
|
|
||||||
|
|
||||||
my ( $sent_year, $sent_month, $sent_day ) = split "-",
|
|
||||||
$num->{'datesent'};
|
|
||||||
$sent_day = ( split " ", $sent_day )[0];
|
|
||||||
( $sent_year, $sent_month, $sent_day ) = Add_Delta_Days(
|
|
||||||
$sent_year, $sent_month, $sent_day,
|
|
||||||
C4::Context->preference('TransfersMaxDaysWarning')
|
|
||||||
);
|
|
||||||
my $calcDate = Date_to_Days( $sent_year, $sent_month, $sent_day );
|
|
||||||
my $today = Date_to_Days(&Today);
|
|
||||||
my $diff = $today - $calcDate;
|
|
||||||
|
|
||||||
if ( $today > $calcDate ) {
|
|
||||||
$latetransfers = 1;
|
|
||||||
$getransf{'messcompa'} = 1;
|
|
||||||
$getransf{'diff'} = $diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $item = Koha::Items->find( $num->{itemnumber} );
|
|
||||||
my $biblio = $item->biblio;
|
|
||||||
my $itemtype = Koha::ItemTypes->find( $item->effective_itemtype );
|
|
||||||
|
|
||||||
$getransf{'datetransfer'} = $num->{'datesent'};
|
|
||||||
$getransf{'itemtype'} = $itemtype->description; # FIXME Should not it be translated_description?
|
|
||||||
%getransf = (
|
|
||||||
%getransf,
|
|
||||||
title => $biblio->title,
|
|
||||||
subtitle => $biblio->subtitle,
|
|
||||||
medium => $biblio->medium,
|
|
||||||
part_number => $biblio->part_number,
|
|
||||||
part_name => $biblio->part_name,
|
|
||||||
author => $biblio->author,
|
|
||||||
biblionumber => $biblio->biblionumber,
|
|
||||||
itemnumber => $item->itemnumber,
|
|
||||||
barcode => $item->barcode,
|
|
||||||
ccode => $item->ccode,
|
|
||||||
homebranch => $item->homebranch,
|
|
||||||
holdingbranch => $item->holdingbranch,
|
|
||||||
itemcallnumber => $item->itemcallnumber,
|
|
||||||
);
|
|
||||||
|
|
||||||
# we check if we have a reserve for this transfer
|
|
||||||
my $holds = $item->current_holds;
|
|
||||||
if ( my $first_hold = $holds->next ) {
|
|
||||||
$getransf{patron} = Koha::Patrons->find( $first_hold->borrowernumber );
|
|
||||||
}
|
|
||||||
|
|
||||||
# check for a recall for this transfer
|
|
||||||
if ( C4::Context->preference('UseRecalls') ) {
|
|
||||||
my $recall = $item->recall;
|
|
||||||
if ( defined $recall ) {
|
|
||||||
$getransf{recall} = $recall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
push( @transferloop, \%getransf );
|
|
||||||
}
|
|
||||||
|
|
||||||
# If we have a return of reservloop we put it in the branchloop sequence
|
|
||||||
$branchloop{'reserve'} = \@transferloop;
|
|
||||||
}
|
|
||||||
push( @branchesloop, \%branchloop ) if %branchloop;
|
|
||||||
}
|
|
||||||
|
|
||||||
$template->param(
|
$template->param(
|
||||||
branchesloop => \@branchesloop,
|
branchcode => C4::Context->userenv->{'branch'},
|
||||||
show_date => dt_from_string,
|
show_date => dt_from_string,
|
||||||
TransfersMaxDaysWarning => C4::Context->preference('TransfersMaxDaysWarning'),
|
TransfersMaxDaysWarning => C4::Context->preference('TransfersMaxDaysWarning'),
|
||||||
latetransfers => $latetransfers ? 1 : 0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
# Checking if there is a Fast Cataloging Framework
|
|
||||||
$template->param( fast_cataloging => 1 ) if Koha::BiblioFrameworks->find('FA');
|
|
||||||
|
|
||||||
output_html_with_http_headers $input, $cookie, $template->output;
|
output_html_with_http_headers $input, $cookie, $template->output;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
[% USE raw %]
|
[% USE raw %]
|
||||||
[% USE Asset %]
|
[% USE Asset %]
|
||||||
[% USE AuthorisedValues %]
|
|
||||||
[% USE Koha %]
|
[% USE Koha %]
|
||||||
[% USE KohaDates %]
|
[% USE KohaDates %]
|
||||||
[% USE Branches %]
|
[% USE Branches %]
|
||||||
|
[% USE TablesSettings %]
|
||||||
[% PROCESS 'i18n.inc' %]
|
[% PROCESS 'i18n.inc' %]
|
||||||
[% SET footerjs = 1 %]
|
[% SET footerjs = 1 %]
|
||||||
[% INCLUDE 'doc-head-open.inc' %]
|
[% INCLUDE 'doc-head-open.inc' %]
|
||||||
|
|
@ -35,102 +35,171 @@
|
||||||
[% SET aside = Koha.Preference('CircSidebar') ? 'circ-nav' : '' %]
|
[% SET aside = Koha.Preference('CircSidebar') ? 'circ-nav' : '' %]
|
||||||
[% WRAPPER 'main-container.inc' aside=aside %]
|
[% WRAPPER 'main-container.inc' aside=aside %]
|
||||||
<h1>Transfers made to your library as of [% show_date | $KohaDates %]</h1>
|
<h1>Transfers made to your library as of [% show_date | $KohaDates %]</h1>
|
||||||
[% IF ( branchesloop ) %]
|
<p>Your library is the destination for the following transfer(s)</p>
|
||||||
<p>Your library is the destination for the following transfer(s)</p>
|
[% IF ( TransfersMaxDaysWarning ) %]<p>Transfers are <span class="error">considered late</span> after [% TransfersMaxDaysWarning | html %] days.</p>[% END %]
|
||||||
[% IF ( latetransfers ) %]<p>Transfers are <span class="error">considered late</span> after [% TransfersMaxDaysWarning | html %] days.</p>[% END %]
|
<div id="resultlist">
|
||||||
<div id="resultlist">
|
<div class="page-section">
|
||||||
[% FOREACH branchesloo IN branchesloop %]
|
<table id="transferstoreceive"></table>
|
||||||
[% IF ( branchesloo.branchcode ) %]
|
|
||||||
<div class="page-section">
|
|
||||||
<table style="width: 100%" id="transferst[% branchesloo.branchcode | html %]">
|
|
||||||
<caption>Coming from [% branchesloo.branchname | html %]</caption>
|
|
||||||
<thead
|
|
||||||
><tr>
|
|
||||||
<th>Date of transfer</th>
|
|
||||||
<th class="anti-the">Title</th>
|
|
||||||
<th>On hold for</th>
|
|
||||||
<th>Home library</th>
|
|
||||||
<th>Call number</th>
|
|
||||||
<th class="no-sort"> </th>
|
|
||||||
</tr></thead
|
|
||||||
>
|
|
||||||
<tbody
|
|
||||||
>[% FOREACH reser IN branchesloo.reserve %]
|
|
||||||
<tr>
|
|
||||||
<td data-order="[% reser.datetransfer | html %]">
|
|
||||||
[% reser.datetransfer | $KohaDates %]
|
|
||||||
[% IF ( reser.messcompa ) %]
|
|
||||||
<span class="badge bg-warning text-dark">[% tnx('{count} day late', '{count} days late', reser.diff, { count = reser.diff }) %]</span>
|
|
||||||
[% END %]
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
[% INCLUDE 'biblio-title.inc' biblio=reser link = 1 %] [% IF ( reser.author ) %]by [% reser.author | html %][% END %]
|
|
||||||
[% IF ( reser.itemtype ) %](<strong>[% reser.itemtype | html %]</strong>)[% END %]
|
|
||||||
[% IF ( reser.ccode ) -%]
|
|
||||||
<span class="collection_code">Collection: [% AuthorisedValues.GetDescriptionByKohaField(kohafield => 'items.ccode', authorised_value => reser.ccode) | html %]</span>
|
|
||||||
[%- END %]
|
|
||||||
<br />Barcode: [% reser.barcode | html %]
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
>[% IF ( reser.patron ) %]
|
|
||||||
<a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% reser.patron.borrowernumber | uri %]">
|
|
||||||
[% reser.patron.surname | html %][% IF ( reser.patron.firstname ) %], [% reser.patron.firstname | html %][% END %]
|
|
||||||
</a>
|
|
||||||
[% IF ( reser.patron.phone ) %]<br />[% reser.patron.phone | html %][% END %]
|
|
||||||
[% IF ( reser.patron.notice_email_address ) %]
|
|
||||||
<br />
|
|
||||||
[% BLOCK subject %]Hold:[% END %]
|
|
||||||
<a href="mailto:[% reser.patron.notice_email_address | uri %]?subject=[% INCLUDE subject %] [% reser.title | uri %]"> [% reser.patron.notice_email_address | html %] </a>
|
|
||||||
[% END %]
|
|
||||||
[% IF reser.patron.cardnumber %]
|
|
||||||
<div class="cardnumber">Card number: [% reser.patron.cardnumber | html %]</div>
|
|
||||||
[% END %]
|
|
||||||
[% ELSIF ( reser.recall ) %]
|
|
||||||
Recall requested by
|
|
||||||
<a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% reser.recall.patron_id | uri %]"
|
|
||||||
>[% reser.recall.patron.surname | html %][% IF reser.recall.patron.firstname %], [% reser.recall.patron.firstname | html %][% END %] ([% reser.recall.patron.cardnumber | html %])</a
|
|
||||||
>
|
|
||||||
[% ELSE %]
|
|
||||||
<p>None</p>
|
|
||||||
[% END %]
|
|
||||||
</td>
|
|
||||||
<td>[% IF reser.homebranch %][% Branches.GetName( reser.homebranch ) | html %][% END %]</td>
|
|
||||||
<td>[% reser.itemcallnumber | html %]</td>
|
|
||||||
<td class="actions"
|
|
||||||
><a href="/cgi-bin/koha/circ/returns.pl?itemnumber=[% reser.itemnumber | uri %]&canceltransfer=1&dest=ttr" class="btn btn-default btn-xs"
|
|
||||||
><i class="fa fa-times"></i> Cancel transfer</a
|
|
||||||
></td
|
|
||||||
>
|
|
||||||
</tr>
|
|
||||||
[% END %]</tbody
|
|
||||||
>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- /.page-section -->
|
|
||||||
[% END %]
|
|
||||||
[% END %]
|
|
||||||
</div>
|
</div>
|
||||||
[% ELSE %]
|
<!-- /.page-section -->
|
||||||
<p>No transfers to receive</p>
|
</div>
|
||||||
[% END %]
|
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
[% MACRO jsinclude BLOCK %]
|
[% MACRO jsinclude BLOCK %]
|
||||||
[% INCLUDE 'datatables.inc' %]
|
[% INCLUDE 'datatables.inc' %]
|
||||||
<script nonce="[% Koha.CSPNonce | $raw %]" data-tt-allowed="true">
|
[% INCLUDE 'js-biblio-format.inc' %]
|
||||||
let library_ids = [];
|
[% INCLUDE 'js-date-format.inc' %]
|
||||||
[% FOR library IN branchesloop %]
|
<script nonce="[% Koha.CSPNonce | $raw %]">
|
||||||
library_ids.push("[% library.branchcode | html %]");
|
var table_settings = [% TablesSettings.GetTableSettings( 'circ', 'transferstoreceive', 'transferstoreceive', 'json' ) | $raw %];
|
||||||
[% END %]
|
var TransfersMaxDaysWarning = [% TransfersMaxDaysWarning ? TransfersMaxDaysWarning : 0 | html %];
|
||||||
|
|
||||||
|
// Mirror the legacy "late transfer" calculation: a transfer is late once it has been
|
||||||
|
// sent for more than TransfersMaxDaysWarning days without arriving
|
||||||
|
function days_late(sent_date) {
|
||||||
|
if (!sent_date || !TransfersMaxDaysWarning) return 0;
|
||||||
|
var sent = new Date(sent_date);
|
||||||
|
var threshold = new Date(sent.getFullYear(), sent.getMonth(), sent.getDate() + Number(TransfersMaxDaysWarning));
|
||||||
|
var now = new Date();
|
||||||
|
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||||
|
var diff = Math.floor((today - threshold) / 86400000);
|
||||||
|
return diff > 0 ? diff : 0;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<script nonce="[% Koha.CSPNonce | $raw %]">
|
<script nonce="[% Koha.CSPNonce | $raw %]">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
library_ids.forEach(library_id => {
|
var additional_filters = {
|
||||||
$(`#transferst${library_id}`).kohaTable({
|
to_library_id: "[% branchcode | html %]",
|
||||||
dom: "t",
|
sent_date: { "!=": null },
|
||||||
paging: false,
|
arrived_date: { "=": null },
|
||||||
});
|
cancellation_date: { "=": null },
|
||||||
});
|
};
|
||||||
|
|
||||||
|
$("#transferstoreceive").kohaTable(
|
||||||
|
{
|
||||||
|
ajax: {
|
||||||
|
url: "/api/v1/transfers",
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
emptyTable: _("No transfers to receive"),
|
||||||
|
},
|
||||||
|
embed: [
|
||||||
|
"item+strings",
|
||||||
|
"item.biblio",
|
||||||
|
"item.home_library",
|
||||||
|
"item.item_type",
|
||||||
|
"item.first_hold.patron",
|
||||||
|
"item.recall.patron",
|
||||||
|
"from_library",
|
||||||
|
],
|
||||||
|
order: [[1, "asc"]],
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
data: "me.from_library_id",
|
||||||
|
title: _("Sent from"),
|
||||||
|
searchable: true,
|
||||||
|
orderable: true,
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
return escape_str(row.from_library.name);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "sent_date",
|
||||||
|
title: _("Date of transfer"),
|
||||||
|
type: "date",
|
||||||
|
searchable: false,
|
||||||
|
orderable: true,
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
var html = $date(data);
|
||||||
|
var late = days_late(data);
|
||||||
|
if (late > 0) {
|
||||||
|
var badge = __nx("{count} day late", "{count} days late", late, { count: late });
|
||||||
|
html += ` <span class="badge bg-warning text-dark">${badge}</span>`;
|
||||||
|
}
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "item.biblio.title",
|
||||||
|
title: _("Title"),
|
||||||
|
searchable: true,
|
||||||
|
orderable: true,
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
var item = row.item;
|
||||||
|
var html = $biblio_to_html(item.biblio, { link: 1 });
|
||||||
|
|
||||||
|
if (item.biblio.author) {
|
||||||
|
var author = escape_str(item.biblio.author);
|
||||||
|
html += ` ${_("by")} ${author}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.item_type) {
|
||||||
|
var item_type = escape_str(item.item_type.translated_description || item.item_type.description);
|
||||||
|
if (item_type) html += ` (<strong>${item_type}</strong>)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item._strings.collection_code && item._strings.collection_code.str) {
|
||||||
|
var collection_code = escape_str(item._strings.collection_code.str);
|
||||||
|
html += ` <span class="collection_code">${_("Collection:")} ${collection_code}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
var barcode = escape_str(item.external_id);
|
||||||
|
html += `<br>${_("Barcode:")} ${barcode}`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: null,
|
||||||
|
title: _("On hold for"),
|
||||||
|
searchable: false,
|
||||||
|
orderable: false,
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
var item = row.item;
|
||||||
|
if (item.first_hold) {
|
||||||
|
return $patron_to_html(item.first_hold.patron, { url: true, display_cardnumber: true });
|
||||||
|
} else if (item.recall) {
|
||||||
|
var patron = $patron_to_html(item.recall.patron, { url: true, display_cardnumber: true });
|
||||||
|
return `${_("Recall requested by")} ${patron}`;
|
||||||
|
} else {
|
||||||
|
return _("None");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "item.home_library_id",
|
||||||
|
title: _("Home library"),
|
||||||
|
searchable: true,
|
||||||
|
orderable: true,
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
return escape_str(row.item.home_library.name);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "item.callnumber",
|
||||||
|
title: _("Call number"),
|
||||||
|
searchable: true,
|
||||||
|
orderable: true,
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
return escape_str(data);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: null,
|
||||||
|
title: _("Actions"),
|
||||||
|
class: "actions",
|
||||||
|
searchable: false,
|
||||||
|
orderable: false,
|
||||||
|
render: function (data, type, row, meta) {
|
||||||
|
var itemnumber = encodeURIComponent(row.item_id);
|
||||||
|
return `<a href="/cgi-bin/koha/circ/returns.pl?itemnumber=${itemnumber}&canceltransfer=1&dest=ttr" class="btn btn-default btn-xs"><i class="fa fa-times"></i> ${_("Cancel transfer")}</a>`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
table_settings,
|
||||||
|
1,
|
||||||
|
additional_filters
|
||||||
|
);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue