View | Details | Raw Unified | Return to bug 18589
Collapse All | Expand All

(-)a/Koha/REST/V1/Illrequests.pm (-1 / +9 lines)
Lines 56-62 sub list { Link Here
56
    }
56
    }
57
57
58
    # Get all requests
58
    # Get all requests
59
    my @requests = Koha::Illrequests->as_list;
59
    # If necessary, only get those from a specified patron
60
    my @requests;
61
    if ($args->{borrowernumber}) {
62
		@requests = Koha::Illrequests->search(
63
			{ borrowernumber => $args->{borrowernumber} }
64
		);
65
    } else {
66
        @requests = Koha::Illrequests->as_list;
67
    }
60
68
61
    # Identify patrons & branches that
69
    # Identify patrons & branches that
62
    # we're going to need and get them
70
    # we're going to need and get them
(-)a/ill/ill-requests.pl (-3 / +2 lines)
Lines 278-290 if ( $backends_available ) { Link Here
278
        my $active_filters = [];
278
        my $active_filters = [];
279
        foreach my $filter(@{$possible_filters}) {
279
        foreach my $filter(@{$possible_filters}) {
280
            if ($params->{$filter}) {
280
            if ($params->{$filter}) {
281
                push @{$active_filters},
281
                push @{$active_filters}, "$filter=$params->{$filter}";
282
                    { name => $filter, value => $params->{$filter}};
283
            }
282
            }
284
        }
283
        }
285
        if (scalar @{$active_filters} > 0) {
284
        if (scalar @{$active_filters} > 0) {
286
            $template->param(
285
            $template->param(
287
                prefilters => $active_filters
286
                prefilters => join(",", @{$active_filters})
288
            );
287
            );
289
        }
288
        }
290
289
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc (-1 / +1 lines)
Lines 127-133 Link Here
127
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% patron.borrowernumber | uri %]">Housebound</a></li>
127
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% patron.borrowernumber | uri %]">Housebound</a></li>
128
    [% END %]
128
    [% END %]
129
    [% IF Koha.Preference('ILLModule') && CAN_user_ill %]
129
    [% IF Koha.Preference('ILLModule') && CAN_user_ill %]
130
        <li><a href="/cgi-bin/koha/ill/ill-requests.pl?borrowernumber=[% patron.borrowernumber | uri %]">Interlibrary loans</a></li>
