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

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