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

(-)a/Koha/REST/V1/Illrequests.pm (-1 / +10 lines)
Lines 55-62 sub list { Link Here
55
        delete $args->{embed};
55
        delete $args->{embed};
56
    }
56
    }
57
57
58
58
    # Get all requests
59
    # Get all requests
59
    my @requests = Koha::Illrequests->as_list;
60
    # If necessary, only get those from a specified patron
61
    my @requests;
62
    if ($args->{borrowernumber}) {
63
		@requests = Koha::Illrequests->search(
64
			{ borrowernumber => $args->{borrowernumber} }
65
		);
66
    } else {
67
        @requests = Koha::Illrequests->as_list;
68
    }
60
69
61
    # Identify patrons & branches that
70
    # Identify patrons & branches that
62
    # we're going to need and get them
71
    # we're going to need and get them
(-)a/ill/ill-requests.pl (-3 / +2 lines)
Lines 277-289 if ( $backends_available ) { Link Here
277
        my $active_filters = [];
277
        my $active_filters = [];
278
        foreach my $filter(@{$possible_filters}) {
278
        foreach my $filter(@{$possible_filters}) {
279
            if ($params->{$filter}) {
279
            if ($params->{$filter}) {
280
                push @{$active_filters},
280
                push @{$active_filters}, "$filter=$params->{$filter}";
281
                    { name => $filter, value => $params->{$filter}};
282
            }
281
            }
283
        }
282
        }
284
        if (scalar @{$active_filters} > 0) {
283
        if (scalar @{$active_filters} > 0) {
285
            $template->param(
284
            $template->param(
286
                prefilters => $active_filters
285
                prefilters => join(",", @{$active_filters})
287
            );
286
            );
288
        }
287
        }
289
288
(-)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 (-523 / +18 lines)
Lines 494-536 Link Here
494
                    <h1>View ILL requests</h1>
494
                    <h1>View ILL requests</h1>
495
                    <div id="results">
495
                    <div id="results">
496
                        <h3>Details for all requests</h3>
496
                        <h3>Details for all requests</h3>
497
497
						[% INCLUDE 'ill-list-table.inc' %]
498
                        <table id="ill-requests">
499
                            <thead>
500
                                <tr id="illview-header">
501
                                    <th scope="col">Request ID</th>
502
                                    <th scope="col">Author</th>
503
                                    <th scope="col">Title</th>
504
                                    <th scope="col">Article title</th>
505
                                    <th scope="col">Issue</th>
506
                                    <th scope="col">Volume</th>
507
                                    <th scope="col">Year</th>
508
                                    <th scope="col">Pages</th>
509
                                    <th scope="col">Type</th>
510
                                    <th scope="col">Order ID</th>
511
                                    <th scope="col">Patron ID</th>
512
                                    <th scope="col" class="patron_cardnumber">Patron barcode</th>
513
                                    <th scope="col">Patron</th>
514
                                    <th scope="col">Biblio ID</th>
515
                                    <th scope="col">Branch</th>
516
                                    <th scope="col">Status</th>
517
                                    <th scope="col" class="placed">&nbsp;</th>
518
                                    <th scope="col" class="placed_formatted">Placed on</th>
519
                                    <th scope="col" class="updated">&nbsp;</th>
520
                                    <th scope="col" class="updated_formatted">Updated on</th>
521
                                    <th scope="col">Replied</th>
522
                                    <th scope="col">Completed on</th>
523
                                    <th scope="col">Access URL</th>
524
                                    <th scope="col">Cost</th>
525
                                    <th scope="col">OPAC notes</th>
526
                                    <th scope="col">Staff notes</th>
527
                                    <th scope="col">Backend</th>
528
                                    <th scope="col" class="actions"></th>
529
                                </tr>
530
                            </thead>
531
                            <tbody id="illview-body">
532
                            </tbody>
533
                        </table>
534
                    </div>
498
                    </div>
535
                [% ELSE %]
499
                [% ELSE %]
536
                <!-- Custom Backend Action -->
500
                <!-- Custom Backend Action -->
Lines 543-1041 Link Here
543
    </div>
507
    </div>
544
</div>
508
</div>
545
509
546
[% TRY %]
547
[% PROCESS backend_jsinclude %]
548
[% CATCH %]
549
[% END %]
550
551
[% MACRO jsinclude BLOCK %]
510
[% MACRO jsinclude BLOCK %]
552
    [% INCLUDE 'datatables.inc' %]
511
    [% INCLUDE 'datatables.inc' %]
553
    [% INCLUDE 'columns_settings.inc' %]
512
    [% INCLUDE 'columns_settings.inc' %]
554
    [% INCLUDE 'calendar.inc' %]
513
    [% INCLUDE 'calendar.inc' %]
555
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
514
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
556
    <script>
515
    <script>
557
        $(document).ready(function() {
516
        // Date format for datepickers
558
517
        var dateMap = {
559
            // Illview Datatable setup
518
            dmydot: 'dd.mm.yy',
560
519
            iso: 'yy-mm-dd',
561
            var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
520
            metric: 'dd/mm/yy',
562
521
            us: 'mm/dd/yy'
563
            var table;
522
        };
564
523
        var dateFormat = dateMap['[% Koha.Preference('dateformat') %]'];
565
            // Filters that are active
524
        var prefilters = '[% prefilters %]';
566
            var activeFilters = {};
525
        // Set column settings
567
526
        var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
568
            // Fields we need to expand (flatten)
569
            var expand = [
570
                'metadata',
571
                'patron',
572
                'library'
573
            ];
574
575
            // Expanded fields
576
            // This is auto populated
577
            var expanded = {};
578
579
            // Filterable columns
580
            var filterable = {
581
                status: {
582
                    prep: function(tableData, oData) {
583
                        var uniques = {};
584
                        tableData.forEach(function(row) {
585
                            var resolvedName = getStatusName(
586
                                oData[0].capabilities[row.status].name,
587
                                row
588
                            );
589
                            uniques[resolvedName] = 1
590
                        });
591
                        Object.keys(uniques).sort().forEach(function(unique) {
592
                            $('#illfilter_status').append(
593
                                '<option value="' + unique  +
594
                                '">' + unique +  '</option>'
595
                            );
596
                        });
597
                    },
598
                    listener: function() {
599
                        var me = 'status';
600
                        $('#illfilter_status').change(function() {
601
                            var sel = $('#illfilter_status option:selected').val();
602
                            if (sel && sel.length > 0) {
603
                                activeFilters[me] = function() {
604
                                    table.api().column(15).search(sel);
605
                                }
606
                            } else {
607
                                if (activeFilters.hasOwnProperty(me)) {
608
                                    delete activeFilters[me];
609
                                }
610
                            }
611
                        });
612
                    },
613
                    clear: function() {
614
                        $('#illfilter_status').val('');
615
                    }
616
                },
617
                pickupBranch: {
618
                    prep: function(tableData, oData) {
619
                        var uniques = {};
620
                        tableData.forEach(function(row) {
621
                            uniques[row.library_branchname] = 1
622
                        });
623
                        Object.keys(uniques).sort().forEach(function(unique) {
624
                            $('#illfilter_branchname').append(
625
                                '<option value="' + unique  +
626
                                '">' + unique +  '</option>'
627
                            );
628
                        });
629
                    },
630
                    listener: function() {
631
                        var me = 'pickupBranch';
632
                        $('#illfilter_branchname').change(function() {
633
                            var sel = $('#illfilter_branchname option:selected').val();
634
                            if (sel && sel.length > 0) {
635
                                activeFilters[me] = function() {
636
                                    table.api().column(14).search(sel);
637
                                }
638
                            } else {
639
                                if (activeFilters.hasOwnProperty(me)) {
640
                                    delete activeFilters[me];
641
                                }
642
                            }
643
                        });
644
                    },
645
                    clear: function() {
646
                        $('#illfilter_branchname').val('');
647
                    }
648
                },
649
                barcode: {
650
                    listener: function() {
651
                        var me = 'barcode';
652
                        $('#illfilter_barcode').change(function() {
653
                            var val = $('#illfilter_barcode').val();
654
                            if (val && val.length > 0) {
655
                                activeFilters[me] = function() {
656
                                    table.api().column(11).search(val);
657
                                }
658
                            } else {
659
                                if (activeFilters.hasOwnProperty(me)) {
660
                                    delete activeFilters[me];
661
                                }
662
                            }
663
                        });
664
                    },
665
                    clear: function() {
666
                        $('#illfilter_barcode').val('');
667
                    }
668
                },
669
                dateModified: {
670
                    clear: function() {
671
                        $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
672
                    }
673
                },
674
                datePlaced: {
675
                    clear: function() {
676
                        $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
677
                    }
678
                }
679
            };
680
681
            // Expand any fields we're expanding
682
            var expandExpand = function(row) {
683
                expand.forEach(function(thisExpand) {
684
                    if (row.hasOwnProperty(thisExpand)) {
685
                        if (!expanded.hasOwnProperty(thisExpand)) {
686
                            expanded[thisExpand] = [];
687
                        }
688
                        var expandObj = row[thisExpand];
689
                        Object.keys(expandObj).forEach(
690
                            function(thisExpandCol) {
691
                                var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
692
                                // Keep a list of fields that have been expanded
693
                                // so we can create toggle links for them
694
                                if (expanded[thisExpand].indexOf(expColName) == -1) {
695
                                    expanded[thisExpand].push(expColName);
696
                                }
697
                                expandObj[expColName] =
698
                                    expandObj[thisExpandCol];
699
                                delete expandObj[thisExpandCol];
700
                            }
701
                        );
702
                        $.extend(true, row, expandObj);
703
                        delete row[thisExpand];
704
                    }
705
                });
706
            };
707
708
            // Strip the expand prefix if it exists, we do this for display
709
            var stripPrefix = function(value) {
710
                expand.forEach(function(thisExpand) {
711
                    var regex = new RegExp(thisExpand + '_', 'g');
712
                    value = value.replace(regex, '');
713
                });
714
                return value;
715
            };
716
717
            // Our 'render' function for borrowerlink
718
            var createPatronLink = function(data, type, row) {
719
                var patronLink = '<a title="' + _("View borrower details") + '" ' +
720
                    'href="/cgi-bin/koha/members/moremember.pl?' +
721
                    'borrowernumber='+row.borrowernumber+'">';
722
                if ( row.patron_firstname ) {
723
                    patronLink = patronLink + row.patron_firstname + ' ';
724
                }
725
                patronLink = patronLink + row.patron_surname + '</a>';
726
                return patronLink;
727
            };
728
729
            // Our 'render' function for biblio_id
730
            var createBiblioLink = function(data, type, row) {
731
                return (row.biblio_id) ?
732
                    '<a title="' + _("View biblio details") + '" ' +
733
                    'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
734
                    row.biblio_id + '">' +
735
                    row.biblio_id +
736
                    '</a>' : '';
737
            };
738
739
            // Render function for request ID
740
            var createRequestId = function(data, type, row) {
741
                return row.id_prefix + row.illrequest_id;
742
            };
743
744
            // Render function for type
745
            var createType = function(data, type, row) {
746
                if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
747
                    if (row.hasOwnProperty('medium') && row.medium) {
748
                        row.metadata_Type = row.medium;
749
                    } else {
750
                        row.metadata_Type = null;
751
                    }
752
                }
753
                return row.metadata_Type;
754
            };
755
756
            // Render function for request status
757
            var createStatus = function(data, type, row, meta) {
758
                if (row.status_alias) {
759
                    return row.status_alias.lib
760
                        ? row.status_alias.lib
761
                        : row.status_alias.authorised_value;
762
                } else {
763
                    var origData = meta.settings.oInit.originalData;
764
                    if (origData.length > 0) {
765
                        var status_name = meta.settings.oInit.originalData[0].capabilities[
766
                            row.status
767
                        ].name;
768
                        return getStatusName(status_name, row);
769
                    } else {
770
                        return '';
771
                    }
772
                }
773
            };
774
775
            var getStatusName = function(origName, row) {
776
                switch( origName ) {
777
                    case "New request":
778
                        return _("New request");
779
                    case "Requested":
780
                        return _("Requested");
781
                    case "Requested from partners":
782
                        var statStr = _("Requested from partners");
783
                        if (
784
                            row.hasOwnProperty('requested_partners') &&
785
                            row.requested_partners &&
786
                            row.requested_partners.length > 0
787
                        ) {
788
                            statStr += ' (' + row.requested_partners + ')';
789
                        }
790
                        return statStr;
791
                    case "Request reverted":
792
                        return _("Request reverted");
793
                    case "Queued request":
794
                        return _("Queued request");
795
                    case "Cancellation requested":
796
                        return _("Cancellation requested");
797
                    case "Completed":
798
                        return _("Completed");
799
                    case "Delete request":
800
                        return _("Delete request");
801
                    default:
802
                        return origName;
803
                }
804
            };
805
806
            // Render function for creating a row's action link
807
            var createActionLink = function(data, type, row) {
808
                return '<a class="btn btn-default btn-sm" ' +
809
                    'href="/cgi-bin/koha/ill/ill-requests.pl?' +
810
                    'method=illview&amp;illrequest_id=' +
811
                    row.illrequest_id +
812
                    '">' + _("Manage request") + '</a>';
813
            };
814
815
            // Columns that require special treatment
816
            var specialCols = {
817
                action: {
818
                    func: createActionLink
819
                },
820
                patron: {
821
                    func: createPatronLink
822
                },
823
                illrequest_id: {
824
                    func: createRequestId
825
                },
826
                status: {
827
                    func: createStatus
828
                },
829
                biblio_id: {
830
                    name: _("Bibliograpic record ID"),
831
                    func: createBiblioLink
832
                },
833
                metadata_Type: {
834
                    func: createType
835
                },
836
                updated: {
837
                    name: _("Updated on"),
838
                },
839
                patron_cardnumber: {
840
                    name: _("Patron barcode")
841
                }
842
            };
843
844
            // Display the modal containing request supplier metadata
845
            $('#ill-request-display-log').on('click', function(e) {
846
                e.preventDefault();
847
                $('#requestLog').modal({show:true});
848
            });
849
850
            // Toggle request attributes in Illview
851
            $('#toggle_requestattributes').on('click', function(e) {
852
                e.preventDefault();
853
                $('#requestattributes').toggleClass('content_hidden');
854
            });
855
856
            // Toggle new comment form in Illview
857
            $('#toggle_addcomment').on('click', function(e) {
858
                e.preventDefault();
859
                $('#addcomment').toggleClass('content_hidden');
860
            });
861
862
            // Filter partner list
863
            $('#partner_filter').keyup(function() {
864
                var needle = $('#partner_filter').val();
865
                $('#partners > option').each(function() {
866
                    var regex = new RegExp(needle, 'i');
867
                    if (
868
                        needle.length == 0 ||
869
                        $(this).is(':selected') ||
870
                        $(this).text().match(regex)
871
                    ) {
872
                        $(this).show();
873
                    } else {
874
                        $(this).hide();
875
                    }
876
                });
877
            });
878
879
            // Display the modal containing request supplier metadata
880
            $('#ill-request-display-metadata').on('click', function(e) {
881
                e.preventDefault();
882
                $('#dataPreview').modal({show:true});
883
            });
884
885
            // Get our data from the API and process it prior to passing
886
            // it to datatables
887
            var ajax = $.ajax(
888
                '/api/v1/illrequests?embed=metadata,patron,capabilities,library,comments,status_alias,requested_partners'
889
                ).done(function() {
890
                    var data = JSON.parse(ajax.responseText);
891
                    // Make a copy, we'll be removing columns next and need
892
                    // to be able to refer to data that has been removed
893
                    var dataCopy = $.extend(true, [], data);
894
                    // Expand columns that need it and create an array
895
                    // of all column names
896
                    $.each(dataCopy, function(k, row) {
897
                        expandExpand(row);
898
                    });
899
900
                    // Assemble an array of column definitions for passing
901
                    // to datatables
902
                    var colData = [];
903
                    columns_settings.forEach(function(thisCol) {
904
                        var colName = thisCol.columnname;
905
                        // Create the base column object
906
                        var colObj = $.extend({}, thisCol);
907
                        colObj.name = colName;
908
                        colObj.className = colName;
909
                        colObj.defaultContent = '';
910
911
                        // We may need to process the data going in this
912
                        // column, so do it if necessary
913
                        if (
914
                            specialCols.hasOwnProperty(colName) &&
915
                            specialCols[colName].hasOwnProperty('func')
916
                        ) {
917
                            colObj.render = specialCols[colName].func;
918
                        } else {
919
                            colObj.data = colName;
920
                        }
921
                        // Make sure properties that aren't present in the API
922
                        // response are populated with null to avoid Datatables
923
                        // choking on their absence
924
                        dataCopy.forEach(function(thisData) {
925
                            if (!thisData.hasOwnProperty(colName)) {
926
                                thisData[colName] = null;
927
                            }
928
                        });
929
                        colData.push(colObj);
930
                    });
931
932
                    // Initialise the datatable
933
                    table = KohaTable("ill-requests", {
934
                        'aoColumnDefs': [
935
                            { // Last column shouldn't be sortable or searchable
936
                                'aTargets': [ 'actions' ],
937
                                'bSortable': false,
938
                                'bSearchable': false
939
                            },
940
                            { // When sorting 'placed', we want to use the
941
                              // unformatted column
942
                              'aTargets': [ 'placed_formatted'],
943
                              'iDataSort': 16
944
                            },
945
                            { // When sorting 'updated', we want to use the
946
                              // unformatted column
947
                              'aTargets': [ 'updated_formatted'],
948
                              'iDataSort': 18
949
                            }
950
                        ],
951
                        'aaSorting': [[ 18, 'desc' ]], // Default sort, updated descending
952
                        'processing': true, // Display a message when manipulating
953
                        'sPaginationType': "full_numbers", // Pagination display
954
                        'deferRender': true, // Improve performance on big datasets
955
                        'data': dataCopy,
956
                        'columns': colData,
957
                        'originalData': data, // Enable render functions to access
958
                                              // our original data
959
                        'initComplete': function() {
960
                            // Prepare any filter elements that need it
961
                            for (var el in filterable) {
962
                                if (filterable.hasOwnProperty(el)) {
963
                                    if (filterable[el].hasOwnProperty('prep')) {
964
                                        filterable[el].prep(dataCopy, data);
965
                                    }
966
                                    if (filterable[el].hasOwnProperty('listener')) {
967
                                        filterable[el].listener();
968
                                    }
969
                                }
970
                            }
971
972
                        }
973
                    }, columns_settings);
974
975
                    // Custom date range filtering
976
                    $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
977
                        var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
978
                        var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
979
                        var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
980
                        var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
981
                        var rowPlaced = data[16] ? new Date(data[16]) : null;
982
                        var rowModified = data[18] ? new Date(data[18]) : null;
983
                        var placedPassed = true;
984
                        var modifiedPassed = true;
985
                        if (placedStart && rowPlaced && rowPlaced < placedStart) {
986
                            placedPassed = false
987
                        };
988
                        if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
989
                            placedPassed = false;
990
                        }
991
                        if (modifiedStart && rowModified && rowModified < modifiedStart) {
992
                            modifiedPassed = false
993
                        };
994
                        if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
995
                            modifiedPassed = false;
996
                        }
997
998
                        return placedPassed && modifiedPassed;
999
1000
                    });