130
        [% IF illview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/ill-requests.pl?borrowernumber=[% patron.borrowernumber | uri %]">Interlibrary loans</a></li>
131
    [% END %]
131
    [% END %]
132
</ul></div>
132
</ul></div>
133
[% END %]
133
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc (+36 lines)
Line 0 Link Here
1
<table id="ill-requests"[% IF prefilters.length > 0 %] data-prefilters="[% prefilters %]"[% END %]>
2
    <thead>
3
        <tr id="illview-header">
4
            <th scope="col">Request ID</th>
5
            <th scope="col">Author</th>
6
            <th scope="col">Title</th>
7
            <th scope="col">Article title</th>
8
            <th scope="col">Issue</th>
9
            <th scope="col">Volume</th>
10
            <th scope="col">Year</th>
11
            <th scope="col">Pages</th>
12
            <th scope="col">Type</th>
13
            <th scope="col">Order ID</th>
14
            <th scope="col">Patron ID</th>
15
            <th scope="col" class="patron_cardnumber">Patron barcode</th>
16
            <th scope="col">Patron</th>
17
            <th scope="col">Biblio ID</th>
18
            <th scope="col">Branch</th>
19
            <th scope="col">Status</th>
20
            <th scope="col" class="placed">&nbsp;</th>
21
            <th scope="col" class="placed_formatted">Placed on</th>
22
            <th scope="col" class="updated">&nbsp;</th>
23
            <th scope="col" class="updated_formatted">Updated on</th>
24
            <th scope="col">Replied</th>
25
            <th scope="col">Completed on</th>
26
            <th scope="col">Access URL</th>
27
            <th scope="col">Cost</th>
28
            <th scope="col">OPAC notes</th>
29
            <th scope="col">Staff notes</th>
30
            <th scope="col">Backend</th>
31
            <th scope="col" class="actions"></th>
32
        </tr>
33
    </thead>
34
    <tbody id="illview-body">
35
    </tbody>
36
</table>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt (-531 / +18 lines)
Lines 499-542 Link Here
499
                    <h1>View ILL requests</h1>
499
                    <h1>View ILL requests</h1>
500
                    <div id="results">
500
                    <div id="results">
501
                        <h3>Details for all requests</h3>
501
                        <h3>Details for all requests</h3>
502
502
                        [% INCLUDE 'ill-list-table.inc' %]
503
                        <table id="ill-requests">
504
                            <thead>
505
                                <tr id="illview-header">
506
                                    <th scope="col">Request ID</th>
507
                                    <th scope="col">Author</th>
508
                                    <th scope="col">Title</th>
509
                                    <th scope="col">Article title</th>
510
                                    <th scope="col">Issue</th>
511
                                    <th scope="col">Volume</th>
512
                                    <th scope="col">Year</th>
513
                                    <th scope="col">Pages</th>
514
                                    <th scope="col">Type</th>
515
                                    <th scope="col">Order ID</th>
516
                                    <th scope="col">Patron ID</th>
517
                                    <th scope="col" class="patron_cardnumber">Patron barcode</th>
518
                                    <th scope="col">Patron</th>
519
                                    <th scope="col">Biblio ID</th>
520
                                    <th scope="col">Branch</th>
521
                                    <th scope="col">Status</th>
522
                                    <th scope="col" class="placed">&nbsp;</th>
523
                                    <th scope="col" class="placed_formatted">Placed on</th>
524
                                    <th scope="col">Replied</th>
525
                                    <th scope="col" class="updated">&nbsp;</th>
526
                                    <th scope="col" class="updated_formatted">Updated on</th>
527
                                    <th scope="col">Replied</th>
528
                                    <th scope="col">Completed on</th>
529
                                    <th scope="col">Access URL</th>
530
                                    <th scope="col">Cost</th>
531
                                    <th scope="col">OPAC notes</th>
532
                                    <th scope="col">Staff notes</th>
533
                                    <th scope="col">Backend</th>
534
                                    <th scope="col" class="actions"></th>
535
                                </tr>
536
                            </thead>
537
                            <tbody id="illview-body">
538
                            </tbody>
539
                        </table>
540
                    </div>
503
                    </div>
541
                [% ELSE %]
504
                [% ELSE %]
542
                <!-- Custom Backend Action -->
505
                <!-- Custom Backend Action -->
Lines 548-1053 Link Here
548
        </div>
511
        </div>
549
    </div>
512
    </div>
550
513
551
[% TRY %]
552
[% PROCESS backend_jsinclude %]
553
[% CATCH %]
554
[% END %]
555
556
[% MACRO jsinclude BLOCK %]
514
[% MACRO jsinclude BLOCK %]
557
    [% INCLUDE 'datatables.inc' %]
515
    [% INCLUDE 'datatables.inc' %]
558
    [% INCLUDE 'columns_settings.inc' %]
516
    [% INCLUDE 'columns_settings.inc' %]
559
    [% INCLUDE 'calendar.inc' %]
517
    [% INCLUDE 'calendar.inc' %]
560
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
518
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
561
    <script>
519
    <script>
562
        $(document).ready(function() {
520
        // Date format for datepickers
563
521
        var dateMap = {
564
            // Illview Datatable setup
522
            dmydot: 'dd.mm.yy',
565
523
            iso: 'yy-mm-dd',
566
            var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
524
            metric: 'dd/mm/yy',
567
525
            us: 'mm/dd/yy'
568
            var table;
526
        };
569
527
        var dateFormat = dateMap['[% Koha.Preference('dateformat') %]'];
570
            // Filters that are active
528
        var prefilters = '[% prefilters %]';
571
            var activeFilters = {};
529
        // Set column settings
572
530
        var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
573
            // Fields we need to expand (flatten)
574
            var expand = [
575
                'metadata',
576
                'patron',
577
                'library'
578
            ];
579
580
            // Expanded fields
581
            // This is auto populated
582
            var expanded = {};
583
584
            // Filterable columns
585
            var filterable = {
586
                status: {
587
                    prep: function(tableData, oData) {
588
                        var uniques = {};
589
                        tableData.forEach(function(row) {
590
                            var resolvedName = getStatusName(
591
                                oData[0].capabilities[row.status].name,
592
                                row
593
                            );
594
                            uniques[resolvedName] = 1
595
                        });
596
                        Object.keys(uniques).sort().forEach(function(unique) {
597
                            $('#illfilter_status').append(
598
                                '<option value="' + unique  +
599
                                '">' + unique +  '</option>'
600
                            );
601
                        });
602
                    },
603
                    listener: function() {
604
                        var me = 'status';
605
                        $('#illfilter_status').change(function() {
606
                            var sel = $('#illfilter_status option:selected').val();
607
                            if (sel && sel.length > 0) {
608
                                activeFilters[me] = function() {
609
                                    table.api().column(15).search(sel);
610
                                }
611
                            } else {
612
                                if (activeFilters.hasOwnProperty(me)) {
613
                                    delete activeFilters[me];
614
                                }
615
                            }
616
                        });
617
                    },
618
                    clear: function() {
619
                        $('#illfilter_status').val('');
620
                    }
621
                },
622
                pickupBranch: {
623
                    prep: function(tableData, oData) {
624
                        var uniques = {};
625
                        tableData.forEach(function(row) {
626
                            uniques[row.library_branchname] = 1
627
                        });
628
                        Object.keys(uniques).sort().forEach(function(unique) {
629
                            $('#illfilter_branchname').append(
630
                                '<option value="' + unique  +
631
                                '">' + unique +  '</option>'
632
                            );
633
                        });
634
                    },
635
                    listener: function() {
636
                        var me = 'pickupBranch';
637
                        $('#illfilter_branchname').change(function() {
638
                            var sel = $('#illfilter_branchname option:selected').val();
639
                            if (sel && sel.length > 0) {
640
                                activeFilters[me] = function() {
641
                                    table.api().column(14).search(sel);
642
                                }
643
                            } else {
644
                                if (activeFilters.hasOwnProperty(me)) {
645
                                    delete activeFilters[me];
646
                                }
647
                            }
648
                        });
649
                    },
650
                    clear: function() {
651
                        $('#illfilter_branchname').val('');
652
                    }
653
                },
654
                barcode: {
655
                    listener: function() {
656
                        var me = 'barcode';
657
                        $('#illfilter_barcode').change(function() {
658
                            var val = $('#illfilter_barcode').val();
659
                            if (val && val.length > 0) {
660
                                activeFilters[me] = function() {
661
                                    table.api().column(11).search(val);
662
                                }
663
                            } else {
664
                                if (activeFilters.hasOwnProperty(me)) {
665
                                    delete activeFilters[me];
666
                                }
667
                            }
668
                        });
669
                    },
670
                    clear: function() {
671
                        $('#illfilter_barcode').val('');
672
                    }
673
                },
674
                dateModified: {
675
                    clear: function() {
676
                        $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
677
                    }
678
                },
679
                datePlaced: {
680
                    clear: function() {
681
                        $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
682
                    }
683
                }
684
            };
685
686
            // Expand any fields we're expanding
687
            var expandExpand = function(row) {
688
                expand.forEach(function(thisExpand) {
689
                    if (row.hasOwnProperty(thisExpand)) {
690
                        if (!expanded.hasOwnProperty(thisExpand)) {
691
                            expanded[thisExpand] = [];
692
                        }
693
                        var expandObj = row[thisExpand];
694
                        Object.keys(expandObj).forEach(
695
                            function(thisExpandCol) {
696
                                var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
697
                                // Keep a list of fields that have been expanded
698
                                // so we can create toggle links for them
699
                                if (expanded[thisExpand].indexOf(expColName) == -1) {
700
                                    expanded[thisExpand].push(expColName);
701
                                }
702
                                expandObj[expColName] =
703
                                    expandObj[thisExpandCol];
704
                                delete expandObj[thisExpandCol];
705
                            }
706
                        );
707
                        $.extend(true, row, expandObj);
708
                        delete row[thisExpand];
709
                    }
710
                });
711
            };
712
713
            // Strip the expand prefix if it exists, we do this for display
714
            var stripPrefix = function(value) {
715
                expand.forEach(function(thisExpand) {
716
                    var regex = new RegExp(thisExpand + '_', 'g');
717
                    value = value.replace(regex, '');
718
                });
719
                return value;
720
            };
721
722
            // Our 'render' function for borrowerlink
723
            var createPatronLink = function(data, type, row) {
724
                var patronLink = '<a title="' + _("View borrower details") + '" ' +
725
                    'href="/cgi-bin/koha/members/moremember.pl?' +
726
                    'borrowernumber='+row.borrowernumber+'">';
727
                if ( row.patron_firstname ) {
728
                    patronLink = patronLink + row.patron_firstname + ' ';
729
                }
730
                patronLink = patronLink + row.patron_surname + '</a>';
731
                return patronLink;
732
            };
733
734
            // Our 'render' function for biblio_id
735
            var createBiblioLink = function(data, type, row) {
736
                return (row.biblio_id) ?
737
                    '<a title="' + _("View biblio details") + '" ' +
738
                    'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
739
                    row.biblio_id + '">' +
740
                    row.biblio_id +
741
                    '</a>' : '';
742
            };
743
744
            // Render function for request ID
745
            var createRequestId = function(data, type, row) {
746
                return row.id_prefix + row.illrequest_id;
747
            };
748
749
            // Render function for type
750
            var createType = function(data, type, row) {
751
                if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
752
                    if (row.hasOwnProperty('medium') && row.medium) {
753
                        row.metadata_Type = row.medium;
754
                    } else {
755
                        row.metadata_Type = null;
756
                    }
757
                }
758
                return row.metadata_Type;
759
            };
760
761
            // Render function for request status
762
            var createStatus = function(data, type, row, meta) {
763
                if (row.status_alias) {
764
                    return row.status_alias.lib
765
                        ? row.status_alias.lib
766
                        : row.status_alias.authorised_value;
767
                } else {
768
                    var origData = meta.settings.oInit.originalData;
769
                    if (origData.length > 0) {
770
                        var status_name = meta.settings.oInit.originalData[0].capabilities[
771
                            row.status
772
                        ].name;
773
                        return getStatusName(status_name, row);
774
                    } else {
775
                        return '';
776
                    }
777
                }
778
            };
779
780
            var getStatusName = function(origName, row) {
781
                switch( origName ) {
782
                    case "New request":
783
                        return _("New request");
784
                    case "Requested":
785
                        return _("Requested");
786
                    case "Requested from partners":
787
                        var statStr = _("Requested from partners");
788
                        if (
789
                            row.hasOwnProperty('requested_partners') &&
790
                            row.requested_partners &&
791
                            row.requested_partners.length > 0
792
                        ) {
793
                            statStr += ' (' + row.requested_partners + ')';
794
                        }
795
                        return statStr;
796
                    case "Request reverted":
797
                        return _("Request reverted");
798
                    case "Queued request":
799
                        return _("Queued request");
800
                    case "Cancellation requested":
801
                        return _("Cancellation requested");
802
                    case "Completed":
803
                        return _("Completed");
804
                    case "Delete request":
805
                        return _("Delete request");
806
                    default:
807
                        return origName;
808
                }
809
            };
810
811
            // Render function for creating a row's action link
812
            var createActionLink = function(data, type, row) {
813
                return '<a class="btn btn-default btn-sm" ' +
814
                    'href="/cgi-bin/koha/ill/ill-requests.pl?' +
815
                    'method=illview&amp;illrequest_id=' +
816
                    row.illrequest_id +
817
                    '">' + _("Manage request") + '</a>';
818
            };
819
820
            // Columns that require special treatment
821
            var specialCols = {
822
                action: {
823
                    func: createActionLink
824
                },
825
                borrowernumber: {
826
                    func: createPatronLink
827
                },
828
                illrequest_id: {
829
                    func: createRequestId
830
                },
831
                status: {
832
                    func: createStatus
833
                },
834
                biblio_id: {
835
                    name: _("Bibliograpic record ID"),
836
                    func: createBiblioLink
837
                },
838
                metadata_Type: {
839
                    func: createType
840
                },
841
                updated: {
842
                    name: _("Updated on"),
843
                },
844
                patron_cardnumber: {
845
                    name: _("Patron barcode")
846
                }
847
            };
848
849
            // Display the modal containing request supplier metadata
850
            $('#ill-request-display-log').on('click', function(e) {
851
                e.preventDefault();
852
                $('#requestLog').modal({show:true});
853
            });
854
855
            // Toggle request attributes in Illview
856
            $('#toggle_requestattributes').on('click', function(e) {
857
                e.preventDefault();
858
                $('#requestattributes').toggleClass('content_hidden');
859
            });
860
861
            // Toggle new comment form in Illview
862
            $('#toggle_addcomment').on('click', function(e) {
863
                e.preventDefault();
864
                $('#addcomment').toggleClass('content_hidden');
865
            });
866
867
            // Filter partner list
868
            $('#partner_filter').keyup(function() {
869
                var needle = $('#partner_filter').val();
870
                $('#partners > option').each(function() {
871
                    var regex = new RegExp(needle, 'i');
872
                    if (
873
                        needle.length == 0 ||
874
                        $(this).is(':selected') ||
875
                        $(this).text().match(regex)
876
                    ) {
877
                        $(this).show();
878
                    } else {
879
                        $(this).hide();
880
                    }
881
                });
882
            });
883
884
            // Display the modal containing request supplier metadata
885
            $('#ill-request-display-metadata').on('click', function(e) {
886
                e.preventDefault();
887
                $('#dataPreview').modal({show:true});
888
            });
889
890
            // Get our data from the API and process it prior to passing
891
            // it to datatables
892
            var ajax = $.ajax(
893
                '/api/v1/illrequests?embed=metadata,patron,capabilities,library,comments,status_alias,requested_partners'
894
                ).done(function() {
895
                    var data = JSON.parse(ajax.responseText);
896
                    // Make a copy, we'll be removing columns next and need
897
                    // to be able to refer to data that has been removed
898
                    var dataCopy = $.extend(true, [], data);
899
                    // Expand columns that need it and create an array
900
                    // of all column names
901
                    $.each(dataCopy, function(k, row) {
902
                        expandExpand(row);
903
                    });
904
905
                    // Assemble an array of column definitions for passing
906
                    // to datatables
907
                    var colData = [];
908
                    columns_settings.forEach(function(thisCol) {
909
                        var colName = thisCol.columnname;
910
                        // Create the base column object
911
                        var colObj = $.extend({}, thisCol);
912
                        colObj.name = colName;
913
                        colObj.className = colName;
914
                        colObj.defaultContent = '';
915
916
                        // We may need to process the data going in this
917
                        // column, so do it if necessary
918
                        if (
919
                            specialCols.hasOwnProperty(colName) &&
920
                            specialCols[colName].hasOwnProperty('func')
921
                        ) {
922
                            colObj.render = specialCols[colName].func;
923
                        } else {
924
                            colObj.data = colName;
925
                        }
926
                        // Make sure properties that aren't present in the API
927
                        // response are populated with null to avoid Datatables
928
                        // choking on their absence
929
                        dataCopy.forEach(function(thisData) {
930
                            if (!thisData.hasOwnProperty(colName)) {
931
                                thisData[colName] = null;
932
                            }
933
                        });
934
                        colData.push(colObj);
935
                    });
936
937
                    // Initialise the datatable
938
                    table = KohaTable("ill-requests", {})
939
                    var illTable = KohaTable("ill-requests", {
940
                        'aoColumnDefs': [
941
                            { // Last column shouldn't be sortable or searchable
942
                                'aTargets': [ 'actions' ],
943
                                'bSortable': false,
944
                                'bSearchable': false
945
                            },
946
                            { // When sorting 'placed', we want to use the
947
                              // unformatted column
948
                              'aTargets': [ 'placed_formatted'],
949
                              'iDataSort': 16
950
                            },
951
                            { // When sorting 'updated', we want to use the
952
                              // unformatted column
953
                              'aTargets': [ 'updated_formatted'],
954
                              'iDataSort': 18
955
                            },
956
                            {
957
                              'aTargets': [ 'patron_cardnumber' ],
958
                              'bVisible': false,
959
                              'bSearchable': true
960
                            }
961
                        ],
962
                        'aaSorting': [[ 18, 'desc' ]], // Default sort, updated descending
963
                        'processing': true, // Display a message when manipulating
964
                        'sPaginationType': "full_numbers", // Pagination display
965
                        'deferRender': true, // Improve performance on big datasets
966
                        'data': dataCopy,
967
                        'columns': colData,
968
                        'originalData': data, // Enable render functions to access
969
                                              // our original data
970
                        'initComplete': function() {
971
972
                            // Prepare any filter elements that need it
973
                            for (var el in filterable) {
974
                                if (filterable.hasOwnProperty(el)) {
975
                                    if (filterable[el].hasOwnProperty('prep')) {
976
                                        filterable[el].prep(dataCopy, data);
977
                                    }
978
                                    if (filterable[el].hasOwnProperty('listener')) {
979
                                        filterable[el].listener();
980
                                    }
981
                                }
982
                            }
983
984
                        }
985
                    }, columns_settings);
986
987
                    // Custom date range filtering
988
                    $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
989
                        var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
990
                        var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
991
                        var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
992
                        var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
993
                        var rowPlaced = data[16] ? new Date(data[16]) : null;
994
                        var rowModified = data[18] ? new Date(data[18]) : null;
995
                        var placedPassed = true;
996
                        var modifiedPassed = true;
997
                        if (placedStart && rowPlaced && rowPlaced < placedStart) {
998
                            placedPassed = false
999
                        };
1000
                        if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
1001
                            placedPassed = false;
1002
                        }
1003
                        if (modifiedStart && rowModified && rowModified < modifiedStart) {
1004
                            modifiedPassed = false
1005
                        };
1006
                        if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
1007
                            modifiedPassed = false;
1008
                        }
1009
1010
                        return placedPassed && modifiedPassed;
1011
1012
                    });
