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

(-)a/C4/HoldsQueue.pm (-145 / +257 lines)
Lines 36-55 use List::MoreUtils qw(any); Link Here
36
use Data::Dumper;
36
use Data::Dumper;
37
37
38
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
38
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
39
39
BEGIN {
40
BEGIN {
40
    $VERSION = 3.03;
41
    $VERSION = 3.03;
41
    require Exporter;
42
    require Exporter;
42
    @ISA = qw(Exporter);
43
    @ISA       = qw(Exporter);
43
    @EXPORT_OK = qw(
44
    @EXPORT_OK = qw(
44
        &CreateQueue
45
      &CreateQueue
45
        &GetHoldsQueueItems
46
      &GetHoldsQueueItems
46
47
47
        &TransportCostMatrix
48
      &TransportCostMatrix
48
        &UpdateTransportCostMatrix
49
      &UpdateTransportCostMatrix
49
     );
50
    );
50
}
51
}
51
52
52
53
=head1 FUNCTIONS
53
=head1 FUNCTIONS
54
54
55
=head2 TransportCostMatrix
55
=head2 TransportCostMatrix
Lines 61-76 Returns Transport Cost Matrix as a hashref <to branch code> => <from branch code Link Here
61
=cut
61
=cut
62
62
63
sub TransportCostMatrix {
63
sub TransportCostMatrix {
64
    my $dbh   = C4::Context->dbh;
64
    my $dbh = C4::Context->dbh;
65
    my $transport_costs = $dbh->selectall_arrayref("SELECT * FROM transport_cost",{ Slice => {} });
65
    my $transport_costs =
66
      $dbh->selectall_arrayref( "SELECT * FROM transport_cost",
67
        { Slice => {} } );
66
68
67
    my %transport_cost_matrix;
69
    my %transport_cost_matrix;
68
    foreach (@$transport_costs) {
70
    foreach (@$transport_costs) {
69
        my $from = $_->{frombranch};
71
        my $from     = $_->{frombranch};
70
        my $to = $_->{tobranch};
72
        my $to       = $_->{tobranch};
71
        my $cost = $_->{cost};
73
        my $cost     = $_->{cost};
72
        my $disabled = $_->{disable_transfer};
74
        my $disabled = $_->{disable_transfer};
73
        $transport_cost_matrix{$to}{$from} = { cost => $cost, disable_transfer => $disabled };
75
        $transport_cost_matrix{$to}{$from} =
76
          { cost => $cost, disable_transfer => $disabled };
74
    }
77
    }
75
    return \%transport_cost_matrix;
78
    return \%transport_cost_matrix;
76
}
79
}
Lines 86-105 Records: { frombranch => <code>, tobranch => <code>, cost => <figure>, disable_t Link Here
86
89
87
sub UpdateTransportCostMatrix {
90
sub UpdateTransportCostMatrix {
88
    my ($records) = @_;
91
    my ($records) = @_;
89
    my $dbh   = C4::Context->dbh;
92
    my $dbh = C4::Context->dbh;
90
93
91
    my $sth = $dbh->prepare("INSERT INTO transport_cost (frombranch, tobranch, cost, disable_transfer) VALUES (?, ?, ?, ?)");
94
    my $sth = $dbh->prepare(
95
"INSERT INTO transport_cost (frombranch, tobranch, cost, disable_transfer) VALUES (?, ?, ?, ?)"
96
    );
92
97
93
    $dbh->do("TRUNCATE TABLE transport_cost");
98
    $dbh->do("DELETE FROM transport_cost")
99
      ; #Deleting is slower than TRUNCATE, but using TRUNCATE screws up $dbh->{AutoCommit} in unit tests
94
    foreach (@$records) {
100
    foreach (@$records) {
95
        my $cost = $_->{cost};
101
        my $cost = $_->{cost};
96
        my $from = $_->{frombranch};
102
        my $from = $_->{frombranch};
97
        my $to = $_->{tobranch};
103
        my $to   = $_->{tobranch};
98
        if ($_->{disable_transfer}) {
104
        if ( $_->{disable_transfer} ) {
99
            $cost ||= 0;
105
            $cost ||= 0;
100
        }
106
        }
101
        elsif ( !defined ($cost) || ($cost !~ m/(0|[1-9][0-9]*)(\.[0-9]*)?/o) ) {
107
        elsif ( !defined($cost) || ( $cost !~ m/(0|[1-9][0-9]*)(\.[0-9]*)?/o ) )
102
            warn  "Invalid $from -> $to cost $cost - must be a number >= 0, disablig";
108
        {
109
            warn
110
"Invalid $from -> $to cost $cost - must be a number >= 0, disablig";
103
            $cost = 0;
111
            $cost = 0;
104
            $_->{disable_transfer} = 1;
112
            $_->{disable_transfer} = 1;
105
        }
113
        }
Lines 117-150 Returns hold queue for a holding branch. If branch is omitted, then whole queue Link Here
117
125
118
sub GetHoldsQueueItems {
126
sub GetHoldsQueueItems {
119
    my ($branchlimit) = @_;
127
    my ($branchlimit) = @_;
120
    my $dbh   = C4::Context->dbh;
128
    my $dbh = C4::Context->dbh;
121
129
122
    my @bind_params = ();
130
    my @bind_params = ();
123
    my $query = q/SELECT tmp_holdsqueue.*, biblio.author, items.ccode, items.itype, biblioitems.itemtype, items.location, items.enumchron, items.cn_sort, biblioitems.publishercode,biblio.copyrightdate,biblioitems.publicationyear,biblioitems.pages,biblioitems.size,biblioitems.publicationyear,biblioitems.isbn,items.copynumber
131
    my $query =
132
q/SELECT tmp_holdsqueue.*, biblio.author, items.ccode, items.itype, biblioitems.itemtype, items.location, items.enumchron, items.cn_sort, biblioitems.publishercode,biblio.copyrightdate,biblioitems.publicationyear,biblioitems.pages,biblioitems.size,biblioitems.publicationyear,biblioitems.isbn,items.copynumber
124
                  FROM tmp_holdsqueue
133
                  FROM tmp_holdsqueue
125
                       JOIN biblio      USING (biblionumber)
134
                       JOIN biblio      USING (biblionumber)
126
                  LEFT JOIN biblioitems USING (biblionumber)
135
                  LEFT JOIN biblioitems USING (biblionumber)
127
                  LEFT JOIN items       USING (  itemnumber)
136
                  LEFT JOIN items       USING (  itemnumber)
128
                /;
137
                /;
129
    if ($branchlimit) {
138
    if ($branchlimit) {
130
        $query .=" WHERE tmp_holdsqueue.holdingbranch = ?";
139
        $query .= " WHERE tmp_holdsqueue.holdingbranch = ?";
131
        push @bind_params, $branchlimit;
140
        push @bind_params, $branchlimit;
132
    }
141
    }
133
    $query .= " ORDER BY ccode, location, cn_sort, author, title, pickbranch, reservedate";
142
    $query .=
143
" ORDER BY ccode, location, cn_sort, author, title, pickbranch, reservedate";
134
    my $sth = $dbh->prepare($query);
144
    my $sth = $dbh->prepare($query);
135
    $sth->execute(@bind_params);
145
    $sth->execute(@bind_params);
136
    my $items = [];
146
    my $items = [];
137
    while ( my $row = $sth->fetchrow_hashref ){
147
    while ( my $row = $sth->fetchrow_hashref ) {
138
        $row->{reservedate} = format_date($row->{reservedate});
148
        $row->{reservedate} = format_date( $row->{reservedate} );
139
        my $record = GetMarcBiblio($row->{biblionumber});
149
        my $record = GetMarcBiblio( $row->{biblionumber} );
140
        if ($record){
150
        if ($record) {
141
            $row->{subtitle} = GetRecordValue('subtitle',$record,'')->[0]->{subfield};
151
            $row->{subtitle} =
142
            $row->{parts} = GetRecordValue('parts',$record,'')->[0]->{subfield};
152
              GetRecordValue( 'subtitle', $record, '' )->[0]->{subfield};
143
            $row->{numbers} = GetRecordValue('numbers',$record,'')->[0]->{subfield};
153
            $row->{parts} =
154
              GetRecordValue( 'parts', $record, '' )->[0]->{subfield};
155
            $row->{numbers} =
156
              GetRecordValue( 'numbers', $record, '' )->[0]->{subfield};
144
        }
157
        }
145
158
146
        # return the bib-level or item-level itype per syspref
159
        # return the bib-level or item-level itype per syspref
147
        if (!C4::Context->preference('item-level_itypes')) {
160
        if ( !C4::Context->preference('item-level_itypes') ) {
148
            $row->{itype} = $row->{itemtype};
161
            $row->{itype} = $row->{itemtype};
149
        }
162
        }
150
        delete $row->{itemtype};
163
        delete $row->{itemtype};
Lines 163-171 Top level function that turns reserves into tmp_holdsqueue and hold_fill_targets Link Here
163
=cut
176
=cut
164
177
165
sub CreateQueue {
178
sub CreateQueue {
166
    my $dbh   = C4::Context->dbh;
179
    my $dbh = C4::Context->dbh;
167
180
168
    $dbh->do("DELETE FROM tmp_holdsqueue");  # clear the old table for new info
181
    $dbh->do("DELETE FROM tmp_holdsqueue");   # clear the old table for new info
169
    $dbh->do("DELETE FROM hold_fill_targets");
182
    $dbh->do("DELETE FROM hold_fill_targets");
170
183
171
    my $total_bibs            = 0;
184
    my $total_bibs            = 0;
Lines 175-184 sub CreateQueue { Link Here
175
188
176
    my $branches_to_use;
189
    my $branches_to_use;
177
    my $transport_cost_matrix;
190
    my $transport_cost_matrix;
178
    my $use_transport_cost_matrix = C4::Context->preference("UseTransportCostMatrix");
191
    my $use_transport_cost_matrix =
192
      C4::Context->preference("UseTransportCostMatrix");
179
    if ($use_transport_cost_matrix) {
193
    if ($use_transport_cost_matrix) {
180
        $transport_cost_matrix = TransportCostMatrix();
194
        $transport_cost_matrix = TransportCostMatrix();
181
        unless (keys %$transport_cost_matrix) {
195
        unless ( keys %$transport_cost_matrix ) {
182
            warn "UseTransportCostMatrix set to yes, but matrix not populated";
196
            warn "UseTransportCostMatrix set to yes, but matrix not populated";
183
            undef $transport_cost_matrix;
197
            undef $transport_cost_matrix;
184
        }
198
        }
Lines 191-215 sub CreateQueue { Link Here
191
205
192
    foreach my $biblionumber (@$bibs_with_pending_requests) {
206
    foreach my $biblionumber (@$bibs_with_pending_requests) {
193
        $total_bibs++;
207
        $total_bibs++;
194
        my $hold_requests   = GetPendingHoldRequestsForBib($biblionumber);
208
        my $hold_requests = GetPendingHoldRequestsForBib($biblionumber);
195
        my $available_items = GetItemsAvailableToFillHoldRequestsForBib($biblionumber, $branches_to_use);
209
        my $available_items =
210
          GetItemsAvailableToFillHoldRequestsForBib( $biblionumber,
211
            $branches_to_use );
196
        $total_requests        += scalar(@$hold_requests);
212
        $total_requests        += scalar(@$hold_requests);
197
        $total_available_items += scalar(@$available_items);
213
        $total_available_items += scalar(@$available_items);
198
214
199
        my $item_map = MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
215
        my $item_map = MapItemsToHoldRequests(
200
        $item_map  or next;
216
            $hold_requests,   $available_items,
201
        my $item_map_size = scalar(keys %$item_map)
217
            $branches_to_use, $transport_cost_matrix
218
        );
219
        $item_map or next;
220
        my $item_map_size = scalar( keys %$item_map )
202
          or next;
221
          or next;
203
222
204
        $num_items_mapped += $item_map_size;
223
        $num_items_mapped += $item_map_size;
205
        CreatePicklistFromItemMap($item_map);
224
        CreatePicklistFromItemMap($item_map);
206
        AddToHoldTargetMap($item_map);
225
        AddToHoldTargetMap($item_map);
207
        if (($item_map_size < scalar(@$hold_requests  )) and
226
        if (    ( $item_map_size < scalar(@$hold_requests) )
208
            ($item_map_size < scalar(@$available_items))) {
227
            and ( $item_map_size < scalar(@$available_items) ) )
209
            # DOUBLE CHECK, but this is probably OK - unfilled item-level requests
228
        {
210
            # FIXME
229
      # DOUBLE CHECK, but this is probably OK - unfilled item-level requests
211
            #warn "unfilled requests for $biblionumber";
230
      # FIXME
212
            #warn Dumper($hold_requests), Dumper($available_items), Dumper($item_map);
231
      #warn "unfilled requests for $biblionumber";
232
      #warn Dumper($hold_requests), Dumper($available_items), Dumper($item_map);
213
        }
233
        }
214
    }
234
    }
215
}
235
}
Lines 267-273 sub GetPendingHoldRequestsForBib { Link Here
267
287
268
    my $dbh = C4::Context->dbh;
288
    my $dbh = C4::Context->dbh;
269
289
270
    my $request_query = "SELECT biblionumber, borrowernumber, itemnumber, priority, reserves.branchcode,
290
    my $request_query =
291
"SELECT biblionumber, borrowernumber, itemnumber, priority, reserves.branchcode,
271
                                reservedate, reservenotes, borrowers.branchcode AS borrowerbranch
292
                                reservedate, reservenotes, borrowers.branchcode AS borrowerbranch
272
                         FROM reserves
293
                         FROM reserves
273
                         JOIN borrowers USING (borrowernumber)
294
                         JOIN borrowers USING (borrowernumber)
Lines 280-286 sub GetPendingHoldRequestsForBib { Link Here
280
    my $sth = $dbh->prepare($request_query);
301
    my $sth = $dbh->prepare($request_query);
281
    $sth->execute($biblionumber);
302
    $sth->execute($biblionumber);
282
303
283
    my $requests = $sth->fetchall_arrayref({});
304
    my $requests = $sth->fetchall_arrayref( {} );
284
    return $requests;
305
    return $requests;
285
306
286
}
307
}
Lines 303-325 to fill a hold request if and only if: Link Here
303
=cut
324
=cut
304
325
305
sub GetItemsAvailableToFillHoldRequestsForBib {
326
sub GetItemsAvailableToFillHoldRequestsForBib {
306
    my ($biblionumber, $branches_to_use) = @_;
327
    my ( $biblionumber, $branches_to_use ) = @_;
307
328
308
    my $dbh = C4::Context->dbh;
329
    my $dbh = C4::Context->dbh;
309
    my $items_query = "SELECT itemnumber, homebranch, holdingbranch, itemtypes.itemtype AS itype
330
    my $items_query =
331
"SELECT itemnumber, homebranch, holdingbranch, ccode, itemtypes.itemtype AS itype
310
                       FROM items ";
332
                       FROM items ";
311
333
312
    if (C4::Context->preference('item-level_itypes')) {
334
    if ( C4::Context->preference('item-level_itypes') ) {
313
        $items_query .=   "LEFT JOIN itemtypes ON (itemtypes.itemtype = items.itype) ";
335
        $items_query .=
314
    } else {
336
          "LEFT JOIN itemtypes ON (itemtypes.itemtype = items.itype) ";
315
        $items_query .=   "JOIN biblioitems USING (biblioitemnumber)
337
    }
338
    else {
339
        $items_query .= "JOIN biblioitems USING (biblioitemnumber)
316
                           LEFT JOIN itemtypes USING (itemtype) ";
340
                           LEFT JOIN itemtypes USING (itemtype) ";
317
    }
341
    }
318
    $items_query .=   "WHERE items.notforloan = 0
342
    $items_query .= "WHERE items.notforloan = 0
319
                       AND holdingbranch IS NOT NULL
343
                       AND holdingbranch IS NOT NULL
320
                       AND itemlost = 0
344
                       AND itemlost = 0
321
                       AND withdrawn = 0";
345
                       AND withdrawn = 0";
322
    $items_query .= "  AND damaged = 0" unless C4::Context->preference('AllowHoldsOnDamagedItems');
346
    $items_query .= "  AND damaged = 0"
347
      unless C4::Context->preference('AllowHoldsOnDamagedItems');
323
    $items_query .= "  AND items.onloan IS NULL
348
    $items_query .= "  AND items.onloan IS NULL
324
                       AND (itemtypes.notforloan IS NULL OR itemtypes.notforloan = 0)
349
                       AND (itemtypes.notforloan IS NULL OR itemtypes.notforloan = 0)
325
                       AND itemnumber NOT IN (
350
                       AND itemnumber NOT IN (
Lines 330-352 sub GetItemsAvailableToFillHoldRequestsForBib { Link Here
330
                           AND (found IS NOT NULL OR priority = 0)
355
                           AND (found IS NOT NULL OR priority = 0)
331
                        )
356
                        )
332
                       AND items.biblionumber = ?";
357
                       AND items.biblionumber = ?";
333
    $items_query .=  " AND damaged = 0 "
358
    $items_query .= " AND damaged = 0 "
334
      unless C4::Context->preference('AllowHoldsOnDamagedItems');
359
      unless C4::Context->preference('AllowHoldsOnDamagedItems');
335
360
336
    my @params = ($biblionumber, $biblionumber);
361
    my @params = ( $biblionumber, $biblionumber );
337
    if ($branches_to_use && @$branches_to_use) {
362
    if ( $branches_to_use && @$branches_to_use ) {
338
        $items_query .= " AND holdingbranch IN (" . join (",", map { "?" } @$branches_to_use) . ")";
363
        $items_query .= " AND holdingbranch IN ("
364
          . join( ",", map { "?" } @$branches_to_use ) . ")";
339
        push @params, @$branches_to_use;
365
        push @params, @$branches_to_use;
340
    }
366
    }
341
    my $sth = $dbh->prepare($items_query);
367
    my $sth = $dbh->prepare($items_query);
342
    $sth->execute(@params);
368
    $sth->execute(@params);
343
369
344
    my $itm = $sth->fetchall_arrayref({});
370
    my $itm = $sth->fetchall_arrayref( {} );
345
    my @items = grep { ! scalar GetTransfers($_->{itemnumber}) } @$itm;
371
    my @items = grep { !scalar GetTransfers( $_->{itemnumber} ) } @$itm;
346
    return [ grep {
372
    return [
347
        my $rule = GetBranchItemRule($_->{homebranch}, $_->{itype});
373
        grep {
348
        $_->{holdallowed} = $rule->{holdallowed};
374
            my $rule = GetBranchItemRule( $_->{homebranch}, $_->{itype} );
349
    } @items ];
375
            $_->{holdallowed} = $rule->{holdallowed};
376
        } @items
377
    ];
350
}
378
}
351
379
352
=head2 MapItemsToHoldRequests
380
=head2 MapItemsToHoldRequests
Lines 356-362 sub GetItemsAvailableToFillHoldRequestsForBib { Link Here
356
=cut
384
=cut
357
385
358
sub MapItemsToHoldRequests {
386
sub MapItemsToHoldRequests {
359
    my ($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix) = @_;
387
    my (
388
        $hold_requests,   $available_items,
389
        $branches_to_use, $transport_cost_matrix
390
    ) = @_;
360
391
361
    # handle trival cases
392
    # handle trival cases
362
    return unless scalar(@$hold_requests) > 0;
393
    return unless scalar(@$hold_requests) > 0;
Lines 364-371 sub MapItemsToHoldRequests { Link Here
364
395
365
    # identify item-level requests
396
    # identify item-level requests
366
    my %specific_items_requested = map { $_->{itemnumber} => 1 }
397
    my %specific_items_requested = map { $_->{itemnumber} => 1 }
367
                                   grep { defined($_->{itemnumber}) }
398
      grep { defined( $_->{itemnumber} ) } @$hold_requests;
368
                                   @$hold_requests;
369
399
370
    # group available items by itemnumber
400
    # group available items by itemnumber
371
    my %items_by_itemnumber = map { $_->{itemnumber} => $_ } @$available_items;
401
    my %items_by_itemnumber = map { $_->{itemnumber} => $_ } @$available_items;
Lines 382-404 sub MapItemsToHoldRequests { Link Here
382
        last if $num_items_remaining == 0;
412
        last if $num_items_remaining == 0;
383
413
384
        # is this an item-level request?
414
        # is this an item-level request?
385
        if (defined($request->{itemnumber})) {
415
        if ( defined( $request->{itemnumber} ) ) {
416
386
            # fill it if possible; if not skip it
417
            # fill it if possible; if not skip it
387
            if (exists $items_by_itemnumber{$request->{itemnumber}} and
418
            if ( exists $items_by_itemnumber{ $request->{itemnumber} }
388
                not exists $allocated_items{$request->{itemnumber}}) {
419
                and not exists $allocated_items{ $request->{itemnumber} } )
389
                $item_map{$request->{itemnumber}} = {
420
            {
390
                    borrowernumber => $request->{borrowernumber},
421
391
                    biblionumber => $request->{biblionumber},
422
#Dereferencing variables for sanitys sake, and because it is faster when repeatedly called.
392
                    holdingbranch =>  $items_by_itemnumber{$request->{itemnumber}}->{holdingbranch},
423
                my $holdingBranch =
393
                    pickup_branch => $request->{branchcode} || $request->{borrowerbranch},
424
                  $items_by_itemnumber{ $request->{itemnumber} }
394
                    item_level => 1,
425
                  ->{holdingbranch};
395
                    reservedate => $request->{reservedate},
426
                my $pickupBranch =
396
                    reservenotes => $request->{reservenotes},
427
                  $request->{branchcode} || $request->{borrowerbranch};
397
                };
428
398
                $allocated_items{$request->{itemnumber}}++;
429
                my ( $transferOk, $errorMsg ) =
399
                $num_items_remaining--;
430
                  C4::Circulation::CanItemBeTransferred( $pickupBranch,
431
                    $holdingBranch,
432
                    $items_by_itemnumber{ $request->{itemnumber} }, undef );
433
                if ( !$transferOk ) {
434
435
#UseBranchTransfer check failed, gosh damn how can this be! It should be blocked in the UI for item level holds!
436
                    warn
437
"HoldsQueue.pm:> Branch transfer failed for pickup branch $pickupBranch, holding branch $holdingBranch, itemnumber $request->{itemnumber}\n"
438
                      . "This should be impossible because branch transfer limits for item level holds must be checked when placing holds!";
439
                }
440
                else {
441
                    $item_map{ $request->{itemnumber} } = {
442
                        borrowernumber => $request->{borrowernumber},
443
                        biblionumber   => $request->{biblionumber},
444
                        holdingbranch =>
445
                          $items_by_itemnumber{ $request->{itemnumber} }
446
                          ->{holdingbranch},
447
                        pickup_branch => $request->{branchcode}
448
                          || $request->{borrowerbranch},
449
                        item_level   => 1,
450
                        reservedate  => $request->{reservedate},
451
                        reservenotes => $request->{reservenotes},
452
                    };
453
                    $allocated_items{ $request->{itemnumber} }++;
454
                    $num_items_remaining--;
455
                }
400
            }
456
            }
401
        } else {
457
        }
458
        else {
402
            # it's title-level request that will take up one item
459
            # it's title-level request that will take up one item
403
            $num_items_remaining--;
460
            $num_items_remaining--;
404
        }
461
        }
Lines 415-432 sub MapItemsToHoldRequests { Link Here
415
    return \%item_map unless keys %items_by_branch;
472
    return \%item_map unless keys %items_by_branch;
416
473
417
    # now handle the title-level requests
474
    # now handle the title-level requests
418
    $num_items_remaining = scalar(@$available_items) - scalar(keys %allocated_items);
475
    $num_items_remaining =
476
      scalar(@$available_items) - scalar( keys %allocated_items );
419
    my $pull_branches;
477
    my $pull_branches;
420
    foreach my $request (@$hold_requests) {
478
    foreach my $request (@$hold_requests) {
421
        last if $num_items_remaining == 0;
479
        last if $num_items_remaining == 0;
422
        next if defined($request->{itemnumber}); # already handled these
480
        next if defined( $request->{itemnumber} );    # already handled these
423
481
424
        # look for local match first
482
        # look for local match first
425
        my $pickup_branch = $request->{branchcode} || $request->{borrowerbranch};
483
        my $pickup_branch =
426
        my ($itemnumber, $holdingbranch);
484
          $request->{branchcode} || $request->{borrowerbranch};
485
        my ( $itemnumber, $holdingbranch );
486
487
        # Used to detect if no Items can be transferred to the pickup location.
488
        my $branchTransfersAllowedCount =
489
          1;    #Default this to 1. It is decremented when branchTransfers fail.
427
490
428
        my $holding_branch_items = $items_by_branch{$pickup_branch};
491
        my $holding_branch_items = $items_by_branch{$pickup_branch};
429
        if ( $holding_branch_items ) {
492
        if ($holding_branch_items) {
430
            foreach my $item (@$holding_branch_items) {
493
            foreach my $item (@$holding_branch_items) {
431
                if ( $request->{borrowerbranch} eq $item->{homebranch} ) {
494
                if ( $request->{borrowerbranch} eq $item->{homebranch} ) {
432
                    $itemnumber = $item->{itemnumber};
495
                    $itemnumber = $item->{itemnumber};
Lines 437-467 sub MapItemsToHoldRequests { Link Here
437
            $itemnumber ||= $holding_branch_items->[0]->{itemnumber};
500
            $itemnumber ||= $holding_branch_items->[0]->{itemnumber};
438
        }
501
        }
439
        elsif ($transport_cost_matrix) {
502
        elsif ($transport_cost_matrix) {
440
            $pull_branches = [keys %items_by_branch];
503
            $pull_branches = [ keys %items_by_branch ];
441
            $holdingbranch = least_cost_branch( $pickup_branch, $pull_branches, $transport_cost_matrix );
504
            $holdingbranch = least_cost_branch( $pickup_branch, $pull_branches,
442
            if ( $holdingbranch ) {
505
                $transport_cost_matrix );
506
            if ($holdingbranch) {
443
507
444
                my $holding_branch_items = $items_by_branch{$holdingbranch};
508
                my $holding_branch_items = $items_by_branch{$holdingbranch};
509
                $branchTransfersAllowedCount = @$holding_branch_items;
510
511
# Making sure to put preference to the Item, which is originally from the Borrowers homebranch.
445
                foreach my $item (@$holding_branch_items) {
512
                foreach my $item (@$holding_branch_items) {
446
                    next if $request->{borrowerbranch} ne $item->{homebranch};
513
                    next if $request->{borrowerbranch} ne $item->{homebranch};
447
514
448
                    $itemnumber = $item->{itemnumber};
515
                    $itemnumber = $item->{itemnumber};
449
                    last;
516
                    last;
450
                }
517
                }
518
519
                # Checking if the Item can be Transferred to the pickup library
520
                if ( !$itemnumber
521
                    && C4::Context->preference("UseBranchTransferLimits") == 1 )
522
                {
523
                    foreach my $item (@$holding_branch_items) {
524
                        my ( $transferOk, $errorMsg ) =
525
                          C4::Circulation::CanItemBeTransferred( $pickup_branch,
526
                            $item->{holdingbranch},
527
                            $item, undef );
528
                        if ( !$transferOk ) {
529
                            $branchTransfersAllowedCount--;
530
                            next;
531
                        }
532
533
                        $itemnumber = $item->{itemnumber};
534
                        last;
535
                    }
536
                }
537
538
# If one is not using UseBranchTransferLimits, then revert to the default setting.
539
                elsif ( !$itemnumber ) {
540
541
# FIXME Choosing the Item based on the first available, even if it could be on hold.
542
#  Problematic since holds tend to stack on this first item then.
543
                    $itemnumber ||= $holding_branch_items->[0]->{itemnumber};
544
                }
451
            }
545
            }
452
            else {
546
            else {
453
                warn "No transport costs for $pickup_branch";
547
                warn "No transport costs for $pickup_branch";
454
            }
548
            }
455
        }
549
        }
456
550
457
        unless ($itemnumber) {
551
        if ( ( !$itemnumber ) && $branchTransfersAllowedCount > 0 ) {
458
            # not found yet, fall back to basics
552
553
       # not found yet and branchTransfers are not blocking, fall back to basics
459
            if ($branches_to_use) {
554
            if ($branches_to_use) {
460
                $pull_branches = $branches_to_use;
555
                $pull_branches = $branches_to_use;
461
            } else {
462
                $pull_branches = [keys %items_by_branch];
463
            }
556
            }
464
            PULL_BRANCHES:
557
            else {
558
                $pull_branches = [ keys %items_by_branch ];
559
            }
560
          PULL_BRANCHES:
465
            foreach my $branch (@$pull_branches) {
561
            foreach my $branch (@$pull_branches) {
466
                my $holding_branch_items = $items_by_branch{$branch}
562
                my $holding_branch_items = $items_by_branch{$branch}
467
                  or next;
563
                  or next;
Lines 470-486 sub MapItemsToHoldRequests { Link Here
470
                foreach my $item (@$holding_branch_items) {
566
                foreach my $item (@$holding_branch_items) {
471
                    next if $pickup_branch ne $item->{homebranch};
567
                    next if $pickup_branch ne $item->{homebranch};
472
568
473
                    $itemnumber = $item->{itemnumber};
569
                    $itemnumber    = $item->{itemnumber};
474
                    $holdingbranch = $branch;
570
                    $holdingbranch = $branch;
475
                    last PULL_BRANCHES;
571
                    last PULL_BRANCHES;
476
                }
572
                }
477
            }
573
            }
478
574
479
            unless ( $itemnumber ) {
575
            unless ($itemnumber) {
480
                foreach my $current_item ( @{ $items_by_branch{$holdingbranch} } ) {
576
                foreach
481
                    if ( $holdingbranch && ( $current_item->{holdallowed} == 2 || $pickup_branch eq $current_item->{homebranch} ) ) {
577
                  my $current_item ( @{ $items_by_branch{$holdingbranch} } )
578
                {
579
                    if (
580
                        $holdingbranch
581
                        && (   $current_item->{holdallowed} == 2
582
                            || $pickup_branch eq $current_item->{homebranch} )
583
                      )
584
                    {
482
                        $itemnumber = $current_item->{itemnumber};
585
                        $itemnumber = $current_item->{itemnumber};
483
                        last; # quit this loop as soon as we have a suitable item
586
                        last
587
                          ;  # quit this loop as soon as we have a suitable item
484
                    }
588
                    }
485
                }
589
                }
486
            }
590
            }
Lines 489-505 sub MapItemsToHoldRequests { Link Here
489
        if ($itemnumber) {
593
        if ($itemnumber) {
490
            my $holding_branch_items = $items_by_branch{$holdingbranch}
594
            my $holding_branch_items = $items_by_branch{$holdingbranch}
491
              or die "Have $itemnumber, $holdingbranch, but no items!";
595
              or die "Have $itemnumber, $holdingbranch, but no items!";
492
            @$holding_branch_items = grep { $_->{itemnumber} != $itemnumber } @$holding_branch_items;
596
            @$holding_branch_items =
493
            delete $items_by_branch{$holdingbranch} unless @$holding_branch_items;
597
              grep { $_->{itemnumber} != $itemnumber } @$holding_branch_items;
598
            delete $items_by_branch{$holdingbranch}
599
              unless @$holding_branch_items;
494
600
495
            $item_map{$itemnumber} = {
601
            $item_map{$itemnumber} = {
496
                borrowernumber => $request->{borrowernumber},
602
                borrowernumber => $request->{borrowernumber},
497
                biblionumber => $request->{biblionumber},
603
                biblionumber   => $request->{biblionumber},
498
                holdingbranch => $holdingbranch,
604
                holdingbranch  => $holdingbranch,
499
                pickup_branch => $pickup_branch,
605
                pickup_branch  => $pickup_branch,
500
                item_level => 0,
606
                item_level     => 0,
501
                reservedate => $request->{reservedate},
607
                reservedate    => $request->{reservedate},
502
                reservenotes => $request->{reservenotes},
608
                reservenotes   => $request->{reservenotes},
503
            };
609
            };
504
            $num_items_remaining--;
610
            $num_items_remaining--;
505
        }
611
        }
Lines 516-554 sub CreatePicklistFromItemMap { Link Here
516
622
517
    my $dbh = C4::Context->dbh;
623
    my $dbh = C4::Context->dbh;
518
624
519
    my $sth_load=$dbh->prepare("
625
    my $sth_load = $dbh->prepare( "
520
        INSERT INTO tmp_holdsqueue (biblionumber,itemnumber,barcode,surname,firstname,phone,borrowernumber,
626
        INSERT INTO tmp_holdsqueue (biblionumber,itemnumber,barcode,surname,firstname,phone,borrowernumber,
521
                                    cardnumber,reservedate,title, itemcallnumber,
627
                                    cardnumber,reservedate,title, itemcallnumber,
522
                                    holdingbranch,pickbranch,notes, item_level_request)
628
                                    holdingbranch,pickbranch,notes, item_level_request)
523
        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
629
        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
524
    ");
630
    " );
525
631
526
    foreach my $itemnumber  (sort keys %$item_map) {
632
    foreach my $itemnumber ( sort keys %$item_map ) {
527
        my $mapped_item = $item_map->{$itemnumber};
633
        my $mapped_item    = $item_map->{$itemnumber};
528
        my $biblionumber = $mapped_item->{biblionumber};
634
        my $biblionumber   = $mapped_item->{biblionumber};
529
        my $borrowernumber = $mapped_item->{borrowernumber};
635
        my $borrowernumber = $mapped_item->{borrowernumber};
530
        my $pickbranch = $mapped_item->{pickup_branch};
636
        my $pickbranch     = $mapped_item->{pickup_branch};
531
        my $holdingbranch = $mapped_item->{holdingbranch};
637
        my $holdingbranch  = $mapped_item->{holdingbranch};
532
        my $reservedate = $mapped_item->{reservedate};
638
        my $reservedate    = $mapped_item->{reservedate};
533
        my $reservenotes = $mapped_item->{reservenotes};
639
        my $reservenotes   = $mapped_item->{reservenotes};
534
        my $item_level = $mapped_item->{item_level};
640
        my $item_level     = $mapped_item->{item_level};
535
641
536
        my $item = GetItem($itemnumber);
642
        my $item           = GetItem($itemnumber);
537
        my $barcode = $item->{barcode};
643
        my $barcode        = $item->{barcode};
538
        my $itemcallnumber = $item->{itemcallnumber};
644
        my $itemcallnumber = $item->{itemcallnumber};
539
645
540
        my $borrower = GetMember('borrowernumber'=>$borrowernumber);
646
        my $borrower   = GetMember( 'borrowernumber' => $borrowernumber );
541
        my $cardnumber = $borrower->{'cardnumber'};
647
        my $cardnumber = $borrower->{'cardnumber'};
542
        my $surname = $borrower->{'surname'};
648
        my $surname    = $borrower->{'surname'};
543
        my $firstname = $borrower->{'firstname'};
649
        my $firstname  = $borrower->{'firstname'};
544
        my $phone = $borrower->{'phone'};
650
        my $phone      = $borrower->{'phone'};
545
651
546
        my $bib = GetBiblioData($biblionumber);
652
        my $bib   = GetBiblioData($biblionumber);
547
        my $title = $bib->{title};
653
        my $title = $bib->{title};
548
654
549
        $sth_load->execute($biblionumber, $itemnumber, $barcode, $surname, $firstname, $phone, $borrowernumber,
655
        $sth_load->execute(
550
                           $cardnumber, $reservedate, $title, $itemcallnumber,
656
            $biblionumber, $itemnumber,   $barcode,        $surname,
551
                           $holdingbranch, $pickbranch, $reservenotes, $item_level);
657
            $firstname,    $phone,        $borrowernumber, $cardnumber,
658
            $reservedate,  $title,        $itemcallnumber, $holdingbranch,
659
            $pickbranch,   $reservenotes, $item_level
660
        );
552
    }
661
    }
553
}
662
}
554
663
Lines 567-576 sub AddToHoldTargetMap { Link Here
567
    );
676
    );
568
    my $sth_insert = $dbh->prepare($insert_sql);
677
    my $sth_insert = $dbh->prepare($insert_sql);
569
678
570
    foreach my $itemnumber (keys %$item_map) {
679
    foreach my $itemnumber ( keys %$item_map ) {
571
        my $mapped_item = $item_map->{$itemnumber};
680
        my $mapped_item = $item_map->{$itemnumber};
572
        $sth_insert->execute($mapped_item->{borrowernumber}, $mapped_item->{biblionumber}, $itemnumber,
681
        $sth_insert->execute(
573
                             $mapped_item->{holdingbranch}, $mapped_item->{item_level});
682
            $mapped_item->{borrowernumber}, $mapped_item->{biblionumber},
683
            $itemnumber,                    $mapped_item->{holdingbranch},
684
            $mapped_item->{item_level}
685
        );
574
    }
686
    }
575
}
687
}
576
688
Lines 589-595 sub load_branches_to_pull_from { Link Here
589
701
590
    my @branches_to_use = map _trim($_), split /,/, $static_branch_list;
702
    my @branches_to_use = map _trim($_), split /,/, $static_branch_list;
591
703
592
    @branches_to_use = shuffle(@branches_to_use) if  C4::Context->preference("RandomizeHoldsQueueWeight");
704
    @branches_to_use = shuffle(@branches_to_use)
705
      if C4::Context->preference("RandomizeHoldsQueueWeight");
593
706
594
    return \@branches_to_use;
707
    return \@branches_to_use;
595
}
708
}
Lines 597-621 sub load_branches_to_pull_from { Link Here
597
sub least_cost_branch {
710
sub least_cost_branch {
598
711
599
    #$from - arrayref
712
    #$from - arrayref
600
    my ($to, $from, $transport_cost_matrix) = @_;
713
    my ( $to, $from, $transport_cost_matrix ) = @_;
601
714
602
    # Nothing really spectacular: supply to branch, a list of potential from branches
715
# Nothing really spectacular: supply to branch, a list of potential from branches
603
    # and find the minimum from - to value from the transport_cost_matrix
716
# and find the minimum from - to value from the transport_cost_matrix
604
    return $from->[0] if @$from == 1;
717
    return $from->[0] if @$from == 1;
605
718
606
    # If the pickup library is in the list of libraries to pull from,
719
    # If the pickup library is in the list of libraries to pull from,
607
    # return that library right away, it is obviously the least costly
720
    # return that library right away, it is obviously the least costly
608
    return ($to) if any { $_ eq $to } @$from;
721
    return ($to) if any { $_ eq $to } @$from;
609
722
610
    my ($least_cost, @branch);
723
    my ( $least_cost, @branch );
611
    foreach (@$from) {
724
    foreach (@$from) {
612
        my $cell = $transport_cost_matrix->{$to}{$_};
725
        my $cell = $transport_cost_matrix->{$to}{$_};
613
        next if $cell->{disable_transfer};
726
        next if $cell->{disable_transfer};
614
727
615
        my $cost = $cell->{cost};
728
        my $cost = $cell->{cost};
616
        next unless defined $cost; # XXX should this be reported?
729
        next unless defined $cost;    # XXX should this be reported?
617
730
618
        unless (defined $least_cost) {
731
        unless ( defined $least_cost ) {
619
            $least_cost = $cost;
732
            $least_cost = $cost;
620
            push @branch, $_;
733
            push @branch, $_;
621
            next;
734
            next;
Lines 623-634 sub least_cost_branch { Link Here
623
736
624
        next if $cost > $least_cost;
737
        next if $cost > $least_cost;
625
738
626
        if ($cost == $least_cost) {
739
        if ( $cost == $least_cost ) {
627
            push @branch, $_;
740
            push @branch, $_;
628
            next;
741
            next;
629
        }
742
        }
630
743
631
        @branch = ($_);
744
        @branch     = ($_);
632
        $least_cost = $cost;
745
        $least_cost = $cost;
633
    }
746
    }
634
747
Lines 638-642 sub least_cost_branch { Link Here
638
    # return $branch[0] if @branch == 1;
751
    # return $branch[0] if @branch == 1;
639
}
752
}
640
753
641
642
1;
754
1;
(-)a/t/db_dependent/HoldsQueue_UseBranchTransferLimits.t (-1 / +326 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
=head TESTING PLAN
4
5
PRECONDITIONS|
6
-------------+
7
UseBranchTranferLimits itemtype DVD
8
IPT -> CPL -> FFL -> IPT
9
no transfers into LPL
10
11
TransportCostMatrix in use
12
Allow all, cost 1
13
14
->  pickup branch IPT  <-
15
->  Borrowers homebranch LPL <-
16
17
18
Biblio1 Items
19
Item barcode	holdingbranch	transferOK?	itemtype
20
cpl11		    CPL		        0	    	DVD
21
cpl12		    CPL		        1	    	BK
22
ffl11	    	FFL		        1	    	DVD
23
ffl12	    	FFL		        1	    	BK
24
25
Biblio2 Items
26
Item barcode	holdingbranch	transferOK?	itemtype
27
cpl21	    	CPL		        0		    DVD
28
cpl22	    	CPL		        0		    DVD
29
30
Biblio3 Items
31
Item barcode	holdingbranch	transferOK?	itemtype
32
ffl31	       	FFL     		1	    	DVD
33
ffl32	    	FFL	        	1	    	BK
34
35
---------------------
36
--- HoldsQueue.pm ---
37
---------------------
38
39
CASE1:		Title-level multi_hold on all Biblios, pickup location IPT
40
41
Expected:
42
Hold for Biblio1 gets satisfied with Items cpl12, ipt11, ipt12 or ffl1.
43
Hold for Biblio2 fails to satisfy, no messages given.
44
Hold for Biblio3 is satisfied with ffl31.
45
46
47
CASE2:		Item-level holds, pickup location IPT
48
49
Expected:
50
Item to hold	hold satisfied?
51
cpl1	    	0
52
cpl12	    	1
53
ipt11	    	1
54
ipt12	    	1
55
ffl1	    	1
56
cpl21	    	0
57
cpl22	    	0
58
cpl23	    	1
59
ffl31	    	1
60
ffl32	    	1
61
62
63
=cut
64
65
use Modern::Perl;
66
67
use C4::Context;
68
69
use Test::More tests => 9;
70
71
use C4::Circulation;
72
use C4::Record;
73
use C4::HoldsQueue;
74
use C4::Reserves;
75
use C4::HoldsQueue;
76
77
# Start transaction
78
my $dbh = C4::Context->dbh;
79
$dbh->{AutoCommit} = 0;
80
$dbh->{RaiseError} = 1;
81
82
83
##########################xxxxxxxxxxxxxxxxxxxxxxxxx###########################
84
### xxxxxxxxxxxxxxxxxxxxxxxxxx Set up the stage xxxxxxxxxxxxxxxxxxxxxxxxxx ###
85
##########################xxxxxxxxxxxxxxxxxxxxxxxxx###########################
86
87
## Set branches
88
my $desiredBranches = ['IPT', 'CPL', 'FFL', 'LPL'];
89
my $branches = C4::Branch::GetBranches;
90
91
foreach my $branchCode (@$desiredBranches) {
92
    if (exists $branches->{$branchCode}) {
93
        #Alls well and the library is here yay!
94
    }
95
    else {
96
        #le fuu! who is not using the default libraries?
97
        my $branchwhatever = C4::SQLHelper::InsertInTable("branches",{branchcode=>"$branchCode",branchname=>"Yet another branch",city=>" ",zipcode=>" "},1);
98
        print $branchwhatever;
99
    }
100
}
101
102
my %data = (
103
    cardnumber => 'CARDNUMBER42',
104
    firstname =>  'my firstname',
105
    surname => 'my surname',
106
    categorycode => 'S',
107
    branchcode => 'LPL',
108
);
109
110
my $borrowernumber = C4::Members::AddMember(%data);
111
my $borrower = C4::Members::GetMember( borrowernumber => $borrowernumber );
112
113
114
## Itemtypes
115
my $itemTypes = {  map {$_->{itemtype} => 1} C4::ItemType->all()  };
116
if (! (exists $itemTypes->{'BK'})) {
117
    $dbh->do("INSERT INTO `kohadata`.`itemtypes` (`itemtype`, `description`, `rentalcharge`, `notforloan`, `imageurl`, `summary`, `checkinmsg`, `checkinmsgtype`) VALUES ('BK', NULL, '0', NULL, NULL, NULL, NULL, 'message');");
118
}
119
if (! (exists $itemTypes->{'DVD'})) {
120
    $dbh->do("INSERT INTO `kohadata`.`itemtypes` (`itemtype`, `description`, `rentalcharge`, `notforloan`, `imageurl`, `summary`, `checkinmsg`, `checkinmsgtype`) VALUES ('DVD', NULL, '0', NULL, NULL, NULL, NULL, 'message');");
121
}
122
123
124
125
## Add example Bibliographic records
126
127
my ( $biblioNumber1, $biblioNumber2, $biblioNumber3, $discardBiblioitemnumber);
128
my $bibFramework = ''; #Using the default bibliographic framework.
129
130
my $marcxml=qq(<?xml version="1.0" encoding="UTF-8"?>
131
<record format="MARC21" type="Bibliographic">
132
  <leader>00000cim a22000004a 4500</leader>
133
  <controlfield tag="007">ss||||j|||||||</controlfield>
134
  <controlfield tag="008">       uuuu    xxk|||||||||||||||||eng|c</controlfield>
135
  <datafield tag="245" ind1="1" ind2="4">
136
    <subfield code="a">BIBLIO1</subfield>
137
  </datafield>
138
</record>
139
);
140
my $record=C4::Record::marcxml2marc($marcxml);
141
( $biblioNumber1, $discardBiblioitemnumber ) = C4::Biblio::AddBiblio( $record, $bibFramework, { defer_marc_save => 1 } );
142
143
$marcxml=qq(<?xml version="1.0" encoding="UTF-8"?>
144
<record format="MARC21" type="Bibliographic">
145
  <leader>00000cim a22000004a 4500</leader>
146
  <controlfield tag="007">ss||||j|||||||</controlfield>
147
  <controlfield tag="008">       uuuu    xxk|||||||||||||||||eng|c</controlfield>
148
  <datafield tag="245" ind1="1" ind2="4">
149
    <subfield code="a">BIBLIO2</subfield>
150
  </datafield>
151
</record>
152
);
153
$record=C4::Record::marcxml2marc($marcxml);
154
( $biblioNumber2, $discardBiblioitemnumber ) = C4::Biblio::AddBiblio( $record, $bibFramework, { defer_marc_save => 1 } );
155
156
$marcxml=qq(<?xml version="1.0" encoding="UTF-8"?>
157
<record format="MARC21" type="Bibliographic">
158
  <leader>00000cim a22000004a 4500</leader>
159
  <controlfield tag="007">ss||||j|||||||</controlfield>
160
  <controlfield tag="008">       uuuu    xxk|||||||||||||||||eng|c</controlfield>
161
  <datafield tag="245" ind1="1" ind2="4">
162
    <subfield code="a">BIBLIO3</subfield>
163
  </datafield>
164
</record>
165
);
166
$record=C4::Record::marcxml2marc($marcxml);
167
( $biblioNumber3, $discardBiblioitemnumber ) = C4::Biblio::AddBiblio( $record, $bibFramework, { defer_marc_save => 1 } );
168
169
## Add items to the mix
170
my ($discardThis, $discardThat); # Not needing biblionumber or biblioitemnumber for the added items
171
my ($cpl11, $cpl12, $ffl11, $ffl12, $cpl21, $cpl22, $ffl31, $ffl32);
172
173
($discardThis, $discardThat, $cpl11) = C4::Items::AddItem({ barcode => 'cpl11', homebranch => 'CPL', holdingbranch => 'CPL', itype => 'DVD', notforloan => '0' }, $biblioNumber1);
174
($discardThis, $discardThat, $cpl12) = C4::Items::AddItem({ barcode => 'cpl12', homebranch => 'CPL', holdingbranch => 'CPL', itype => 'BK' , notforloan => '0' }, $biblioNumber1);
175
($discardThis, $discardThat, $ffl11) = C4::Items::AddItem({ barcode => 'ffl11', homebranch => 'FFL', holdingbranch => 'FFL', itype => 'DVD', notforloan => '0' }, $biblioNumber1);
176
($discardThis, $discardThat, $ffl12) = C4::Items::AddItem({ barcode => 'ffl12', homebranch => 'FFL', holdingbranch => 'FFL', itype => 'BK' , notforloan => '0' }, $biblioNumber1);
177
178
($discardThis, $discardThat, $cpl21) = C4::Items::AddItem({ barcode => 'cpl21', homebranch => 'CPL', holdingbranch => 'CPL', itype => 'DVD', notforloan => '0' }, $biblioNumber2);
179
($discardThis, $discardThat, $cpl22) = C4::Items::AddItem({ barcode => 'cpl22', homebranch => 'CPL', holdingbranch => 'CPL', itype => 'DVD', notforloan => '0' }, $biblioNumber2);
180
181
($discardThis, $discardThat, $ffl31) = C4::Items::AddItem({ barcode => 'ffl31', homebranch => 'FFL', holdingbranch => 'FFL', itype => 'DVD', notforloan => '0' }, $biblioNumber3);
182
($discardThis, $discardThat, $ffl32) = C4::Items::AddItem({ barcode => 'ffl32', homebranch => 'FFL', holdingbranch => 'FFL', itype => 'BK' , notforloan => '0' }, $biblioNumber3);
183
184
185
## Sysprefs and cost matrix
186
C4::Context->set_preference('UseTransportCostMatrix', 1);
187
C4::Context->set_preference('item-level_itypes', 1);
188
C4::Context->set_preference("BranchTransferLimitsType", 'itemtype');
189
C4::Context->set_preference('StaticHoldsQueueWeight', 0);
190
C4::Context->set_preference('RandomizeHoldsQueueWeight', 0);
191
192
193
C4::HoldsQueue::UpdateTransportCostMatrix( [
194
            {frombranch => 'IPT', tobranch => 'CPL', cost => 1, disable_transfer => 0},
195
            {frombranch => 'IPT', tobranch => 'FFL', cost => 1, disable_transfer => 0},
196
            {frombranch => 'IPT', tobranch => 'LPL', cost => 1, disable_transfer => 0},
197
            {frombranch => 'CPL', tobranch => 'FFL', cost => 1, disable_transfer => 0},
198
            {frombranch => 'CPL', tobranch => 'IPT', cost => 0.2, disable_transfer => 0},
199
            {frombranch => 'CPL', tobranch => 'LPL', cost => 1, disable_transfer => 0},
200
            {frombranch => 'FFL', tobranch => 'IPT', cost => 1, disable_transfer => 0},
201
            {frombranch => 'FFL', tobranch => 'CPL', cost => 1, disable_transfer => 0},
202
            {frombranch => 'FFL', tobranch => 'LPL', cost => 1, disable_transfer => 0},
203
] );
204
205
206
C4::Circulation::DeleteBranchTransferLimits( 'IPT' );
207
C4::Circulation::DeleteBranchTransferLimits( 'CPL' );
208
C4::Circulation::DeleteBranchTransferLimits( 'FFL' );
209
C4::Circulation::DeleteBranchTransferLimits( 'LPL' );
210
#                CreateBranchTransferLimit(  to  ,  from,  code ); Block these transfers!
211
C4::Circulation::CreateBranchTransferLimit( 'IPT', 'CPL', 'DVD' );
212
C4::Circulation::CreateBranchTransferLimit( 'CPL', 'FFL', 'DVD' );
213
C4::Circulation::CreateBranchTransferLimit( 'FFL', 'IPT', 'DVD' );
214
C4::Circulation::CreateBranchTransferLimit( 'LPL', 'IPT', 'DVD' );
215
C4::Circulation::CreateBranchTransferLimit( 'LPL', 'CPL', 'DVD' );
216
C4::Circulation::CreateBranchTransferLimit( 'LPL', 'FFL', 'DVD' );
217
218
219
#############################xxxxxxxxxxxxxxxxxxxxxxxxx###########################
220
### xxxxxxxxxxxxxxxxxxxxxxxxxx Start running tests xxxxxxxxxxxxxxxxxxxxxxxxxx ###
221
#############################xxxxxxxxxxxxxxxxxxxxxxxxx###########################
222
223
224
   #############
225
### CASE1: Title-level multi_hold on all Biblios, pickup location IPT ###
226
   #############
227
228
## Remove existing reserves, makes debugging easier
229
$dbh->do("DELETE FROM reserves");
230
231
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber1, 'a', '',  '1' );
232
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber2, 'a', '',  '1' );
233
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber3, 'a', '',  '1' );
234
235
$dbh->do("UPDATE reserves SET reservedate = DATE_SUB( reservedate, INTERVAL 1 DAY )");
236
237
## Item cpl11 is a DVD and transferring it from CPL to IPT is strictly forbidden.
238
## Transport matrix favours CPL -> IPT transports, so we should get either the BK or DVD.
239
my $testOk = 0;
240
my $branches_to_use = undef; #Testing only with Transport cost matrix since it is the future!
241
my $transport_cost_matrix = C4::HoldsQueue::TransportCostMatrix();
242
my $hold_requests   = C4::HoldsQueue::GetPendingHoldRequestsForBib($biblioNumber1);
243
my $available_items = C4::HoldsQueue::GetItemsAvailableToFillHoldRequestsForBib($biblioNumber1, $branches_to_use);
244
my $item_map = C4::HoldsQueue::MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
245
my $rib = (C4::Items::GetItem(  (keys %$item_map)[0]  ))->{barcode}; #Reserved Item's barcode
246
$testOk = 1 if ($rib eq 'cpl12');
247
is($testOk, 1, 'Title-level hold, mixed material, Biblio1');
248
=head
249
## Should fail, requesting DVD's from a transfer-denied branch.
250
$testOk = 0;
251
$branches_to_use = undef; #Testing only with Transport cost matrix since it is the future!
252
$transport_cost_matrix = C4::HoldsQueue::TransportCostMatrix();
253
$hold_requests   = C4::HoldsQueue::GetPendingHoldRequestsForBib($biblioNumber2);
254
$available_items = C4::HoldsQueue::GetItemsAvailableToFillHoldRequestsForBib($biblioNumber2, $branches_to_use);
255
$item_map = C4::HoldsQueue::MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
256
$testOk = %$item_map; #This time we should have no results
257
is($testOk, 0, 'Title-level hold, transfer denied, Biblio2');
258
259
## Should succeed, no transfer blocks
260
$testOk = 0;
261
$branches_to_use = undef; #Testing only with Transport cost matrix since it is the future!
262
$transport_cost_matrix = C4::HoldsQueue::TransportCostMatrix();
263
$hold_requests   = C4::HoldsQueue::GetPendingHoldRequestsForBib($biblioNumber3);
264
$available_items = C4::HoldsQueue::GetItemsAvailableToFillHoldRequestsForBib($biblioNumber3, $branches_to_use);
265
$item_map = C4::HoldsQueue::MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
266
$testOk = keys %$item_map; #This time any result will do
267
is($testOk, 1, 'Title-level hold, transfer allowed, Biblio3');
268
=cut
269
270
271
272
   #############
273
### CASE2: Item-level holds, pickup location IPT ###
274
   #############
275
276
## Remove existing reserves, makes debugging easier
277
$dbh->do("DELETE FROM reserves");
278
279
#$branch, $borrowernumber, $biblionumber, $constraint, $bibitems,  $priority, $resdate, $expdate, $notes, $title, $checkitem, $found
280
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber1, undef, undef,  '1', undef, undef, undef, undef, $cpl11, undef );
281
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber1, undef, undef,  '1', undef, undef, undef, undef, $cpl12, undef );
282
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber1, undef, undef,  '1', undef, undef, undef, undef, $ffl11, undef );
283
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber1, undef, undef,  '1', undef, undef, undef, undef, $ffl12, undef );
284
285
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber2, undef, undef,  '1', undef, undef, undef, undef, $cpl21, undef );
286
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber2, undef, undef,  '1', undef, undef, undef, undef, $cpl22, undef );
287
288
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber3, undef, undef,  '1', undef, undef, undef, undef, $ffl31, undef );
289
C4::Reserves::AddReserve ( 'IPT', $borrowernumber, $biblioNumber3, undef, undef,  '1', undef, undef, undef, undef, $ffl32, undef );
290
291
$dbh->do("UPDATE reserves SET reservedate = DATE_SUB( reservedate, INTERVAL 1 DAY )");
292
293
## Transfer of cpl11 should be blocked, other 3 Items are mapped
294
$testOk = 0;
295
$branches_to_use = undef; #Testing only with Transport cost matrix since it is the future!
296
$transport_cost_matrix = C4::HoldsQueue::TransportCostMatrix();
297
$hold_requests   = C4::HoldsQueue::GetPendingHoldRequestsForBib($biblioNumber1);
298
$available_items = C4::HoldsQueue::GetItemsAvailableToFillHoldRequestsForBib($biblioNumber1, $branches_to_use);
299
$item_map = C4::HoldsQueue::MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
300
is( (exists $item_map->{$cpl11}) , '', 'Item cpl11 transfer denied');
301
is( (exists $item_map->{$cpl12}) , 1, 'Item cpl12 transfer allowed');
302
is( (exists $item_map->{$ffl11}) , 1, 'Item cpl11 transfer allowed');
303
is( (exists $item_map->{$ffl12}) , 1, 'Item cpl11 transfer allowed');
304
305
## All items should fail their transfer
306
$testOk = 0;
307
$branches_to_use = undef; #Testing only with Transport cost matrix since it is the future!
308
$transport_cost_matrix = C4::HoldsQueue::TransportCostMatrix();
309
$hold_requests   = C4::HoldsQueue::GetPendingHoldRequestsForBib($biblioNumber2);
310
$available_items = C4::HoldsQueue::GetItemsAvailableToFillHoldRequestsForBib($biblioNumber2, $branches_to_use);
311
$item_map = C4::HoldsQueue::MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
312
is( (exists $item_map->{$cpl21}) , '', 'Item cpl21 transfer denied');
313
is( (exists $item_map->{$cpl22}) , '', 'Item cpl22 transfer denied');
314
315
## Should succeed, no transfer blocks
316
$testOk = 0;
317
$branches_to_use = undef; #Testing only with Transport cost matrix since it is the future!
318
$transport_cost_matrix = C4::HoldsQueue::TransportCostMatrix();
319
$hold_requests   = C4::HoldsQueue::GetPendingHoldRequestsForBib($biblioNumber3);
320
$available_items = C4::HoldsQueue::GetItemsAvailableToFillHoldRequestsForBib($biblioNumber3, $branches_to_use);
321
$item_map = C4::HoldsQueue::MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
322
is( (exists $item_map->{$ffl31}) , 1, 'Item ffl31 transfer allowed');
323
is( (exists $item_map->{$ffl32}) , 1, 'Item ffl32 transfer allowed');
324
325
# Cleanup
326
$dbh->rollback();

Return to bug 11024