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

(-)a/C4/Items.pm (-17 / +114 lines)
Lines 25-30 use Carp; Link Here
25
use C4::Context;
25
use C4::Context;
26
use C4::Koha;
26
use C4::Koha;
27
use C4::Biblio;
27
use C4::Biblio;
28
use C4::SQLHelper;
28
use C4::Dates qw/format_date format_date_in_iso/;
29
use C4::Dates qw/format_date format_date_in_iso/;
29
use MARC::Record;
30
use MARC::Record;
30
use C4::ClassSource;
31
use C4::ClassSource;
Lines 1186-1191 sub GetItemsByBiblioitemnumber { Link Here
1186
=head2 GetItemsInfo
1187
=head2 GetItemsInfo
1187
1188
1188
  @results = GetItemsInfo($biblionumber);
1189
  @results = GetItemsInfo($biblionumber);
1190
  @results = GetItemsInfo($biblionumber, $filter);
1189
1191
1190
Returns information about items with the given biblionumber.
1192
Returns information about items with the given biblionumber.
1191
1193
Lines 1223-1234 If this is set, it is set to C<One Order>. Link Here
1223
1225
1224
=back
1226
=back
1225
1227
1228
=item C<$filter>
1229
1230
A reference-to-hash. Valid filters are:
1231
$filter->{branch} = 'CPL';          #Branchcode of the library whose items should only be displayed.
1232
$filter->{volume} = '2013';         #The volume of the item from items.enumchron aka. "Numbering formula".
1233
$filter->{number} = '11';           #The number or issue of the item from items.enumchron aka. "Numbering formula".
1234
$filter->{fromDate} = '01/01/2013'; #Filters only serial issues by the serialitems.publisheddate
1235
                                          #The starting date in C4::Context->preference('dateformat') format
1236
$filter->{toDate} = '31/12/2014';   #Filters only serial issues by the serialitems.publisheddate
1237
                                          #The ending date in C4::Context->preference('dateformat') format
1238
1239
Filters are expected to be validated! If a filter is not defined, that filter is not present in the $filter-HASH
1240
1226
=cut
1241
=cut
1227
1242
1228
sub GetItemsInfo {
1243
sub GetItemsInfo {
1229
    my ( $biblionumber ) = @_;
1244
    my ( $biblionumber, $filter ) = @_;
1230
    my $dbh   = C4::Context->dbh;
1245
    my $dbh   = C4::Context->dbh;
1246
1247
    #Prepare the filter
1248
    my $filterEnumchron = 0;
1249
    my $enumchronSQLRegexp;
1250
    if (defined $filter && ref $filter eq 'HASH') {
1251
1252
        #Items enumchron can be filtered by volume or number or both.
1253
        #Because the format of enumchron
1254
        #For performance reasons regexp's need to be as simple as possible.
1255
        ## It is entirely possible to search with just volume or just number or just issue.
1256
        ##  We don't know which filters are used so it is safer and more efficient to just
1257
        ##  prepare the enumeration parsing SQL every time.
1258
        $enumchronSQLRegexp = C4::Context->preference('NumberingFormulaParsingRegexp');
1259
1260
        if (exists $filter->{volume}) {
1261
            $filterEnumchron = 1;
1262
            $enumchronSQLRegexp =~ s/volume/$filter->{volume}/;
1263
        }
1264
        else {
1265
            $enumchronSQLRegexp =~ s/volume/[0-9]*/;
1266
        }
1267
        if (exists $filter->{number}) {
1268
            $filterEnumchron = 1;
1269
            $enumchronSQLRegexp =~ s/number/$filter->{number}/;
1270
        }
1271
        else {
1272
            $enumchronSQLRegexp =~ s/number/[0-9]*/;
1273
        }
1274
        if (exists $filter->{issue}) {
1275
            $filterEnumchron = 1;
1276
            $enumchronSQLRegexp =~ s/issue/$filter->{issue}/;
1277
        }
1278
        else {
1279
            $enumchronSQLRegexp =~ s/issue/[0-9]*/;
1280
        }
1281
    }
1282
    #If we know that this item is a serial, we can better optimize our big SQL.
1283
    # This is especially useful when we want to filter based on the publication date.
1284
    # SELECTing a huge blob of serials just to remove unnecessary ones will be really sloooow.
1285
    my $search = C4::SQLHelper::SearchInTable("biblio",{biblionumber => $biblionumber}, undef, undef, ['serial'], undef, "exact");
1286
    my $serial = $search->[0]->{serial};
1287
1231
    # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1288
    # note biblioitems.* must be avoided to prevent large marc and marcxml fields from killing performance.
1289
    #Because it is uncertain how many parameters the SQL query needs, we need to build the parameters dynamically
1290
    # This is because we cannot predict what filters our users use.
1291
    my $queryParams = [$biblionumber];
1232
    my $query = "
1292
    my $query = "
1233
    SELECT items.*,
1293
    SELECT items.*,
1234
           biblio.*,
1294
           biblio.*,
Lines 1248-1254 sub GetItemsInfo { Link Here
1248
           itemtypes.notforloan as notforloan_per_itemtype,
1308
           itemtypes.notforloan as notforloan_per_itemtype,
1249
           holding.branchurl,
1309
           holding.branchurl,
1250
           holding.branchname,
1310
           holding.branchname,
1251
           holding.opac_info as branch_opac_info
1311
           holding.opac_info as branch_opac_info ";
1312
    if ($serial) {
1313
        $query .= ",
1314
           serial.serialseq,
1315
           serial.publisheddate ";
1316
    }
1317
    $query .= "
1252
     FROM items
1318
     FROM items
1253
     LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1319
     LEFT JOIN branches AS holding ON items.holdingbranch = holding.branchcode
1254
     LEFT JOIN branches AS home ON items.homebranch=home.branchcode
1320
     LEFT JOIN branches AS home ON items.homebranch=home.branchcode
Lines 1256-1274 sub GetItemsInfo { Link Here
1256
     LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1322
     LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1257
     LEFT JOIN itemtypes   ON   itemtypes.itemtype         = "
1323
     LEFT JOIN itemtypes   ON   itemtypes.itemtype         = "
1258
     . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1324
     . (C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype');
1259
    $query .= " WHERE items.biblionumber = ? ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ;
1325
1326
    if ($serial) {
1327
        $query .= "
1328
           LEFT JOIN serialitems ON serialitems.itemnumber = items.itemnumber
1329
           LEFT JOIN serial ON serialitems.serialid = serial.serialid ";
1330
    }
1331
1332
    $query .= " WHERE items.biblionumber = ? ";
1333
1334
    if (exists $filter->{branch}) {
1335
        $query .= " AND items.holdingbranch = ?";
1336
        push @$queryParams, $filter->{branch};
1337
    }
1338
    if ($filterEnumchron) {
1339
        $query .= " AND items.enumchron REGEXP ?";
1340
        push @$queryParams, $enumchronSQLRegexp;
1341
    }
1342
    if (exists $filter->{fromDate}) {
1343
        if ($serial) {
1344
            $query .= " AND serial.publisheddate >= ?";
1345
        }
1346
        else {
1347
            $query .= " AND items.timestamp >= ?";
1348
        }
1349
        push @$queryParams, $filter->{fromDate};
1350
    }
1351
    if (exists $filter->{toDate}) {
1352
        if ($serial) {
1353
            $query .= " AND serial.publisheddate <= ?";
1354
        }
1355
        else {
1356
            $query .= " AND items.timestamp <= ?";
1357
        }
1358
        push @$queryParams, $filter->{toDate};
1359
    }
1360
1361
    $query .= "ORDER BY home.branchname, items.enumchron, LPAD( items.copynumber, 8, '0' ), items.dateaccessioned DESC" ;
1260
    my $sth = $dbh->prepare($query);
1362
    my $sth = $dbh->prepare($query);
1261
    $sth->execute($biblionumber);
1363
    $sth->execute(@$queryParams);
1262
    my $i = 0;
1364
    my $i = 0;
1263
    my @results;
1365
    my @results;
1264
    my $serial;
1265
1366
1266
    my $isth    = $dbh->prepare(
1367
    my $isth    = $dbh->prepare(
1267
        "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1368
        "SELECT issues.*,borrowers.cardnumber,borrowers.surname,borrowers.firstname,borrowers.branchcode as bcode
1268
        FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1369
        FROM   issues LEFT JOIN borrowers ON issues.borrowernumber=borrowers.borrowernumber
1269
        WHERE  itemnumber = ?"
1370
        WHERE  itemnumber = ?"
1270
       );
1371
       );
1271
	my $ssth = $dbh->prepare("SELECT serialseq,publisheddate from serialitems left join serial on serialitems.serialid=serial.serialid where serialitems.itemnumber=? "); 
1372
1373
1272
	while ( my $data = $sth->fetchrow_hashref ) {
1374
	while ( my $data = $sth->fetchrow_hashref ) {
1273
        my $datedue = '';
1375
        my $datedue = '';
1274
        $isth->execute( $data->{'itemnumber'} );
1376
        $isth->execute( $data->{'itemnumber'} );
Lines 1279-1295 sub GetItemsInfo { Link Here
1279
            $data->{firstname}     = $idata->{firstname};
1381
            $data->{firstname}     = $idata->{firstname};
1280
            $data->{lastreneweddate} = $idata->{lastreneweddate};
1382
            $data->{lastreneweddate} = $idata->{lastreneweddate};
1281
            $datedue                = $idata->{'date_due'};
1383
            $datedue                = $idata->{'date_due'};
1282
        if (C4::Context->preference("IndependentBranches")){
1384
            if (C4::Context->preference("IndependentBranches")){
1283
        my $userenv = C4::Context->userenv;
1385
                my $userenv = C4::Context->userenv;
1284
        if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) { 
1386
                if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
1285
            $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1387
                    $data->{'NOTSAMEBRANCH'} = 1 if ($idata->{'bcode'} ne $userenv->{branch});
1286
        }
1388
                }
1287
        }
1389
            }
1288
        }
1289
		if ( $data->{'serial'}) {	
1290
			$ssth->execute($data->{'itemnumber'}) ;
1291
			($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
1292
			$serial = 1;
1293
        }
1390
        }
1294
        #get branch information.....
1391
        #get branch information.....
1295
        my $bsth = $dbh->prepare(
1392
        my $bsth = $dbh->prepare(
(-)a/installer/data/mysql/sysprefs.sql (+2 lines)
Lines 110-115 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
110
('ExtendedPatronAttributes','0',NULL,'Use extended patron IDs and attributes','YesNo'),
110
('ExtendedPatronAttributes','0',NULL,'Use extended patron IDs and attributes','YesNo'),
111
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
111
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
112
('FilterBeforeOverdueReport','0','','Do not run overdue report until filter selected','YesNo'),
112
('FilterBeforeOverdueReport','0','','Do not run overdue report until filter selected','YesNo'),
113
('FilterSerialsByIssue','0',NULL,'Use issue-field when filtering serial issues in addition to the volume- and number-fields? This relates to NumberingFormulaParsingRegexp system preference.','YesNo'),
113
('FineNotifyAtCheckin','0',NULL,'If ON notify librarians of overdue fines on the items they are checking in.','YesNo'),
114
('FineNotifyAtCheckin','0',NULL,'If ON notify librarians of overdue fines on the items they are checking in.','YesNo'),
114
('finesCalendar','noFinesWhenClosed','ignoreCalendar|noFinesWhenClosed','Specify whether to use the Calendar in calculating duedates and fines','Choice'),
115
('finesCalendar','noFinesWhenClosed','ignoreCalendar|noFinesWhenClosed','Specify whether to use the Calendar in calculating duedates and fines','Choice'),
115
('FinesIncludeGracePeriod','1',NULL,'If enabled, fines calculations will include the grace period.','YesNo'),
116
('FinesIncludeGracePeriod','1',NULL,'If enabled, fines calculations will include the grace period.','YesNo'),
Lines 192-197 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
192
('NovelistSelectPassword',NULL,NULL,'Enable Novelist user Profile','free'),
193
('NovelistSelectPassword',NULL,NULL,'Enable Novelist user Profile','free'),
193
('NovelistSelectProfile',NULL,NULL,'Novelist Select user Password','free'),
194
('NovelistSelectProfile',NULL,NULL,'Novelist Select user Password','free'),
194
('NovelistSelectView','tab','tab|above|below|right','Where to display Novelist Select content','Choice'),
195
('NovelistSelectView','tab','tab|above|below|right','Where to display Novelist Select content','Choice'),
196
('NumberingFormulaParsingRegexp','','','Explanation','free')
195
('numReturnedItemsToShow','20',NULL,'Number of returned items to show on the check-in page','Integer'),
197
('numReturnedItemsToShow','20',NULL,'Number of returned items to show on the check-in page','Integer'),
196
('numSearchResults','20',NULL,'Specify the maximum number of results to display on a page of results','Integer'),
198
('numSearchResults','20',NULL,'Specify the maximum number of results to display on a page of results','Integer'),
197
('numSearchRSSResults','50',NULL,'Specify the maximum number of results to display on a RSS page of results','Integer'),
199
('numSearchRSSResults','50',NULL,'Specify the maximum number of results to display on a RSS page of results','Integer'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/serials.pref (+14 lines)
Lines 50-52 Serials: Link Here
50
    -
50
    -
51
        - List of fields which must not be rewritten when a subscription is duplicated (Separated by pipe |)
51
        - List of fields which must not be rewritten when a subscription is duplicated (Separated by pipe |)
52
        - pref: SubscriptionDuplicateDroppedInput
52
        - pref: SubscriptionDuplicateDroppedInput
53
    -
54
        - pref: NumberingFormulaParsingRegexp
55
          class: long
56
        - "When dealing with potentially thousands of Serial items, there is a need to limit the display of a large number of serial issues in OPAC. This preference controls how the enumeration of a serial issue should be parsed."
57
        - "It must be a MySQL compliant regular expression, with values volume, number, issue representing those enumerants."
58
        - 'Example: ^[^0-9]*volume[^0-9]*number[^0-9]*$  would work with enumerations like "Vol 2013, No 11" or "2013 :11", but not with enumerations like "No 11, Vol 2013" or "1.a Vol 2013, No 11".'
59
        - It is recommended to not mix different enumeration schemes inside a Koha installation. It is not possible to have one branch enumerate like "issue, volume, number" and other as "volume, number" or even "volume, number, issue".
60
        - Use the recommeded value, or if you are using the FilterSerialsByIssue -system preference, use this ^[^0-9]*volume[^0-9]*number[^0-9]*issue[^0-9]*$
61
    -
62
        - pref: FilterSerialsByIssue
63
          choices:
64
              yes: "Use"
65
              no: "Don't use"
66
        - issue-field when filtering serial issues in addition to the volume- and number-fields. This relates to NumberingFormulaParsingRegexp system preference.
(-)a/koha-tmpl/opac-tmpl/prog/en/css/opac-detail.css (+14 lines)
Line 0 Link Here
1
/* Lots of stuff copied from opac.css since it shouldn't be modified. The button elements should be generalized for maintainability reasons! */
2
3
#filterIssuesButton {
4
    z-index: 1001; /* Make sure this element is always over the #filterIssuesFormContainer */
5
6
	padding : .3em .7em;
7
8
    background-image: url("../../images/desc.gif"); /* FF3.6+ */
9
    background-position:  center right;
10
	background-repeat: no-repeat;
11
12
	padding-right: 20px;
13
    margin-right: 6px;
14
}
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-detail.tt (-23 / +148 lines)
Lines 19-24 Link Here
19
19
20
[% INCLUDE 'doc-head-open.inc' %][% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo; Details for: [% title |html %][% FOREACH subtitl IN subtitle %], [% subtitl.subfield |html %][% END %]
20
[% INCLUDE 'doc-head-open.inc' %][% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo; Details for: [% title |html %][% FOREACH subtitl IN subtitle %], [% subtitl.subfield |html %][% END %]
21
[% INCLUDE 'doc-head-close.inc' %]
21
[% INCLUDE 'doc-head-close.inc' %]
22
[% INCLUDE 'calendar.inc' %]
22
[% INCLUDE 'datatables.inc' %]
23
[% INCLUDE 'datatables.inc' %]
23
[% IF ( SocialNetworks ) %]
24
[% IF ( SocialNetworks ) %]
24
    <script type="text/javascript" src="https://apis.google.com/js/plusone.js">
25
    <script type="text/javascript" src="https://apis.google.com/js/plusone.js">
Lines 35-40 Link Here
35
[% IF ( bidi ) %]
36
[% IF ( bidi ) %]
36
  <link rel="stylesheet" type="text/css" href="[% themelang %]/css/right-to-left.css" />
37
  <link rel="stylesheet" type="text/css" href="[% themelang %]/css/right-to-left.css" />
37
[% END %]
38
[% END %]
39
<link rel="stylesheet" type="text/css" href="[% themelang %]/css/opac-detail.css" />
38
<script type="text/javascript">
40
<script type="text/javascript">
39
//<![CDATA[
41
//<![CDATA[
40
42
Lines 66-72 Link Here
66
            $(".highlight_toggle").toggle();
68
            $(".highlight_toggle").toggle();
67
        }
69
        }
68
    [% END %]
70
    [% END %]
69
71
// ------------------------------ //
72
//>>> Document READY starts here! //
73
// ------------------------------ //
70
     $(document).ready(function() { 
74
     $(document).ready(function() { 
71
        $('#bibliodescriptions').tabs();
75
        $('#bibliodescriptions').tabs();
72
        $(".branch-info-tooltip-trigger").tooltip({
76
        $(".branch-info-tooltip-trigger").tooltip({
Lines 109-135 Link Here
109
        });
113
        });
110
[% END %]
114
[% END %]
111
115
112
            $(".holdingst").dataTable($.extend(true, {}, dataTablesDefaults, {
116
        $(".holdingst").dataTable($.extend(true, {}, dataTablesDefaults, {
113
                "aoColumns": [
117
            "aoColumns": [
114
                    [% IF ( item_level_itypes ) %]null,[% END %]
118
                [% IF ( item_level_itypes ) %]null,[% END %]
115
                    null,
119
                null,
116
                    [% IF ( itemdata_ccode ) %]null,[% END %]
120
                [% IF ( itemdata_ccode ) %]null,[% END %]
121
                null,
122
                [% IF ( itemdata_enumchron ) %]null,[% END %]
123
                [% IF ( itemdata_uri ) %]null,[% END %]
124
                [% IF ( itemdata_copynumber ) %]null,[% END %]
125
                null,
126
                [% IF ( itemdata_itemnotes ) %]null,[% END %]
127
                { "sType": "title-string" },
128
                [% IF ( OPACShowBarcode ) %]null,[% END %]
129
                [% IF holds_count.defined %]
117
                    null,
130
                    null,
118
                    [% IF ( itemdata_enumchron ) %]null,[% END %]
131
                [% ELSIF show_priority %]
119
                    [% IF ( itemdata_uri ) %]null,[% END %]
120
                    [% IF ( itemdata_copynumber ) %]null,[% END %]
121
                    null,
132
                    null,
122
                    [% IF ( itemdata_itemnotes ) %]null,[% END %]
133
                [% END %]
123
                    { "sType": "title-string" },
134
                [% IF ( ShowCourseReservesHeader ) %]null,[% END %]
124
                    [% IF ( OPACShowBarcode ) %]null,[% END %]
135
            ]
125
                    [% IF holds_count.defined %]
136
        }));
126
                        null,
137
127
                    [% ELSIF show_priority %]
138
        //Bind the datepicker
128
                        null,
139
        $('.datepicker').datepicker();
129
                    [% END %]
130
                    [% IF ( ShowCourseReservesHeader ) %]null,[% END %]
131
                ]
132
            }));
133
140
134
        [% IF ( query_desc ) %][% IF ( OpacHighlightedWords ) %]var query_desc = "[% query_desc |replace("'", "\'") |replace('\n', '\\n') |replace('\r', '\\r') |html %]";
141
        [% IF ( query_desc ) %][% IF ( OpacHighlightedWords ) %]var query_desc = "[% query_desc |replace("'", "\'") |replace('\n', '\\n') |replace('\r', '\\r') |html %]";
135
            q_array = query_desc.split(" ");
142
            q_array = query_desc.split(" ");
Lines 248-254 $(function () { Link Here
248
    }
255
    }
249
256
250
[% END %]
257
[% END %]
251
});
258
259
        $('#filterIssuesFormContainer').hide(); /* Making this element unobtrusive for javascript consumers */
260
        $('#filterIssuesButton').click(function() {
261
            $('#filterIssuesFormContainer').toggle();
262
        });
263
    });
264
// --------------------------- //
265
//<<< Document READY ends here //
266
// --------------------------- //
252
[% IF ( IDreamBooksReviews || IDreamBooksReadometer ) %]
267
[% IF ( IDreamBooksReviews || IDreamBooksReadometer ) %]
253
function parseIDBJSON( json ) {
268
function parseIDBJSON( json ) {
254
    if(json.total_results > 0 && json.book.rating > 0){
269
    if(json.total_results > 0 && json.book.rating > 0){
Lines 1038-1046 YAHOO.util.Event.onContentReady("furtherm", function () { Link Here
1038
1053
1039
1054
1040
<div id="holdings">
1055
<div id="holdings">
1056
1057
    [% IF ( lotsofholdingsitems ) %]
1058
        [%# Display the items filtering form used to filter the shown items. See the end of this file! %]
1059
        [% INCLUDE filter_form tab="holdings" %]
1060
    [% END %]
1061
1041
[% IF ( itemloop.size ) %]
1062
[% IF ( itemloop.size ) %]
1042
    [% IF ( lotsofholdingsitems ) %]
1063
    [% IF ( lotsofholdingsitems ) %]
1043
        <p>This record has many physical items. <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% biblionumber %]&amp;viewallitems=1#holdings">Click here to view them all.</a></p>
1064
        <p>This record has many physical items. <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% biblionumber %]&amp;viewallitems=1#holdings">Click here to view them all.</a> Or use the filter above to limit your selection</p>
1044
    [% ELSE %]
1065
    [% ELSE %]
1045
        [% INCLUDE items_table items=itemloop tab="holdings" %]
1066
        [% INCLUDE items_table items=itemloop tab="holdings" %]
1046
    [% END %]
1067
    [% END %]
Lines 1064-1070 YAHOO.util.Event.onContentReady("furtherm", function () { Link Here
1064
        <div id="alternateholdings"><span class="holdings_label">Holdings:</span> [% ALTERNATEHOLDING.holding %]</div>
1085
        <div id="alternateholdings"><span class="holdings_label">Holdings:</span> [% ALTERNATEHOLDING.holding %]</div>
1065
    [% END %]
1086
    [% END %]
1066
    [% ELSE %]
1087
    [% ELSE %]
1067
    <div id="noitems">No physical items for this record</div>
1088
    <h4 id="noitems">
1089
        No physical items for this record.
1090
        [% IF filter %]
1091
            <br/> Try clearing the filter.
1092
        [% END %]
1093
    </h4>
1068
    [% END %]
1094
    [% END %]
1069
[% END %]
1095
[% END %]
1070
1096
Lines 1604-1606 YAHOO.util.Event.onContentReady("furtherm", function () { Link Here
1604
	    [% END %]</tbody>
1630
	    [% END %]</tbody>
1605
	</table>
1631
	</table>
1606
[% END %][%# end of items_table block %]
1632
[% END %][%# end of items_table block %]
1633
1634
[% BLOCK filter_form %]
1635
    [% IF ( notDefined_NumberingFormulaParsingRegexp ) %]
1636
        <div class="dialog alert">
1637
            You must define the NumberingFormulaParsingRegexp system preference to filter items by enumeration!
1638
        </div>
1639
    [% END %]
1640
1641
    <div id="filterIssuesParentContainer">
1642
    <a id="filterIssuesButton" >Limit issues</a>
1643
    [% IF filter %]
1644
        <form id="issuesFilter" method="get" action="/cgi-bin/koha/opac-detail.pl">
1645
            <input type="hidden" name="biblionumber" id="biblionumber" value="[% biblionumber %]"/>
1646
            <input type="submit" name="clearFilter" value="Clear filter" class="submit"/>
1647
        </form>
1648
    [% END %]
1649
    <div id="filterIssuesFormContainer">
1650
    <form id="issuesFilter" method="get" action="/cgi-bin/koha/opac-detail.pl">
1651
        <input type="hidden" name="biblionumber" id="biblionumber" value="[% biblionumber %]"/>
1652
        <input type="hidden" name="viewallitems" id="viewallitems" value="1"/>
1653
1654
        <fieldset>
1655
            <table>
1656
                <tr><td>
1657
                        <label for="filterBranchLimiter">
1658
                            Library:
1659
                        </label>
1660
                    </td><td>
1661
                        <select name="filterBranchLimiter" size="1" id="filterBranchLimiter">
1662
                        [%- FOREACH branchloo IN branchloop %]
1663
                            [% IF ( branchloo.selected ) -%]
1664
                                <option value="[% branchloo.branchcode %]" selected="selected">
1665
                            [%- ELSE -%]
1666
                                <option value="[% branchloo.branchcode %]">
1667
                            [%- END -%]
1668
                            [% IF ( branchloo.branchcode ) == '_ShowAll' -%]
1669
                                Show from any library</option>
1670
                            [%- ELSE -%]
1671
                                [% branchloo.branchname %]</option>
1672
                            [%- END -%]
1673
                        [%- END %]
1674
                        </select>
1675
                    </td>
1676
                </tr>
1677
                [% IF isSerial %]
1678
                    <tr>
1679
                        <td>
1680
                            <label for="filterVolume">
1681
                                Issue volume:
1682
                            </label>
1683
                        </td><td>
1684
                            <input type="number" id="filterVolume" name="filterVolume" min="0" max="9999" maxlength="4" value="[% filter.volume %]">
1685
                        </td>
1686
                    </tr><tr>
1687
                        <td>
1688
                            <label for="filterNumber">
1689
                                Issue number:
1690
                            </label>
1691
                        </td><td>
1692
                            <input type="number" id="filterNumber" name="filterNumber" min="0" max="99" maxlength="2" value="[% filter.number %]">
1693
                        </td>
1694
                    </tr>
1695
                    [% IF useFilterIssueInput %]
1696
                    <tr>
1697
                        <td>
1698
                            <label for="filterIssue">
1699
                                Issue issue:
1700
                            </label>
1701
                        </td><td>
1702
                            <input type="number" id="filterIssue" name="filterIssue" min="0" max="99" maxlength="2" value="[% filter.issue %]">
1703
                        </td>
1704
                    </tr>
1705
                    [% END %]
1706
                [% END %][%# End of IF isSerial %]
1707
                <tr>
1708
                    <td>
1709
                        <label for="filterFrom">
1710
                            From date:
1711
                        </label>
1712
                    </td><td>
1713
                        <input type="text" size="10" id="filterFrom" name="filterFrom" value="[% filter.serialFromDate %]" class="datepicker" />
1714
                    </td>
1715
                </tr><tr>
1716
                    <td>
1717
                        <label for="filterTo">
1718
                            To date:
1719
                        </label>
1720
                    </td><td>
1721
                        <input type="text" size="10" id="filterTo" name="filterTo" value="[% filter.serialToDate %]" class="datepicker" />
1722
                    </td>
1723
                </tr>
1724
            </table>
1725
1726
            <input type="submit" name="filterIssues" value="Submit" class="submit"/>
1727
        </fieldset>
1728
    </form>
1729
    </div>
1730
    </div>
1731
[% END %][%# end of filter_form block %]
(-)a/opac/opac-detail.pl (-1 / +113 lines)
Lines 73-79 my ( $template, $borrowernumber, $cookie ) = get_template_and_user( Link Here
73
my $biblionumber = $query->param('biblionumber') || $query->param('bib') || 0;
73
my $biblionumber = $query->param('biblionumber') || $query->param('bib') || 0;
74
$biblionumber = int($biblionumber);
74
$biblionumber = int($biblionumber);
75
75
76
my @all_items = GetItemsInfo($biblionumber);
76
##
77
##>> Handling the Serial issue filter parameters from the user
78
##
79
# We can filter issues based on these five values.
80
my $filterBranchLimiter = $query->param('filterBranchLimiter') ? $query->param('filterBranchLimiter') : '_ShowAll';
81
my $filterVolume = $query->param('filterVolume') ? $query->param('filterVolume') : undef;
82
my $filterNumber = $query->param('filterNumber') ? $query->param('filterNumber') : undef;
83
my $filterIssue = $query->param('filterIssue') ? $query->param('filterIssue') : undef;
84
my $filterFromDate = $query->param('filterFrom') ? $query->param('filterFrom') : undef;
85
my $filterToDate = $query->param('filterTo') ? $query->param('filterTo') : undef;
86
87
my $filter; #a HASH! Collect the filters here, so they can be more conveniently moved around.
88
89
#We filter by the branch only if a valid branch is given.
90
if (defined $filterBranchLimiter && $filterBranchLimiter ne '_ShowAll') {
91
    $filter->{branch} = $filterBranchLimiter;
92
}
93
if (defined $filterVolume && length $filterVolume > 0) {
94
    if (!($filterVolume =~ /\d{1,4}/)) {
95
        print $query->header(); #bad data goddamnit!
96
        print "Invalid volume. Please try again. \n";
97
        exit;
98
    }
99
    else {
100
        $filter->{volume} = $filterVolume;
101
    }
102
}
103
if (defined $filterNumber && length $filterNumber > 0) {
104
    if (!($filterNumber =~ /\d{1,2}/)) {
105
        print $query->header(); #stop spamming bad data!
106
        print "Invalid number. Please try again. \n";
107
        exit;
108
    }
109
    else {
110
        $filter->{number} = $filterNumber;
111
    }
112
}
113
if (defined $filterIssue && length $filterIssue > 0) {
114
    if (!($filterIssue =~ /\d{1,2}/) ) {
115
        print $query->header(); #stop spamming bad data!
116
        print "Invalid issue. Please try again. \n";
117
        exit;
118
    }
119
    else {
120
        $filter->{issue} = $filterIssue;
121
    }
122
}
123
if (defined $filterFromDate && length $filterFromDate > 0) {
124
    if (!($filterFromDate =~ C4::Dates->regexp( C4::Context->preference('dateformat') )) ) {
125
        print $query->header(); #noo not anymore noo!
126
        print "Invalid starting date. Please try again. \n";
127
        exit;
128
    }
129
    else {
130
        $filter->{fromDate} = C4::Dates::format_date_in_iso( $filterFromDate );
131
    }
132
}
133
if (defined $filterToDate && length $filterToDate > 0) {
134
    if (!($filterToDate =~ C4::Dates->regexp( C4::Context->preference('dateformat') )) ) {
135
        print $query->header(); #take your bad data away!
136
        print "Invalid ending date. Please try again. \n";
137
        exit;
138
    }
139
    else {
140
        $filter->{toDate} = C4::Dates::format_date_in_iso( $filterToDate );
141
    }
142
}
143
144
145
##Prepare the custom branches loop containing the _ShowAll entry to show issues from all libraries.
146
my $branchloop;
147
if ( $filterBranchLimiter eq '_ShowAll' || !(defined $filterBranchLimiter) ) {
148
    $branchloop = C4::Branch::GetBranchesLoop('0'); #Using '0' to disable reverting to the users home branch
149
    unshift @$branchloop, { branchcode => '_ShowAll', branchname => 'Show from any library', selected => '1', value => '_ShowAll'};
150
}
151
else {
152
    $branchloop = C4::Branch::GetBranchesLoop($filterBranchLimiter);
153
    unshift @$branchloop, { branchcode => '_ShowAll', branchname => 'Show from any library', selected => '0', value => '_ShowAll'};
154
}
155
$template->param( branchloop => $branchloop );
156
$template->param( filter => $filter ) if defined $filter;
157
158
##
159
##<< Serial issues filter parameters handled! ##
160
##
161
162
163
my @all_items = GetItemsInfo($biblionumber, $filter);
164
165
# Now that the filter is no longer needed, we can reuse it to keep the filter modifications in the UI,
166
#  by reverting the dates to the same format as in the UI layer.
167
$filter->{fromDate} = $filterFromDate;
168
$filter->{toDate} = $filterToDate;
169
77
my @hiddenitems;
170
my @hiddenitems;
78
if (scalar @all_items >= 1) {
171
if (scalar @all_items >= 1) {
79
    push @hiddenitems, GetHiddenItemnumbers(@all_items);
172
    push @hiddenitems, GetHiddenItemnumbers(@all_items);
Lines 91-96 if ( ! $record ) { Link Here
91
}
184
}
92
$template->param( biblionumber => $biblionumber );
185
$template->param( biblionumber => $biblionumber );
93
186
187
#Figure out if we are dealing with a serial! This affects the filter fields in UI
188
if (scalar @all_items > 0) {
189
    $template->param( isSerial => $all_items[0]->{serial} );
190
}
191
else {
192
    #It could be that a serial has no items to be displayed. This could be because there are none or the filters filter all items
193
    my $search = C4::SQLHelper::SearchInTable("biblio",{biblionumber => $biblionumber}, undef, undef, ['serial'], undef, "exact");
194
    $template->param( isSerial => $search->[0]->{serial} );
195
}
196
197
198
199
94
# get biblionumbers stored in the cart
200
# get biblionumbers stored in the cart
95
my @cart_list;
201
my @cart_list;
96
202
Lines 1068-1072 if ( C4::Context->preference('UseCourseReserves') ) { Link Here
1068
        $i->{'course_reserves'} = GetItemCourseReservesInfo( itemnumber => $i->{'itemnumber'} );
1174
        $i->{'course_reserves'} = GetItemCourseReservesInfo( itemnumber => $i->{'itemnumber'} );
1069
    }
1175
    }
1070
}
1176
}
1177
## Defining general Serial issue filter related system preferences
1178
#Making sure the NumberingFormulaParsingRegexp preference is set!
1179
if ( length C4::Context->preference('NumberingFormulaParsingRegexp') < 3 ) {
1180
    $template->{VARS}->{notDefined_NumberingFormulaParsingRegexp} = 1;
1181
}
1182
$template->{VARS}->{useFilterIssueInput} = 1 if (C4::Context->preference('FilterSerialsByIssue'));
1071
1183
1072
output_html_with_http_headers $query, $cookie, $template->output;
1184
output_html_with_http_headers $query, $cookie, $template->output;
(-)a/t/db_dependent/Items.t (-1 / +34 lines)
Lines 21-27 use Modern::Perl; Link Here
21
use MARC::Record;
21
use MARC::Record;
22
use C4::Biblio;
22
use C4::Biblio;
23
23
24
use Test::More tests => 3;
24
use Test::More tests => 4;
25
25
26
BEGIN {
26
BEGIN {
27
    use_ok('C4::Items');
27
    use_ok('C4::Items');
Lines 142-147 subtest 'GetHiddenItemnumbers tests' => sub { Link Here
142
    $dbh->rollback;
142
    $dbh->rollback;
143
};
143
};
144
144
145
146
subtest 'Filter items tests' => sub {
147
148
    plan tests => 2;
149
150
    # Start transaction
151
    $dbh->{AutoCommit} = 0;
152
    $dbh->{RaiseError} = 1;
153
154
    # Create a new biblio
155
    my ($biblionumber, $biblioitemnumber) = get_biblio();
156
157
    # Add two items
158
    my ($item1_bibnum, $item1_bibitemnum, $item1_itemnumber) = AddItem(
159
            { homebranch => 'CPL',
160
              holdingbranch => 'CPL', },
161
            $biblionumber
162
    );
163
    my ($item2_bibnum, $item2_bibitemnum, $item2_itemnumber) = AddItem(
164
            { homebranch => 'MPL',
165
              holdingbranch => 'MPL', },
166
            $biblionumber
167
    );
168
169
    # Testing the branch filter
170
    my @shouldBeItem2 = C4::Items::GetItemsInfo($biblionumber, {branch => 'MPL'});
171
    is( $shouldBeItem2[0]->{itemnumber}, $item2_itemnumber, "Filtering by branch");
172
173
    # Testing the dates filter
174
    my @shouldBeEmpty = C4::Items::GetItemsInfo($biblionumber, {toDate => '01/01/1933'});
175
    is( scalar(@shouldBeEmpty), 0, "Filtering by date");
176
};
177
145
# Helper method to set up a Biblio.
178
# Helper method to set up a Biblio.
146
sub get_biblio {
179
sub get_biblio {
147
    my $bib = MARC::Record->new();
180
    my $bib = MARC::Record->new();
(-)a/t/db_dependent/Serials.t (-1 / +84 lines)
Lines 14-19 use C4::Debug; Link Here
14
use C4::Bookseller;
14
use C4::Bookseller;
15
use C4::Biblio;
15
use C4::Biblio;
16
use C4::Budgets;
16
use C4::Budgets;
17
use C4::Items;
18
use C4::Biblio;
19
17
use Test::More tests => 35;
20
use Test::More tests => 35;
18
21
19
BEGIN {
22
BEGIN {
Lines 180-183 is(C4::Serials::check_routing(), undef, 'test checking route'); Link Here
180
183
181
is(C4::Serials::addroutingmember(),undef, 'test adding route member');
184
is(C4::Serials::addroutingmember(),undef, 'test adding route member');
182
185
186
187
subtest 'Filter items tests' => sub {
188
189
    plan tests => 4;
190
191
192
    # Start transaction
193
    $dbh->{AutoCommit} = 0;
194
    $dbh->{RaiseError} = 1;
195
196
    # Create a new biblio
197
    my ($biblionumber, $biblioitemnumber) = get_biblio();
198
199
    # Add items
200
	my ($item0_bibnum, $item0_bibitemnum, $item0_itemnumber) = AddItem(
201
            { homebranch => 'CPL',
202
              holdingbranch => 'CPL',
203
			  enumchron => 'Vol 2012 : No 1, Issuezz 1'},
204
            $biblionumber
205
    );
206
    my ($item1_bibnum, $item1_bibitemnum, $item1_itemnumber) = AddItem(
207
            { homebranch => 'CPL',
208
              holdingbranch => 'CPL',
209
			  enumchron => 'Vol 2013 : No 11, Issuezz 1'},
210
            $biblionumber
211
    );
212
    my ($item2_bibnum, $item2_bibitemnum, $item2_itemnumber) = AddItem(
213
            { homebranch => 'MPL',
214
              holdingbranch => 'MPL',
215
			  enumchron => 'Vol 2013 : No 11, Issuezz 2'},
216
            $biblionumber
217
    );
218
	my ($item3_bibnum, $item3_bibitemnum, $item3_itemnumber) = AddItem(
219
            { homebranch => 'CPL',
220
              holdingbranch => 'CPL',
221
			  enumchron => 'Vol 2013 : No 12, Issuezz 1'},
222
            $biblionumber
223
    );
224
    my ($item4_bibnum, $item4_bibitemnum, $item4_itemnumber) = AddItem(
225
            { homebranch => 'MPL',
226
              holdingbranch => 'MPL',
227
			  enumchron => 'Vol 2013 : No 12, Issuezz 2'},
228
            $biblionumber
229
    );
230
	my ($item5_bibnum, $item5_bibitemnum, $item5_itemnumber) = AddItem(
231
            { homebranch => 'MPL',
232
              holdingbranch => 'MPL',
233
			  enumchron => 'Vol 2014 : No 12, Issuezz 3'},
234
            $biblionumber
235
    );
236
237
	C4::Context->set_preference('NumberingFormulaParsingRegexp', '^[^0-9]*volume[^0-9]*number[^0-9]*issue[^0-9]*$');
238
239
    # Testing the volume filter
240
    my @shouldBe4Items = C4::Items::GetItemsInfo($biblionumber, {volume => '2013'});
241
    is( scalar(@shouldBe4Items), 4, "Filtering by volume");
242
243
    # Testing the number filter
244
    my @shouldBe3Items = C4::Items::GetItemsInfo($biblionumber, {number => '12'});
245
    is( scalar(@shouldBe3Items), 3, "Filtering by number");
246
247
	# Testing the issue filter
248
    my @shouldBe2Items = C4::Items::GetItemsInfo($biblionumber, {issue => '2'});
249
    is( scalar(@shouldBe2Items), 2, "Filtering by issue");
250
251
	# Testing the volume + number + issue filter
252
    my @shouldBeItem4 = C4::Items::GetItemsInfo($biblionumber, {volume => 2013, number => 12, issue => '2'});
253
    is( $shouldBeItem4[0]->{itemnumber}, $item4_itemnumber, "Filtering by volume + number + issue");
254
};
255
256
# Helper method to set up a Biblio.
257
sub get_biblio {
258
    my $bib = MARC::Record->new();
259
    $bib->append_fields(
260
        MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
261
        MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
262
    );
263
    my ($bibnum, $bibitemnum) = C4::Biblio::AddBiblio($bib, '');
264
    return ($bibnum, $bibitemnum);
265
}
266
183
$dbh->rollback;
267
$dbh->rollback;
184
- 

Return to bug 11129