1013
1014
                }
1015
            );
1016
1017
            var clearSearch = function() {
1018
                table.api().search('').columns().search('');
1019
                activeFilters = {};
1020
                for (var filter in filterable) {
1021
                    if (
1022
                        filterable.hasOwnProperty(filter) &&
1023
                        filterable[filter].hasOwnProperty('clear')
1024
                    ) {
1025
                        filterable[filter].clear();
1026
                    }
1027
                }
1028
                table.api().draw();
1029
            };
1030
1031
            // Apply any search filters, or clear any previous
1032
            // ones
1033
            $('#illfilter_form').submit(function(event) {
1034
                event.preventDefault();
1035
                table.search('').columns().search('');
1036
                for (var active in activeFilters) {
1037
                    if (activeFilters.hasOwnProperty(active)) {
1038
                        activeFilters[active]();
1039
                    }
1040
                }
1041
                table.api().draw();
1042
            });
1043
1044
            // Clear all filters
1045
            $('#clear_search').click(function() {
1046
                clearSearch();
1047
            });
1048
1049
        });
1050
    </script>
531
    </script>
532
    [% Asset.js("js/ill-list-table.js") | $raw %]
533
[% END %]
534
535
[% TRY %]
536
[% PROCESS backend_jsinclude %]
537
[% CATCH %]
1051
[% END %]
538
[% END %]
1052
539
1053
[% INCLUDE 'intranet-bottom.inc' %]
540
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt (+63 lines)
Line 0 Link Here
1
[% USE raw %]
2
[% USE Asset %]
3
[% USE Branches %]
4
[% USE Koha %]
5
[% USE KohaDates %]
6
[% SET footerjs = 1 %]
7
[% USE AuthorisedValues %]
8
[% USE ColumnsSettings %]
9
10
[% INCLUDE 'doc-head-open.inc' %]
11
<title>Koha &rsaquo; Patrons &rsaquo; ILL requests for [% INCLUDE 'patron-title.inc' no_html = 1 %]</title>
12
[% INCLUDE 'doc-head-close.inc' %]
13
[% Asset.css("css/datatables.css") %]
14
</head>
15
16
<body id="pat_ill_requests">
17
    [% INCLUDE 'header.inc' %]