1001
1002
                }
1003
            );
1004
1005
            var clearSearch = function() {
1006
                table.api().search('').columns().search('');
1007
                activeFilters = {};
1008
                for (var filter in filterable) {
1009
                    if (
1010
                        filterable.hasOwnProperty(filter) &&
1011
                        filterable[filter].hasOwnProperty('clear')
1012
                    ) {
1013
                        filterable[filter].clear();
1014
                    }
1015
                }
1016
                table.api().draw();
1017
            };
1018
1019
            // Apply any search filters, or clear any previous
1020
            // ones
1021
            $('#illfilter_form').submit(function(event) {
1022
                event.preventDefault();
1023
                table.api().search('').columns().search('');
1024
                for (var active in activeFilters) {
1025
                    if (activeFilters.hasOwnProperty(active)) {
1026
                        activeFilters[active]();
1027
                    }
1028
                }
1029
                table.api().draw();
1030
            });
1031
1032
            // Clear all filters
1033
            $('#clear_search').click(function() {
1034
                clearSearch();
1035
            });
1036
1037
        });
1038
    </script>
527
    </script>
528
    [% Asset.js("js/ill-list-table.js") | $raw %]
529
[% END %]
530
531
[% TRY %]
532
[% PROCESS backend_jsinclude %]
533
[% CATCH %]
1039
[% END %]
534
[% END %]
1040
535
1041
[% INCLUDE 'intranet-bottom.inc' %]
536
[% 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