18
    [% INCLUDE 'patron-search.inc' %]
19
20
    <div id="breadcrumbs">
21
        <a href="/cgi-bin/koha/mainpage.pl">Home</a>
22
        &rsaquo; <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a>
23
        &rsaquo; ILL requests for [% INCLUDE 'patron-title.inc' %]
24
    </div>
25
26
    <div id="doc3" class="yui-t1">
27
28
    <div id="bd">
29
        <div id="yui-main">
30
            <div class="yui-b">
31
                <div class="yui-g">
32
                    <h2>ILL requests</h2>
33
                    [% INCLUDE 'ill-list-table.inc' %]
34
                </div>
35
            </div>
36
        </div>
37
        <div class="yui-b">
38
            [% INCLUDE 'circ-menu.inc' %]
39
        </div>
40
    </div>
41
42
    [% MACRO jsinclude BLOCK %]
43
        [% INCLUDE 'datatables.inc' %]
44
        [% INCLUDE 'columns_settings.inc' %]
45
        [% INCLUDE 'calendar.inc' %]
46
        [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
47
        <script>
48
            // Date format for datepickers
49
            var dateMap = {
50
                dmydot: 'dd.mm.yy',
51
                iso: 'yy-mm-dd',
52
                metric: 'dd/mm/yy',
53
                us: 'mm/dd/yy'
54
            };
55
            var dateFormat = dateMap['[% Koha.Preference('dateformat') %]'];
56
            var prefilters = '[% prefilters %]';
57
            // Set column settings
58
            var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
59
        </script>
60
        [% Asset.js("js/ill-list-table.js") | $raw %]
61
    [% END %]
62
63
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js (+488 lines)
Line 0 Link Here
1
$(document).ready(function() {
2
3
    // Illview Datatable setup
4
5
    var table;
6
7
    // Filters that are active
8
    var activeFilters = {};
9
10
    $('#illfilter_dateplaced_start, #illfilter_dateplaced_end, #illfilter_datemodified_start, #illfilter_datemodified_end').datepicker(
11
        'option', 'dateFormat', dateFormat
12
    );
13
14
    // Get any prefilters
15
    var prefilters = $('table#ill-requests').data('prefilters');
16
17
    // Fields we need to expand (flatten)
18
    var expand = [
19
        'metadata',
20
        'patron',
21
        'library'
22
    ];
23
24
    // Expanded fields
25
    // This is auto populated
26
    var expanded = {};
27
28
    // Filterable columns
29
    var filterable = {
30
        status: {
31
            prep: function(tableData, oData) {
32
                var uniques = {};
33
                tableData.forEach(function(row) {
34
                    var resolvedName = getStatusName(
35
                        oData[0].capabilities[row.status].name,
36
                        row
37
                    );
38
                    uniques[resolvedName] = 1
39
                });
40
                Object.keys(uniques).sort().forEach(function(unique) {
41
                    $('#illfilter_status').append(
42
                        '<option value="' + unique  +
43
                        '">' + unique +  '</option>'
44
                    );
45
                });
46
            },
47
            listener: function() {
48
                var me = 'status';
49
                $('#illfilter_status').change(function() {
50
                    var sel = $('#illfilter_status option:selected').val();
51
                    if (sel && sel.length > 0) {
52
                        activeFilters[me] = function() {
53
                            table.api().column(15).search(sel);
54
                        }
55
                    } else {
56
                        if (activeFilters.hasOwnProperty(me)) {
57
                            delete activeFilters[me];
58
                        }
59
                    }
60
                });
61
            },
62
            clear: function() {
63
                $('#illfilter_status').val('');
64
            }
65
        },
66
        pickupBranch: {
67
            prep: function(tableData, oData) {
68
                var uniques = {};
69
                tableData.forEach(function(row) {
70
                    uniques[row.library_branchname] = 1
71
                });
72
                Object.keys(uniques).sort().forEach(function(unique) {
73
                    $('#illfilter_branchname').append(
74
                        '<option value="' + unique  +
75
                        '">' + unique +  '</option>'
76
                    );
77
                });
78
            },
79
            listener: function() {
80
                var me = 'pickupBranch';
81
                $('#illfilter_branchname').change(function() {
82
                    var sel = $('#illfilter_branchname option:selected').val();
83
                    if (sel && sel.length > 0) {
84
                        activeFilters[me] = function() {
85
                            table.api().column(14).search(sel);
86
                        }
87
                    } else {
88
                        if (activeFilters.hasOwnProperty(me)) {
89
                            delete activeFilters[me];
90
                        }
91
                    }
92
                });
93
            },
94
            clear: function() {
95
                $('#illfilter_branchname').val('');
96
            }
97
        },
98
        barcode: {
99
            listener: function() {
100
                var me = 'barcode';
101
                $('#illfilter_barcode').change(function() {
102
                    var val = $('#illfilter_barcode').val();
103
                    if (val && val.length > 0) {
104
                        activeFilters[me] = function() {
105
                            table.api().column(11).search(val);
106
                        }
107
                    } else {
108
                        if (activeFilters.hasOwnProperty(me)) {
109
                            delete activeFilters[me];
110
                        }
111
                    }
112
                });
113
            },
114
            clear: function() {
115
                $('#illfilter_barcode').val('');
116
            }
117
        },
118
        dateModified: {
119
            clear: function() {
120
                $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
121
            }
122
        },
123
        datePlaced: {
124
            clear: function() {
125
                $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
126
            }
127
        }
128
    };
129
130
    // Expand any fields we're expanding
131
    var expandExpand = function(row) {
132
        expand.forEach(function(thisExpand) {
133
            if (row.hasOwnProperty(thisExpand)) {
134
                if (!expanded.hasOwnProperty(thisExpand)) {
135
                    expanded[thisExpand] = [];
136
                }
137
                var expandObj = row[thisExpand];
138
                Object.keys(expandObj).forEach(
139
                    function(thisExpandCol) {
140
                        var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
141
                        // Keep a list of fields that have been expanded
142
                        // so we can create toggle links for them
143
                        if (expanded[thisExpand].indexOf(expColName) == -1) {
144
                            expanded[thisExpand].push(expColName);
145
                        }
146
                        expandObj[expColName] =
147
                            expandObj[thisExpandCol];
148
                        delete expandObj[thisExpandCol];
149
                    }
150
                );
151
                $.extend(true, row, expandObj);
152
                delete row[thisExpand];
153
            }
154
        });
155
    };
156
157
    // Strip the expand prefix if it exists, we do this for display
158
    var stripPrefix = function(value) {
159
        expand.forEach(function(thisExpand) {
160
            var regex = new RegExp(thisExpand + '_', 'g');
161
            value = value.replace(regex, '');
162
        });
163
        return value;
164
    };
165
166
    // Our 'render' function for borrowerlink
167
    var createPatronLink = function(data, type, row) {
168
        var patronLink = '<a title="' + _("View borrower details") + '" ' +
169
            'href="/cgi-bin/koha/members/moremember.pl?' +
170
            'borrowernumber='+row.borrowernumber+'">';
171
        if ( row.patron_firstname ) {
172
            patronLink = patronLink + row.patron_firstname + ' ';
173
        }
174
        patronLink = patronLink + row.patron_surname + '</a>';
175
        return patronLink;
176
    };
177
178
    // Our 'render' function for biblio_id
179
    var createBiblioLink = function(data, type, row) {
180
        return (row.biblio_id) ?
181
            '<a title="' + _("View biblio details") + '" ' +
182
            'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
183
            row.biblio_id + '">' +
184
            row.biblio_id +
185
            '</a>' : '';
186
    };
187
188
    // Render function for request ID
189
    var createRequestId = function(data, type, row) {
190
        return row.id_prefix + row.illrequest_id;
191
    };
192
193
    // Render function for type
194
    var createType = function(data, type, row) {
195
        if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
196
            if (row.hasOwnProperty('medium') && row.medium) {
197
                row.metadata_Type = row.medium;
198
            } else {
199
                row.metadata_Type = null;
200
            }
201
        }
202
        return row.metadata_Type;
203
    };
204
205
    // Render function for request status
206
    var createStatus = function(data, type, row, meta) {
207
        if (row.status_alias) {
208
            return row.status_alias.lib
209
                ? row.status_alias.lib
210
                : row.status_alias.authorised_value;
211
        } else {
212
            var origData = meta.settings.oInit.originalData;
213
            if (origData.length > 0) {
214
                var status_name = meta.settings.oInit.originalData[0].capabilities[
215
                    row.status
216
                ].name;
217
                return getStatusName(status_name, row);
218
            } else {
219
                return '';
220
            }
221
        }
222
    };
223
224
    var getStatusName = function(origName, row) {
225
        switch( origName ) {
226
            case "New request":
227
                return _("New request");
228
            case "Requested":
229
                return _("Requested");
230
            case "Requested from partners":
231
                var statStr = _("Requested from partners");
232
                if (
233
                    row.hasOwnProperty('requested_partners') &&
234
                    row.requested_partners &&
235
                    row.requested_partners.length > 0
236
                ) {
237
                    statStr += ' (' + row.requested_partners + ')';
238
                }
239
                return statStr;
240
            case "Request reverted":
241
                return _("Request reverted");
242
            case "Queued request":
243
                return _("Queued request");
244
            case "Cancellation requested":
245
                return _("Cancellation requested");
246
            case "Completed":
247
                return _("Completed");
248
            case "Delete request":
249
                return _("Delete request");
250
            default:
251
                return origName;
252
        }
253
    };
254
255
    // Render function for creating a row's action link
256
    var createActionLink = function(data, type, row) {
257
        return '<a class="btn btn-default btn-sm" ' +
258
            'href="/cgi-bin/koha/ill/ill-requests.pl?' +
259
            'method=illview&amp;illrequest_id=' +
260
            row.illrequest_id +
261
            '">' + _("Manage request") + '</a>';
262
    };
263
264
    // Columns that require special treatment
265
    var specialCols = {
266
        action: {
267
            func: createActionLink
268
        },
269
        patron: {
270
            func: createPatronLink
271
        },
272
        illrequest_id: {
273
            func: createRequestId
274
        },
275
        status: {
276
            func: createStatus
277
        },
278
        biblio_id: {
279
            name: _("Bibliograpic record ID"),
280
            func: createBiblioLink
281
        },
282
        metadata_Type: {
283
            func: createType
284
        },
285
        updated: {
286
            name: _("Updated on"),
287
        },
288
        patron_cardnumber: {
289
            name: _("Patron barcode")
290
        }
291
    };
292
293
    // Display the modal containing request supplier metadata
294
    $('#ill-request-display-log').on('click', function(e) {
295
        e.preventDefault();
296
        $('#requestLog').modal({show:true});
297
    });
298
299
    // Toggle request attributes in Illview
300
    $('#toggle_requestattributes').on('click', function(e) {
301
        e.preventDefault();
302
        $('#requestattributes').toggleClass('content_hidden');
303
    });
304
305
    // Toggle new comment form in Illview
306
    $('#toggle_addcomment').on('click', function(e) {
307
        e.preventDefault();
308
        $('#addcomment').toggleClass('content_hidden');
309
    });
310
311
    // Filter partner list
312
    $('#partner_filter').keyup(function() {
313
        var needle = $('#partner_filter').val();
314
        $('#partners > option').each(function() {
315
            var regex = new RegExp(needle, 'i');
316
            if (
317
                needle.length == 0 ||
318
                $(this).is(':selected') ||
319
                $(this).text().match(regex)
320
            ) {
321
                $(this).show();
322
            } else {
323
                $(this).hide();
324
            }
325
        });
326
    });
327
328
    // Display the modal containing request supplier metadata
329
    $('#ill-request-display-metadata').on('click', function(e) {
330
        e.preventDefault();
331
        $('#dataPreview').modal({show:true});
332
    });
333
334
    // Get our data from the API and process it prior to passing
335
    // it to datatables
336
    var filterParam = prefilters ? '&' + prefilters : '';
337
    var ajax = $.ajax(
338
        '/api/v1/illrequests?embed=metadata,patron,capabilities,library,comments,status_alias,requested_partners'
339
        + filterParam
340
        ).done(function() {
341
            var data = JSON.parse(ajax.responseText);
342
            // Make a copy, we'll be removing columns next and need
343
            // to be able to refer to data that has been removed
344
            var dataCopy = $.extend(true, [], data);
345
            // Expand columns that need it and create an array
346
            // of all column names
347
            $.each(dataCopy, function(k, row) {
348
                expandExpand(row);
349
            });
350
351
            // Assemble an array of column definitions for passing
352
            // to datatables
353
            var colData = [];
354
            columns_settings.forEach(function(thisCol) {
355
                var colName = thisCol.columnname;
356
                // Create the base column object
357
                var colObj = $.extend({}, thisCol);
358
                colObj.name = colName;
359
                colObj.className = colName;
360
                colObj.defaultContent = '';
361
362
                // We may need to process the data going in this
363
                // column, so do it if necessary
364
                if (
365
                    specialCols.hasOwnProperty(colName) &&
366
                    specialCols[colName].hasOwnProperty('func')
367
                ) {
368
                    colObj.render = specialCols[colName].func;
369
                } else {
370
                    colObj.data = colName;
371
                }
372
                // Make sure properties that aren't present in the API
373
                // response are populated with null to avoid Datatables
374
                // choking on their absence
375
                dataCopy.forEach(function(thisData) {
376
                    if (!thisData.hasOwnProperty(colName)) {
377
                        thisData[colName] = null;
378
                    }
379
                });
380
                colData.push(colObj);
381
            });
382
383
            // Initialise the datatable
384
            table = KohaTable("ill-requests", {
385
                'aoColumnDefs': [
386
                    { // Last column shouldn't be sortable or searchable
387
                        'aTargets': [ 'actions' ],
388
                        'bSortable': false,
389
                        'bSearchable': false
390
                    },
391
                    { // When sorting 'placed', we want to use the
392
                        // unformatted column
393
                        'aTargets': [ 'placed_formatted'],
394
                        'iDataSort': 16
395
                    },
396
                    { // When sorting 'updated', we want to use the
397
                        // unformatted column
398
                        'aTargets': [ 'updated_formatted'],
399
                        'iDataSort': 18
400
                    }
401
                ],
402
                'aaSorting': [[ 18, 'desc' ]], // Default sort, updated descending
403
                'processing': true, // Display a message when manipulating
404
                'sPaginationType': "full_numbers", // Pagination display
405
                'deferRender': true, // Improve performance on big datasets
406
                'data': dataCopy,
407
                'columns': colData,
408
                'originalData': data, // Enable render functions to access
409
                                        // our original data
410
                'initComplete': function() {
411
                    // Prepare any filter elements that need it
412
                    for (var el in filterable) {
413
                        if (filterable.hasOwnProperty(el)) {
414
                            if (filterable[el].hasOwnProperty('prep')) {
415
                                filterable[el].prep(dataCopy, data);
416
                            }
417
                            if (filterable[el].hasOwnProperty('listener')) {
418
                                filterable[el].listener();
419
                            }
420
                        }
421
                    }
422
423
                }
424
            }, columns_settings);
425
426
            // Custom date range filtering
427
            $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
428
                var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
429
                var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
430
                var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
431
                var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
432
                var rowPlaced = data[16] ? new Date(data[16]) : null;
433
                var rowModified = data[18] ? new Date(data[18]) : null;
434
                var placedPassed = true;
435
                var modifiedPassed = true;
436
                if (placedStart && rowPlaced && rowPlaced < placedStart) {
437
                    placedPassed = false
438
                };
439
                if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
440
                    placedPassed = false;
441
                }
442
                if (modifiedStart && rowModified && rowModified < modifiedStart) {
443
                    modifiedPassed = false
444
                };
445
                if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
446
                    modifiedPassed = false;
447
                }
448
449
                return placedPassed && modifiedPassed;
450
451
            });
452
453
        }
454
    );
455
456
    var clearSearch = function() {
457
        table.api().search('').columns().search('');
458
        activeFilters = {};
459
        for (var filter in filterable) {
460
            if (
461
                filterable.hasOwnProperty(filter) &&
462
                filterable[filter].hasOwnProperty('clear')
463
            ) {
464
                filterable[filter].clear();
465
            }
466
        }
467
        table.api().draw();
468
    };
469
470
    // Apply any search filters, or clear any previous
471
    // ones
472
    $('#illfilter_form').submit(function(event) {
473
        event.preventDefault();
474
        table.api().search('').columns().search('');
475
        for (var active in activeFilters) {
476
            if (activeFilters.hasOwnProperty(active)) {
477
                activeFilters[active]();
478
            }
479
        }
480
        table.api().draw();
481
    });
482
483
    // Clear all filters
484
    $('#clear_search').click(function() {
485
        clearSearch();
486
    });
487
488
});
(-)a/members/ill-requests.pl (-1 / +55 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Copyright 2018 PTFS Europe
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use CGI qw ( -utf8 );
23
use C4::Auth;
24
use C4::Output;
25
use C4::Members;
26
use C4::Members::Attributes qw(GetBorrowerAttributes);
27
use Koha::Patrons;
28
29
my $input = new CGI;
30
31
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
32
    {
33
		template_name   => "members/ill-requests.tt",
34
        query           => $input,
35
        type            => "intranet",
36
        authnotrequired => 0,
37
        flagsrequired   => { borrowers => 'edit_borrowers' },
38
        debug           => 1,
39
    }
40
);
41
42
my $borrowernumber = $input->param('borrowernumber');
43
44
my $logged_in_user = Koha::Patrons->find( $loggedinuser ) or die "Not logged in";
45
my $patron         = Koha::Patrons->find( $borrowernumber );
46
output_and_exit_if_error( $input, $cookie, $template, { module => 'members', logged_in_user => $logged_in_user, current_patron => $patron } );
47
48
my $category = $patron->category;
49
$template->param(
50
    prefilters => "borrowernumber=$borrowernumber",
51
    patron => $patron,
52
    illview  => 1,
53
);
54
55
output_html_with_http_headers $input, $cookie, $template->output;

Return to bug 18589