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

(-)a/C4/Circulation.pm (-958 / +1340 lines)
Lines 18-25 package C4::Circulation; Link Here
18
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# with Koha; if not, write to the Free Software Foundation, Inc.,
19
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
20
21
22
use strict;
21
use strict;
22
23
#use warnings; FIXME - Bug 2505
23
#use warnings; FIXME - Bug 2505
24
use DateTime;
24
use DateTime;
25
use C4::Context;
25
use C4::Context;
Lines 34-45 use C4::Accounts; Link Here
34
use C4::ItemCirculationAlertPreference;
34
use C4::ItemCirculationAlertPreference;
35
use C4::Message;
35
use C4::Message;
36
use C4::Debug;
36
use C4::Debug;
37
use C4::Branch; # GetBranches
37
use C4::Branch;    # GetBranches
38
use C4::Log; # logaction
38
use C4::Log;       # logaction
39
use C4::Koha qw(
39
use C4::Koha qw(
40
    GetAuthorisedValueByCode
40
  GetAuthorisedValueByCode
41
    GetAuthValCode
41
  GetAuthValCode
42
    GetKohaAuthorisedValueLib
42
  GetKohaAuthorisedValueLib
43
);
43
);
44
use C4::Overdues qw(CalcFine UpdateFine);
44
use C4::Overdues qw(CalcFine UpdateFine);
45
use Algorithm::CheckDigits;
45
use Algorithm::CheckDigits;
Lines 62-115 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); Link Here
62
62
63
BEGIN {
63
BEGIN {
64
    require Exporter;
64
    require Exporter;
65
    $VERSION = 3.07.00.049;	# for version checking
65
    $VERSION = 3.07.00.049;    # for version checking
66
    @ISA    = qw(Exporter);
66
    @ISA     = qw(Exporter);
67
67
68
    # FIXME subs that should probably be elsewhere
68
    # FIXME subs that should probably be elsewhere
69
    push @EXPORT, qw(
69
    push @EXPORT, qw(
70
        &barcodedecode
70
      &barcodedecode
71
        &LostItem
71
      &LostItem
72
        &ReturnLostItem
72
      &ReturnLostItem
73
    );
73
    );
74
74
75
    # subs to deal with issuing a book
75
    # subs to deal with issuing a book
76
    push @EXPORT, qw(
76
    push @EXPORT, qw(
77
        &CanBookBeIssued
77
      &CanBookBeIssued
78
        &CanBookBeRenewed
78
      &CanBookBeRenewed
79
        &AddIssue
79
      &AddIssue
80
        &AddRenewal
80
      &AddRenewal
81
        &GetRenewCount
81
      &GetRenewCount
82
        &GetItemIssue
82
      &GetItemIssue
83
        &GetItemIssues
83
      &GetItemIssues
84
        &GetIssuingCharges
84
      &GetIssuingCharges
85
        &GetIssuingRule
85
      &GetIssuingRule
86
        &GetBranchBorrowerCircRule
86
      &GetBranchBorrowerCircRule
87
        &GetBranchItemRule
87
      &GetBranchItemRule
88
        &GetBiblioIssues
88
      &GetBiblioIssues
89
        &GetOpenIssue
89
      &GetOpenIssue
90
        &AnonymiseIssueHistory
90
      &AnonymiseIssueHistory
91
        &CheckIfIssuedToPatron
91
      &CheckIfIssuedToPatron
92
        &IsItemIssued
92
      &IsItemIssued
93
    );
93
    );
94
94
95
    # subs to deal with returns
95
    # subs to deal with returns
96
    push @EXPORT, qw(
96
    push @EXPORT, qw(
97
        &AddReturn
97
      &AddReturn
98
        &MarkIssueReturned
98
      &MarkIssueReturned
99
    );
99
    );
100
100
101
    # subs to deal with transfers
101
    # subs to deal with transfers
102
    push @EXPORT, qw(
102
    push @EXPORT, qw(
103
        &transferbook
103
      &transferbook
104
        &GetTransfers
104
      &GetTransfers
105
        &GetTransfersFromTo
105
      &GetTransfersFromTo
106
        &updateWrongTransfer
106
      &updateWrongTransfer
107
        &DeleteTransfer
107
      &DeleteTransfer
108
                &CanItemBeTransferred
108
      &CanItemBeTransferred
109
                &IsBranchTransferAllowed
109
      &IsBranchTransferAllowed
110
                &CreateBranchTransferLimit
110
      &CreateBranchTransferLimit
111
                &DeleteBranchTransferLimits
111
      &DeleteBranchTransferLimits
112
        &TransferSlip
112
      &TransferSlip
113
    );
113
    );
114
114
115
    # subs to deal with offline circulation
115
    # subs to deal with offline circulation
Lines 159-198 System Pref options. Link Here
159
# FIXME -- these plugins should be moved out of Circulation.pm
159
# FIXME -- these plugins should be moved out of Circulation.pm
160
#
160
#
161
sub barcodedecode {
161
sub barcodedecode {
162
    my ($barcode, $filter) = @_;
162
    my ( $barcode, $filter ) = @_;
163
    my $branch = C4::Branch::mybranch();
163
    my $branch = C4::Branch::mybranch();
164
    $filter = C4::Context->preference('itemBarcodeInputFilter') unless $filter;
164
    $filter = C4::Context->preference('itemBarcodeInputFilter') unless $filter;
165
    $filter or return $barcode;     # ensure filter is defined, else return untouched barcode
165
    $filter
166
    if ($filter eq 'whitespace') {
166
      or
167
      return $barcode; # ensure filter is defined, else return untouched barcode
168
    if ( $filter eq 'whitespace' ) {
167
        $barcode =~ s/\s//g;
169
        $barcode =~ s/\s//g;
168
    } elsif ($filter eq 'cuecat') {
170
    }
171
    elsif ( $filter eq 'cuecat' ) {
169
        chomp($barcode);
172
        chomp($barcode);
170
        my @fields = split( /\./, $barcode );
173
        my @fields = split( /\./, $barcode );
171
        my @results = map( decode($_), @fields[ 1 .. $#fields ] );
174
        my @results = map( decode($_), @fields[ 1 .. $#fields ] );
172
        ($#results == 2) and return $results[2];
175
        ( $#results == 2 ) and return $results[2];
173
    } elsif ($filter eq 'T-prefix') {
176
    }
174
        if ($barcode =~ /^[Tt](\d)/) {
177
    elsif ( $filter eq 'T-prefix' ) {
175
            (defined($1) and $1 eq '0') and return $barcode;
178
        if ( $barcode =~ /^[Tt](\d)/ ) {
176
            $barcode = substr($barcode, 2) + 0;     # FIXME: probably should be substr($barcode, 1)
179
            ( defined($1) and $1 eq '0' ) and return $barcode;
177
        }
180
            $barcode = substr( $barcode, 2 ) +
178
        return sprintf("T%07d", $barcode);
181
              0;    # FIXME: probably should be substr($barcode, 1)
179
        # FIXME: $barcode could be "T1", causing warning: substr outside of string
182
        }
180
        # Why drop the nonzero digit after the T?
183
        return sprintf( "T%07d", $barcode );
181
        # Why pass non-digits (or empty string) to "T%07d"?
184
182
    } elsif ($filter eq 'libsuite8') {
185
      # FIXME: $barcode could be "T1", causing warning: substr outside of string
183
        unless($barcode =~ m/^($branch)-/i){	#if barcode starts with branch code its in Koha style. Skip it.
186
      # Why drop the nonzero digit after the T?
184
            if($barcode =~ m/^(\d)/i){	#Some barcodes even start with 0's & numbers and are assumed to have b as the item type in the libsuite8 software
187
      # Why pass non-digits (or empty string) to "T%07d"?
185
                                $barcode =~ s/^[0]*(\d+)$/$branch-b-$1/i;
188
    }
186
                        }else{
189
    elsif ( $filter eq 'libsuite8' ) {
190
        unless ( $barcode =~ m/^($branch)-/i )
191
        {    #if barcode starts with branch code its in Koha style. Skip it.
192
            if ( $barcode =~ m/^(\d)/i )
193
            { #Some barcodes even start with 0's & numbers and are assumed to have b as the item type in the libsuite8 software
194
                $barcode =~ s/^[0]*(\d+)$/$branch-b-$1/i;
195
            }
196
            else {
187
                $barcode =~ s/^(\D+)[0]*(\d+)$/$branch-$1-$2/i;
197
                $barcode =~ s/^(\D+)[0]*(\d+)$/$branch-$1-$2/i;
188
            }
198
            }
189
        }
199
        }
190
    } elsif ($filter eq 'EAN13') {
200
    }
201
    elsif ( $filter eq 'EAN13' ) {
191
        my $ean = CheckDigits('ean');
202
        my $ean = CheckDigits('ean');
192
        if ( $ean->is_valid($barcode) ) {
203
        if ( $ean->is_valid($barcode) ) {
193
            #$barcode = sprintf('%013d',$barcode); # this doesn't work on 32-bit systems
204
205
    #$barcode = sprintf('%013d',$barcode); # this doesn't work on 32-bit systems
194
            $barcode = '0' x ( 13 - length($barcode) ) . $barcode;
206
            $barcode = '0' x ( 13 - length($barcode) ) . $barcode;
195
        } else {
207
        }
208
        else {
196
            warn "# [$barcode] not valid EAN-13/UPC-A\n";
209
            warn "# [$barcode] not valid EAN-13/UPC-A\n";
197
        }
210
        }
198
    }
211
    }
Lines 219-224 sub decode { Link Here
219
    my $l = ( $#s + 1 ) % 4;
232
    my $l = ( $#s + 1 ) % 4;
220
    if ($l) {
233
    if ($l) {
221
        if ( $l == 1 ) {
234
        if ( $l == 1 ) {
235
222
            # warn "Error: Cuecat decode parsing failed!";
236
            # warn "Error: Cuecat decode parsing failed!";
223
            return;
237
            return;
224
        }
238
        }
Lines 230-237 sub decode { Link Here
230
        my $n = ( ( $s[0] << 6 | $s[1] ) << 6 | $s[2] ) << 6 | $s[3];
244
        my $n = ( ( $s[0] << 6 | $s[1] ) << 6 | $s[2] ) << 6 | $s[3];
231
        $r .=
245
        $r .=
232
            chr( ( $n >> 16 ) ^ 67 )
246
            chr( ( $n >> 16 ) ^ 67 )
233
         .chr( ( $n >> 8 & 255 ) ^ 67 )
247
          . chr( ( $n >> 8 & 255 ) ^ 67 )
234
         .chr( ( $n & 255 ) ^ 67 );
248
          . chr( ( $n & 255 ) ^ 67 );
235
        @s = @s[ 4 .. $#s ];
249
        @s = @s[ 4 .. $#s ];
236
    }
250
    }
237
    $r = substr( $r, 0, length($r) - $l );
251
    $r = substr( $r, 0, length($r) - $l );
Lines 299-310 The item was eligible to be transferred. Barring problems communicating with the Link Here
299
sub transferbook {
313
sub transferbook {
300
    my ( $tbr, $barcode, $ignoreRs ) = @_;
314
    my ( $tbr, $barcode, $ignoreRs ) = @_;
301
    my $messages;
315
    my $messages;
302
    my $dotransfer      = 1;
316
    my $dotransfer = 1;
303
    my $branches        = GetBranches();
317
    my $branches   = GetBranches();
304
    my $item = GetItem(undef,$barcode,undef);
318
    my $item       = GetItem( undef, $barcode, undef );
305
    my $itemnumber = $item->{itemnumber};
319
    my $itemnumber = $item->{itemnumber};
306
    my $issue      = GetItemIssue($itemnumber);
320
    my $issue      = GetItemIssue($itemnumber);
307
    my $biblio = GetBiblioFromItemNumber($itemnumber);
321
    my $biblio     = GetBiblioFromItemNumber($itemnumber);
308
322
309
    # bad barcode..
323
    # bad barcode..
310
    if ( not $itemnumber ) {
324
    if ( not $itemnumber ) {
Lines 318-325 sub transferbook { Link Here
318
332
319
    # if using Branch Transfer Limits
333
    # if using Branch Transfer Limits
320
    my $errMsg;
334
    my $errMsg;
321
    ($dotransfer, $errMsg) = CanItemBeTransferred( $tbr, $fbr, $item, $biblio );
335
    ( $dotransfer, $errMsg ) =
322
    if ( ! $dotransfer ) {
336
      CanItemBeTransferred( $tbr, $fbr, $item, $biblio );
337
    if ( !$dotransfer ) {
323
        $messages->{'NotAllowed'} = $errMsg;
338
        $messages->{'NotAllowed'} = $errMsg;
324
    }
339
    }
325
340
Lines 336-350 sub transferbook { Link Here
336
    }
351
    }
337
352
338
    # check if it is still issued to someone, return it...
353
    # check if it is still issued to someone, return it...
339
    if ($issue->{borrowernumber}) {
354
    if ( $issue->{borrowernumber} ) {
340
        AddReturn( $barcode, $fbr );
355
        AddReturn( $barcode, $fbr );
341
        $messages->{'WasReturned'} = $issue->{borrowernumber};
356
        $messages->{'WasReturned'} = $issue->{borrowernumber};
342
    }
357
    }
343
358
344
    # find reserves.....
359
    # find reserves.....
345
    # That'll save a database query.
360
    # That'll save a database query.
346
    my ( $resfound, $resrec, undef ) =
361
    my ( $resfound, $resrec, undef ) = CheckReserves($itemnumber);
347
      CheckReserves( $itemnumber );
348
    if ( $resfound and not $ignoreRs ) {
362
    if ( $resfound and not $ignoreRs ) {
349
        $resrec->{'ResFound'} = $resfound;
363
        $resrec->{'ResFound'} = $resfound;
350
364
Lines 360-408 sub transferbook { Link Here
360
        $messages->{'WasTransfered'} = 1;
374
        $messages->{'WasTransfered'} = 1;
361
375
362
    }
376
    }
363
    ModDateLastSeen( $itemnumber );
377
    ModDateLastSeen($itemnumber);
364
    return ( $dotransfer, $messages, $biblio );
378
    return ( $dotransfer, $messages, $biblio );
365
}
379
}
366
380
367
368
sub TooMany {
381
sub TooMany {
369
    my $borrower        = shift;
382
    my $borrower     = shift;
370
    my $biblionumber = shift;
383
    my $biblionumber = shift;
371
    my $item		= shift;
384
    my $item         = shift;
372
    my $cat_borrower    = $borrower->{'categorycode'};
385
    my $cat_borrower = $borrower->{'categorycode'};
373
    my $dbh             = C4::Context->dbh;
386
    my $dbh          = C4::Context->dbh;
374
    my $branch;
387
    my $branch;
388
375
    # Get which branchcode we need
389
    # Get which branchcode we need
376
    $branch = _GetCircControlBranch($item,$borrower);
390
    $branch = _GetCircControlBranch( $item, $borrower );
377
    my $type = (C4::Context->preference('item-level_itypes'))
391
    my $type = ( C4::Context->preference('item-level_itypes') )
378
            ? $item->{'itype'}         # item-level
392
      ? $item->{'itype'}        # item-level
379
            : $item->{'itemtype'};     # biblio-level
393
      : $item->{'itemtype'};    # biblio-level
380
 
394
381
    # given branch, patron category, and item type, determine
395
    # given branch, patron category, and item type, determine
382
    # applicable issuing rule
396
    # applicable issuing rule
383
    my $issuing_rule = GetIssuingRule($cat_borrower, $type, $branch);
397
    my $issuing_rule = GetIssuingRule( $cat_borrower, $type, $branch );
384
398
385
    # if a rule is found and has a loan limit set, count
399
    # if a rule is found and has a loan limit set, count
386
    # how many loans the patron already has that meet that
400
    # how many loans the patron already has that meet that
387
    # rule
401
    # rule
388
    if (defined($issuing_rule) and defined($issuing_rule->{'maxissueqty'})) {
402
    if ( defined($issuing_rule) and defined( $issuing_rule->{'maxissueqty'} ) )
403
    {
389
        my @bind_params;
404
        my @bind_params;
390
        my $count_query = "SELECT COUNT(*) FROM issues
405
        my $count_query = "SELECT COUNT(*) FROM issues
391
                           JOIN items USING (itemnumber) ";
406
                           JOIN items USING (itemnumber) ";
392
407
393
        my $rule_itemtype = $issuing_rule->{itemtype};
408
        my $rule_itemtype = $issuing_rule->{itemtype};
394
        if ($rule_itemtype eq "*") {
409
        if ( $rule_itemtype eq "*" ) {
410
395
            # matching rule has the default item type, so count only
411
            # matching rule has the default item type, so count only
396
            # those existing loans that don't fall under a more
412
            # those existing loans that don't fall under a more
397
            # specific rule
413
            # specific rule
398
            if (C4::Context->preference('item-level_itypes')) {
414
            if ( C4::Context->preference('item-level_itypes') ) {
399
                $count_query .= " WHERE items.itype NOT IN (
415
                $count_query .= " WHERE items.itype NOT IN (
400
                                    SELECT itemtype FROM issuingrules
416
                                    SELECT itemtype FROM issuingrules
401
                                    WHERE branchcode = ?
417
                                    WHERE branchcode = ?
402
                                    AND   (categorycode = ? OR categorycode = ?)
418
                                    AND   (categorycode = ? OR categorycode = ?)
403
                                    AND   itemtype <> '*'
419
                                    AND   itemtype <> '*'
404
                                  ) ";
420
                                  ) ";
405
            } else { 
421
            }
422
            else {
406
                $count_query .= " JOIN  biblioitems USING (biblionumber) 
423
                $count_query .= " JOIN  biblioitems USING (biblionumber) 
407
                                  WHERE biblioitems.itemtype NOT IN (
424
                                  WHERE biblioitems.itemtype NOT IN (
408
                                    SELECT itemtype FROM issuingrules
425
                                    SELECT itemtype FROM issuingrules
Lines 414-425 sub TooMany { Link Here
414
            push @bind_params, $issuing_rule->{branchcode};
431
            push @bind_params, $issuing_rule->{branchcode};
415
            push @bind_params, $issuing_rule->{categorycode};
432
            push @bind_params, $issuing_rule->{categorycode};
416
            push @bind_params, $cat_borrower;
433
            push @bind_params, $cat_borrower;
417
        } else {
434
        }
435
        else {
418
            # rule has specific item type, so count loans of that
436
            # rule has specific item type, so count loans of that
419
            # specific item type
437
            # specific item type
420
            if (C4::Context->preference('item-level_itypes')) {
438
            if ( C4::Context->preference('item-level_itypes') ) {
421
                $count_query .= " WHERE items.itype = ? ";
439
                $count_query .= " WHERE items.itype = ? ";
422
            } else { 
440
            }
441
            else {
423
                $count_query .= " JOIN  biblioitems USING (biblionumber) 
442
                $count_query .= " JOIN  biblioitems USING (biblionumber) 
424
                                  WHERE biblioitems.itemtype= ? ";
443
                                  WHERE biblioitems.itemtype= ? ";
425
            }
444
            }
Lines 429-441 sub TooMany { Link Here
429
        $count_query .= " AND borrowernumber = ? ";
448
        $count_query .= " AND borrowernumber = ? ";
430
        push @bind_params, $borrower->{'borrowernumber'};
449
        push @bind_params, $borrower->{'borrowernumber'};
431
        my $rule_branch = $issuing_rule->{branchcode};
450
        my $rule_branch = $issuing_rule->{branchcode};
432
        if ($rule_branch ne "*") {
451
        if ( $rule_branch ne "*" ) {
433
            if (C4::Context->preference('CircControl') eq 'PickupLibrary') {
452
            if ( C4::Context->preference('CircControl') eq 'PickupLibrary' ) {
434
                $count_query .= " AND issues.branchcode = ? ";
453
                $count_query .= " AND issues.branchcode = ? ";
435
                push @bind_params, $branch;
454
                push @bind_params, $branch;
436
            } elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') {
455
            }
456
            elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary' )
457
            {
437
                ; # if branch is the patron's home branch, then count all loans by patron
458
                ; # if branch is the patron's home branch, then count all loans by patron
438
            } else {
459
            }
460
            else {
439
                $count_query .= " AND items.homebranch = ? ";
461
                $count_query .= " AND items.homebranch = ? ";
440
                push @bind_params, $branch;
462
                push @bind_params, $branch;
441
            }
463
            }
Lines 446-471 sub TooMany { Link Here
446
        my ($current_loan_count) = $count_sth->fetchrow_array;
468
        my ($current_loan_count) = $count_sth->fetchrow_array;
447
469
448
        my $max_loans_allowed = $issuing_rule->{'maxissueqty'};
470
        my $max_loans_allowed = $issuing_rule->{'maxissueqty'};
449
        if ($current_loan_count >= $max_loans_allowed) {
471
        if ( $current_loan_count >= $max_loans_allowed ) {
450
            return ($current_loan_count, $max_loans_allowed);
472
            return ( $current_loan_count, $max_loans_allowed );
451
        }
473
        }
452
    }
474
    }
453
475
454
    # Now count total loans against the limit for the branch
476
    # Now count total loans against the limit for the branch
455
    my $branch_borrower_circ_rule = GetBranchBorrowerCircRule($branch, $cat_borrower);
477
    my $branch_borrower_circ_rule =
456
    if (defined($branch_borrower_circ_rule->{maxissueqty})) {
478
      GetBranchBorrowerCircRule( $branch, $cat_borrower );
457
        my @bind_params = ();
479
    if ( defined( $branch_borrower_circ_rule->{maxissueqty} ) ) {
480
        my @bind_params        = ();
458
        my $branch_count_query = "SELECT COUNT(*) FROM issues
481
        my $branch_count_query = "SELECT COUNT(*) FROM issues
459
                                  JOIN items USING (itemnumber)
482
                                  JOIN items USING (itemnumber)
460
                                  WHERE borrowernumber = ? ";
483
                                  WHERE borrowernumber = ? ";
461
        push @bind_params, $borrower->{borrowernumber};
484
        push @bind_params, $borrower->{borrowernumber};
462
485
463
        if (C4::Context->preference('CircControl') eq 'PickupLibrary') {
486
        if ( C4::Context->preference('CircControl') eq 'PickupLibrary' ) {
464
            $branch_count_query .= " AND issues.branchcode = ? ";
487
            $branch_count_query .= " AND issues.branchcode = ? ";
465
            push @bind_params, $branch;
488
            push @bind_params, $branch;
466
        } elsif (C4::Context->preference('CircControl') eq 'PatronLibrary') {
489
        }
490
        elsif ( C4::Context->preference('CircControl') eq 'PatronLibrary' ) {
467
            ; # if branch is the patron's home branch, then count all loans by patron
491
            ; # if branch is the patron's home branch, then count all loans by patron
468
        } else {
492
        }
493
        else {
469
            $branch_count_query .= " AND items.homebranch = ? ";
494
            $branch_count_query .= " AND items.homebranch = ? ";
470
            push @bind_params, $branch;
495
            push @bind_params, $branch;
471
        }
496
        }
Lines 474-481 sub TooMany { Link Here
474
        my ($current_loan_count) = $branch_count_sth->fetchrow_array;
499
        my ($current_loan_count) = $branch_count_sth->fetchrow_array;
475
500
476
        my $max_loans_allowed = $branch_borrower_circ_rule->{maxissueqty};
501
        my $max_loans_allowed = $branch_borrower_circ_rule->{maxissueqty};
477
        if ($current_loan_count >= $max_loans_allowed) {
502
        if ( $current_loan_count >= $max_loans_allowed ) {
478
            return ($current_loan_count, $max_loans_allowed);
503
            return ( $current_loan_count, $max_loans_allowed );
479
        }
504
        }
480
    }
505
    }
481
506
Lines 562-571 sub itemissues { Link Here
562
            $data->{'borrower'} = $data2->{'borrowernumber'};
587
            $data->{'borrower'} = $data2->{'borrowernumber'};
563
        }
588
        }
564
        else {
589
        else {
565
            $data->{'date_due'} = ($data->{'withdrawn'} eq '1') ? 'Cancelled' : 'Available';
590
            $data->{'date_due'} =
591
              ( $data->{'withdrawn'} eq '1' ) ? 'Cancelled' : 'Available';
566
        }
592
        }
567
593
568
569
        # Find the last 3 people who borrowed this item.
594
        # Find the last 3 people who borrowed this item.
570
        $sth2 = $dbh->prepare(
595
        $sth2 = $dbh->prepare(
571
            "SELECT * FROM old_issues
596
            "SELECT * FROM old_issues
Lines 688-701 if the borrower borrows to much things Link Here
688
sub CanBookBeIssued {
713
sub CanBookBeIssued {
689
    my ( $borrower, $barcode, $duedate, $inprocess, $ignore_reserves ) = @_;
714
    my ( $borrower, $barcode, $duedate, $inprocess, $ignore_reserves ) = @_;
690
    my %needsconfirmation;    # filled with problems that needs confirmations
715
    my %needsconfirmation;    # filled with problems that needs confirmations
691
    my %issuingimpossible;    # filled with problems that causes the issue to be IMPOSSIBLE
716
    my %issuingimpossible
692
    my %alerts;               # filled with messages that shouldn't stop issuing, but the librarian should be aware of.
717
      ;    # filled with problems that causes the issue to be IMPOSSIBLE
693
718
    my %alerts
694
    my $item = GetItem(GetItemnumberFromBarcode( $barcode ));
719
      ; # filled with messages that shouldn't stop issuing, but the librarian should be aware of.
695
    my $issue = GetItemIssue($item->{itemnumber});
720
696
    my $biblioitem = GetBiblioItemData($item->{biblioitemnumber});
721
    my $item       = GetItem( GetItemnumberFromBarcode($barcode) );
697
    $item->{'itemtype'}=$item->{'itype'};
722
    my $issue      = GetItemIssue( $item->{itemnumber} );
698
    my $dbh             = C4::Context->dbh;
723
    my $biblioitem = GetBiblioItemData( $item->{biblioitemnumber} );
724
    $item->{'itemtype'} = $item->{'itype'};
725
    my $dbh = C4::Context->dbh;
699
726
700
    # MANDATORY CHECKS - unless item exists, nothing else matters
727
    # MANDATORY CHECKS - unless item exists, nothing else matters
701
    unless ( $item->{barcode} ) {
728
    unless ( $item->{barcode} ) {
Lines 706-720 sub CanBookBeIssued { Link Here
706
    #
733
    #
707
    # DUE DATE is OK ? -- should already have checked.
734
    # DUE DATE is OK ? -- should already have checked.
708
    #
735
    #
709
    if ($duedate && ref $duedate ne 'DateTime') {
736
    if ( $duedate && ref $duedate ne 'DateTime' ) {
710
        $duedate = dt_from_string($duedate);
737
        $duedate = dt_from_string($duedate);
711
    }
738
    }
712
    my $now = DateTime->now( time_zone => C4::Context->tz() );
739
    my $now = DateTime->now( time_zone => C4::Context->tz() );
713
    unless ( $duedate ) {
740
    unless ($duedate) {
714
        my $issuedate = $now->clone();
741
        my $issuedate = $now->clone();
715
742
716
        my $branch = _GetCircControlBranch($item,$borrower);
743
        my $branch = _GetCircControlBranch( $item, $borrower );
717
        my $itype = ( C4::Context->preference('item-level_itypes') ) ? $item->{'itype'} : $biblioitem->{'itemtype'};
744
        my $itype =
745
          ( C4::Context->preference('item-level_itypes') )
746
          ? $item->{'itype'}
747
          : $biblioitem->{'itemtype'};
718
        $duedate = CalcDateDue( $issuedate, $itype, $branch, $borrower );
748
        $duedate = CalcDateDue( $issuedate, $itype, $branch, $borrower );
719
749
720
        # Offline circ calls AddIssue directly, doesn't run through here
750
        # Offline circ calls AddIssue directly, doesn't run through here
Lines 722-743 sub CanBookBeIssued { Link Here
722
    }
752
    }
723
    if ($duedate) {
753
    if ($duedate) {
724
        my $today = $now->clone();
754
        my $today = $now->clone();
725
        $today->truncate( to => 'minute');
755
        $today->truncate( to => 'minute' );
726
        if (DateTime->compare($duedate,$today) == -1 ) { # duedate cannot be before now
756
        if ( DateTime->compare( $duedate, $today ) == -1 )
757
        {    # duedate cannot be before now
727
            $needsconfirmation{INVALID_DATE} = output_pref($duedate);
758
            $needsconfirmation{INVALID_DATE} = output_pref($duedate);
728
        }
759
        }
729
    } else {
760
    }
730
            $issuingimpossible{INVALID_DATE} = output_pref($duedate);
761
    else {
762
        $issuingimpossible{INVALID_DATE} = output_pref($duedate);
731
    }
763
    }
732
764
733
    #
765
    #
734
    # BORROWER STATUS
766
    # BORROWER STATUS
735
    #
767
    #
736
    if ( $borrower->{'category_type'} eq 'X' && (  $item->{barcode}  )) { 
768
    if ( $borrower->{'category_type'} eq 'X' && ( $item->{barcode} ) ) {
737
        # stats only borrower -- add entry to statistics table, and return issuingimpossible{STATS} = 1  .
769
738
        &UpdateStats(C4::Context->userenv->{'branch'},'localuse','','',$item->{'itemnumber'},$item->{'itemtype'},$borrower->{'borrowernumber'}, undef, $item->{'ccode'});
770
# stats only borrower -- add entry to statistics table, and return issuingimpossible{STATS} = 1  .
771
        &UpdateStats(
772
            C4::Context->userenv->{'branch'}, 'localuse',
773
            '',                               '',
774
            $item->{'itemnumber'},            $item->{'itemtype'},
775
            $borrower->{'borrowernumber'},    undef,
776
            $item->{'ccode'}
777
        );
739
        ModDateLastSeen( $item->{'itemnumber'} );
778
        ModDateLastSeen( $item->{'itemnumber'} );
740
        return( { STATS => 1 }, {});
779
        return ( { STATS => 1 }, {} );
741
    }
780
    }
742
    if ( $borrower->{flags}->{GNA} ) {
781
    if ( $borrower->{flags}->{GNA} ) {
743
        $issuingimpossible{GNA} = 1;
782
        $issuingimpossible{GNA} = 1;
Lines 748-770 sub CanBookBeIssued { Link Here
748
    if ( $borrower->{flags}->{'DBARRED'} ) {
787
    if ( $borrower->{flags}->{'DBARRED'} ) {
749
        $issuingimpossible{DEBARRED} = 1;
788
        $issuingimpossible{DEBARRED} = 1;
750
    }
789
    }
751
    if ( !defined $borrower->{dateexpiry} || $borrower->{'dateexpiry'} eq '0000-00-00') {
790
    if ( !defined $borrower->{dateexpiry}
791
        || $borrower->{'dateexpiry'} eq '0000-00-00' )
792
    {
752
        $issuingimpossible{EXPIRED} = 1;
793
        $issuingimpossible{EXPIRED} = 1;
753
    } else {
794
    }
754
        my ($y, $m, $d) =  split /-/,$borrower->{'dateexpiry'};
795
    else {
755
        if ($y && $m && $d) { # are we really writing oinvalid dates to borrs
796
        my ( $y, $m, $d ) = split /-/, $borrower->{'dateexpiry'};
797
        if ( $y && $m && $d ) {  # are we really writing oinvalid dates to borrs
756
            my $expiry_dt = DateTime->new(
798
            my $expiry_dt = DateTime->new(
757
                year => $y,
799
                year      => $y,
758
                month => $m,
800
                month     => $m,
759
                day   => $d,
801
                day       => $d,
760
                time_zone => C4::Context->tz,
802
                time_zone => C4::Context->tz,
761
            );
803
            );
762
            $expiry_dt->truncate( to => 'day');
804
            $expiry_dt->truncate( to => 'day' );
763
            my $today = $now->clone()->truncate(to => 'day');
805
            my $today = $now->clone()->truncate( to => 'day' );
764
            if (DateTime->compare($today, $expiry_dt) == 1) {
806
            if ( DateTime->compare( $today, $expiry_dt ) == 1 ) {
765
                $issuingimpossible{EXPIRED} = 1;
807
                $issuingimpossible{EXPIRED} = 1;
766
            }
808
            }
767
        } else {
809
        }
810
        else {
768
            carp("Invalid expity date in borr");
811
            carp("Invalid expity date in borr");
769
            $issuingimpossible{EXPIRED} = 1;
812
            $issuingimpossible{EXPIRED} = 1;
770
        }
813
        }
Lines 774-837 sub CanBookBeIssued { Link Here
774
    #
817
    #
775
818
776
    # DEBTS
819
    # DEBTS
777
    my ($balance, $non_issue_charges, $other_charges) =
820
    my ( $balance, $non_issue_charges, $other_charges ) =
778
      C4::Members::GetMemberAccountBalance( $borrower->{'borrowernumber'} );
821
      C4::Members::GetMemberAccountBalance( $borrower->{'borrowernumber'} );
779
    my $amountlimit = C4::Context->preference("noissuescharge");
822
    my $amountlimit          = C4::Context->preference("noissuescharge");
780
    my $allowfineoverride = C4::Context->preference("AllowFineOverride");
823
    my $allowfineoverride    = C4::Context->preference("AllowFineOverride");
781
    my $allfinesneedoverride = C4::Context->preference("AllFinesNeedOverride");
824
    my $allfinesneedoverride = C4::Context->preference("AllFinesNeedOverride");
782
    if ( C4::Context->preference("IssuingInProcess") ) {
825
    if ( C4::Context->preference("IssuingInProcess") ) {
783
        if ( $non_issue_charges > $amountlimit && !$inprocess && !$allowfineoverride) {
826
        if (   $non_issue_charges > $amountlimit
827
            && !$inprocess
828
            && !$allowfineoverride )
829
        {
784
            $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_charges );
830
            $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_charges );
785
        } elsif ( $non_issue_charges > $amountlimit && !$inprocess && $allowfineoverride) {
831
        }
832
        elsif ($non_issue_charges > $amountlimit
833
            && !$inprocess
834
            && $allowfineoverride )
835
        {
786
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
836
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
787
        } elsif ( $allfinesneedoverride && $non_issue_charges > 0 && $non_issue_charges <= $amountlimit && !$inprocess ) {
837
        }
838
        elsif ($allfinesneedoverride
839
            && $non_issue_charges > 0
840
            && $non_issue_charges <= $amountlimit
841
            && !$inprocess )
842
        {
788
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
843
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
789
        }
844
        }
790
    }
845
    }
791
    else {
846
    else {
792
        if ( $non_issue_charges > $amountlimit && $allowfineoverride ) {
847
        if ( $non_issue_charges > $amountlimit && $allowfineoverride ) {
793
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
848
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
794
        } elsif ( $non_issue_charges > $amountlimit && !$allowfineoverride) {
849
        }
850
        elsif ( $non_issue_charges > $amountlimit && !$allowfineoverride ) {
795
            $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_charges );
851
            $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_charges );
796
        } elsif ( $non_issue_charges > 0 && $allfinesneedoverride ) {
852
        }
853
        elsif ( $non_issue_charges > 0 && $allfinesneedoverride ) {
797
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
854
            $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_charges );
798
        }
855
        }
799
    }
856
    }
800
    if ($balance > 0 && $other_charges > 0) {
857
    if ( $balance > 0 && $other_charges > 0 ) {
801
        $alerts{OTHER_CHARGES} = sprintf( "%.2f", $other_charges );
858
        $alerts{OTHER_CHARGES} = sprintf( "%.2f", $other_charges );
802
    }
859
    }
803
860
804
    my ($blocktype, $count) = C4::Members::IsMemberBlocked($borrower->{'borrowernumber'});
861
    my ( $blocktype, $count ) =
805
    if ($blocktype == -1) {
862
      C4::Members::IsMemberBlocked( $borrower->{'borrowernumber'} );
863
    if ( $blocktype == -1 ) {
806
        ## patron has outstanding overdue loans
864
        ## patron has outstanding overdue loans
807
        if ( C4::Context->preference("OverduesBlockCirc") eq 'block'){
865
        if ( C4::Context->preference("OverduesBlockCirc") eq 'block' ) {
808
            $issuingimpossible{USERBLOCKEDOVERDUE} = $count;
866
            $issuingimpossible{USERBLOCKEDOVERDUE} = $count;
809
        }
867
        }
810
        elsif ( C4::Context->preference("OverduesBlockCirc") eq 'confirmation'){
868
        elsif ( C4::Context->preference("OverduesBlockCirc") eq 'confirmation' )
869
        {
811
            $needsconfirmation{USERBLOCKEDOVERDUE} = $count;
870
            $needsconfirmation{USERBLOCKEDOVERDUE} = $count;
812
        }
871
        }
813
    } elsif($blocktype == 1) {
872
    }
873
    elsif ( $blocktype == 1 ) {
874
814
        # patron has accrued fine days
875
        # patron has accrued fine days
815
        $issuingimpossible{USERBLOCKEDREMAINING} = $count;
876
        $issuingimpossible{USERBLOCKEDREMAINING} = $count;
816
    }
877
    }
817
878
818
#
879
    #
819
    # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
880
    # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
820
    #
881
    #
821
    my ($current_loan_count, $max_loans_allowed) = TooMany( $borrower, $item->{biblionumber}, $item );
882
    my ( $current_loan_count, $max_loans_allowed ) =
822
    # if TooMany max_loans_allowed returns 0 the user doesn't have permission to check out this book
883
      TooMany( $borrower, $item->{biblionumber}, $item );
823
    if (defined $max_loans_allowed && $max_loans_allowed == 0) {
884
885
# if TooMany max_loans_allowed returns 0 the user doesn't have permission to check out this book
886
    if ( defined $max_loans_allowed && $max_loans_allowed == 0 ) {
824
        $needsconfirmation{PATRON_CANT} = 1;
887
        $needsconfirmation{PATRON_CANT} = 1;
825
    } else {
888
    }
826
        if($max_loans_allowed){
889
    else {
890
        if ($max_loans_allowed) {
827
            if ( C4::Context->preference("AllowTooManyOverride") ) {
891
            if ( C4::Context->preference("AllowTooManyOverride") ) {
828
                $needsconfirmation{TOO_MANY} = 1;
892
                $needsconfirmation{TOO_MANY}           = 1;
829
                $needsconfirmation{current_loan_count} = $current_loan_count;
893
                $needsconfirmation{current_loan_count} = $current_loan_count;
830
                $needsconfirmation{max_loans_allowed} = $max_loans_allowed;
894
                $needsconfirmation{max_loans_allowed}  = $max_loans_allowed;
831
            } else {
895
            }
832
                $issuingimpossible{TOO_MANY} = 1;
896
            else {
897
                $issuingimpossible{TOO_MANY}           = 1;
833
                $issuingimpossible{current_loan_count} = $current_loan_count;
898
                $issuingimpossible{current_loan_count} = $current_loan_count;
834
                $issuingimpossible{max_loans_allowed} = $max_loans_allowed;
899
                $issuingimpossible{max_loans_allowed}  = $max_loans_allowed;
835
            }
900
            }
836
        }
901
        }
837
    }
902
    }
Lines 839-883 sub CanBookBeIssued { Link Here
839
    #
904
    #
840
    # ITEM CHECKING
905
    # ITEM CHECKING
841
    #
906
    #
842
    if ( $item->{'notforloan'} )
907
    if ( $item->{'notforloan'} ) {
843
    {
908
        if ( !C4::Context->preference("AllowNotForLoanOverride") ) {
844
        if(!C4::Context->preference("AllowNotForLoanOverride")){
909
            $issuingimpossible{NOT_FOR_LOAN}    = 1;
845
            $issuingimpossible{NOT_FOR_LOAN} = 1;
846
            $issuingimpossible{item_notforloan} = $item->{'notforloan'};
910
            $issuingimpossible{item_notforloan} = $item->{'notforloan'};
847
        }else{
911
        }
912
        else {
848
            $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
913
            $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
849
            $needsconfirmation{item_notforloan} = $item->{'notforloan'};
914
            $needsconfirmation{item_notforloan}      = $item->{'notforloan'};
850
        }
915
        }
851
    }
916
    }
852
    else {
917
    else {
853
        # we have to check itemtypes.notforloan also
918
        # we have to check itemtypes.notforloan also
854
        if (C4::Context->preference('item-level_itypes')){
919
        if ( C4::Context->preference('item-level_itypes') ) {
920
855
            # this should probably be a subroutine
921
            # this should probably be a subroutine
856
            my $sth = $dbh->prepare("SELECT notforloan FROM itemtypes WHERE itemtype = ?");
922
            my $sth = $dbh->prepare(
857
            $sth->execute($item->{'itemtype'});
923
                "SELECT notforloan FROM itemtypes WHERE itemtype = ?");
858
            my $notforloan=$sth->fetchrow_hashref();
924
            $sth->execute( $item->{'itemtype'} );
859
            if ($notforloan->{'notforloan'}) {
925
            my $notforloan = $sth->fetchrow_hashref();
860
                if (!C4::Context->preference("AllowNotForLoanOverride")) {
926
            if ( $notforloan->{'notforloan'} ) {
861
                    $issuingimpossible{NOT_FOR_LOAN} = 1;
927
                if ( !C4::Context->preference("AllowNotForLoanOverride") ) {
928
                    $issuingimpossible{NOT_FOR_LOAN}        = 1;
862
                    $issuingimpossible{itemtype_notforloan} = $item->{'itype'};
929
                    $issuingimpossible{itemtype_notforloan} = $item->{'itype'};
863
                } else {
930
                }
931
                else {
864
                    $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
932
                    $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
865
                    $needsconfirmation{itemtype_notforloan} = $item->{'itype'};
933
                    $needsconfirmation{itemtype_notforloan}  = $item->{'itype'};
866
                }
934
                }
867
            }
935
            }
868
        }
936
        }
869
        elsif ($biblioitem->{'notforloan'} == 1){
937
        elsif ( $biblioitem->{'notforloan'} == 1 ) {
870
            if (!C4::Context->preference("AllowNotForLoanOverride")) {
938
            if ( !C4::Context->preference("AllowNotForLoanOverride") ) {
871
                $issuingimpossible{NOT_FOR_LOAN} = 1;
939
                $issuingimpossible{NOT_FOR_LOAN} = 1;
872
                $issuingimpossible{itemtype_notforloan} = $biblioitem->{'itemtype'};
940
                $issuingimpossible{itemtype_notforloan} =
873
            } else {
941
                  $biblioitem->{'itemtype'};
942
            }
943
            else {
874
                $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
944
                $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
875
                $needsconfirmation{itemtype_notforloan} = $biblioitem->{'itemtype'};
945
                $needsconfirmation{itemtype_notforloan} =
946
                  $biblioitem->{'itemtype'};
876
            }
947
            }
877
        }
948
        }
878
    }
949
    }
879
    if ( $item->{'withdrawn'} && $item->{'withdrawn'} > 0 )
950
    if ( $item->{'withdrawn'} && $item->{'withdrawn'} > 0 ) {
880
    {
881
        $issuingimpossible{WTHDRAWN} = 1;
951
        $issuingimpossible{WTHDRAWN} = 1;
882
    }
952
    }
883
    if (   $item->{'restricted'}
953
    if (   $item->{'restricted'}
Lines 885-903 sub CanBookBeIssued { Link Here
885
    {
955
    {
886
        $issuingimpossible{RESTRICTED} = 1;
956
        $issuingimpossible{RESTRICTED} = 1;
887
    }
957
    }
888
    if ( $item->{'itemlost'} && C4::Context->preference("IssueLostItem") ne 'nothing' ) {
958
    if ( $item->{'itemlost'}
959
        && C4::Context->preference("IssueLostItem") ne 'nothing' )
960
    {
889
        my $code = GetAuthorisedValueByCode( 'LOST', $item->{'itemlost'} );
961
        my $code = GetAuthorisedValueByCode( 'LOST', $item->{'itemlost'} );
890
        $needsconfirmation{ITEM_LOST} = $code if ( C4::Context->preference("IssueLostItem") eq 'confirm' );
962
        $needsconfirmation{ITEM_LOST} = $code
891
        $alerts{ITEM_LOST} = $code if ( C4::Context->preference("IssueLostItem") eq 'alert' );
963
          if ( C4::Context->preference("IssueLostItem") eq 'confirm' );
964
        $alerts{ITEM_LOST} = $code
965
          if ( C4::Context->preference("IssueLostItem") eq 'alert' );
892
    }
966
    }
893
    if ( C4::Context->preference("IndependentBranches") ) {
967
    if ( C4::Context->preference("IndependentBranches") ) {
894
        my $userenv = C4::Context->userenv;
968
        my $userenv = C4::Context->userenv;
895
        if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
969
        if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
896
            if ( $item->{C4::Context->preference("HomeOrHoldingBranch")} ne $userenv->{branch} ){
970
            if ( $item->{ C4::Context->preference("HomeOrHoldingBranch") } ne
971
                $userenv->{branch} )
972
            {
897
                $issuingimpossible{ITEMNOTSAMEBRANCH} = 1;
973
                $issuingimpossible{ITEMNOTSAMEBRANCH} = 1;
898
                $issuingimpossible{'itemhomebranch'} = $item->{C4::Context->preference("HomeOrHoldingBranch")};
974
                $issuingimpossible{'itemhomebranch'} =
975
                  $item->{ C4::Context->preference("HomeOrHoldingBranch") };
899
            }
976
            }
900
            $needsconfirmation{BORRNOTSAMEBRANCH} = GetBranchName( $borrower->{'branchcode'} )
977
            $needsconfirmation{BORRNOTSAMEBRANCH} =
978
              GetBranchName( $borrower->{'branchcode'} )
901
              if ( $borrower->{'branchcode'} ne $userenv->{branch} );
979
              if ( $borrower->{'branchcode'} ne $userenv->{branch} );
902
        }
980
        }
903
    }
981
    }
Lines 905-919 sub CanBookBeIssued { Link Here
905
    #
983
    #
906
    # CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
984
    # CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
907
    #
985
    #
908
    if ( $issue->{borrowernumber} && $issue->{borrowernumber} eq $borrower->{'borrowernumber'} )
986
    if (   $issue->{borrowernumber}
987
        && $issue->{borrowernumber} eq $borrower->{'borrowernumber'} )
909
    {
988
    {
910
989
911
        # Already issued to current borrower. Ask whether the loan should
990
        # Already issued to current borrower. Ask whether the loan should
912
        # be renewed.
991
        # be renewed.
913
        my ($CanBookBeRenewed,$renewerror) = CanBookBeRenewed(
992
        my ( $CanBookBeRenewed, $renewerror ) =
914
            $borrower->{'borrowernumber'},
993
          CanBookBeRenewed( $borrower->{'borrowernumber'},
915
            $item->{'itemnumber'}
994
            $item->{'itemnumber'} );
916
        );
917
        if ( $CanBookBeRenewed == 0 ) {    # no more renewals allowed
995
        if ( $CanBookBeRenewed == 0 ) {    # no more renewals allowed
918
            $issuingimpossible{NO_MORE_RENEWALS} = 1;
996
            $issuingimpossible{NO_MORE_RENEWALS} = 1;
919
        }
997
        }
Lines 921-968 sub CanBookBeIssued { Link Here
921
            $needsconfirmation{RENEW_ISSUE} = 1;
999
            $needsconfirmation{RENEW_ISSUE} = 1;
922
        }
1000
        }
923
    }
1001
    }
924
    elsif ($issue->{borrowernumber}) {
1002
    elsif ( $issue->{borrowernumber} ) {
925
1003
926
        # issued to someone else
1004
        # issued to someone else
927
        my $currborinfo =    C4::Members::GetMember( borrowernumber => $issue->{borrowernumber} );
1005
        my $currborinfo =
1006
          C4::Members::GetMember( borrowernumber => $issue->{borrowernumber} );
928
1007
929
#        warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
1008
#        warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
930
        $needsconfirmation{ISSUED_TO_ANOTHER} = 1;
1009
        $needsconfirmation{ISSUED_TO_ANOTHER} = 1;
931
        $needsconfirmation{issued_firstname} = $currborinfo->{'firstname'};
1010
        $needsconfirmation{issued_firstname}  = $currborinfo->{'firstname'};
932
        $needsconfirmation{issued_surname} = $currborinfo->{'surname'};
1011
        $needsconfirmation{issued_surname}    = $currborinfo->{'surname'};
933
        $needsconfirmation{issued_cardnumber} = $currborinfo->{'cardnumber'};
1012
        $needsconfirmation{issued_cardnumber} = $currborinfo->{'cardnumber'};
934
        $needsconfirmation{issued_borrowernumber} = $currborinfo->{'borrowernumber'};
1013
        $needsconfirmation{issued_borrowernumber} =
1014
          $currborinfo->{'borrowernumber'};
935
    }
1015
    }
936
1016
937
    unless ( $ignore_reserves ) {
1017
    unless ($ignore_reserves) {
1018
938
        # See if the item is on reserve.
1019
        # See if the item is on reserve.
939
        my ( $restype, $res ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
1020
        my ( $restype, $res ) =
1021
          C4::Reserves::CheckReserves( $item->{'itemnumber'} );
940
        if ($restype) {
1022
        if ($restype) {
941
            my $resbor = $res->{'borrowernumber'};
1023
            my $resbor = $res->{'borrowernumber'};
942
            if ( $resbor ne $borrower->{'borrowernumber'} ) {
1024
            if ( $resbor ne $borrower->{'borrowernumber'} ) {
943
                my ( $resborrower ) = C4::Members::GetMember( borrowernumber => $resbor );
1025
                my ($resborrower) =
1026
                  C4::Members::GetMember( borrowernumber => $resbor );
944
                my $branchname = GetBranchName( $res->{'branchcode'} );
1027
                my $branchname = GetBranchName( $res->{'branchcode'} );
945
                if ( $restype eq "Waiting" )
1028
                if ( $restype eq "Waiting" ) {
946
                {
1029
947
                    # The item is on reserve and waiting, but has been
1030
                    # The item is on reserve and waiting, but has been
948
                    # reserved by some other patron.
1031
                    # reserved by some other patron.
949
                    $needsconfirmation{RESERVE_WAITING} = 1;
1032
                    $needsconfirmation{RESERVE_WAITING} = 1;
950
                    $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'};
1033
                    $needsconfirmation{'resfirstname'} =
951
                    $needsconfirmation{'ressurname'} = $resborrower->{'surname'};
1034
                      $resborrower->{'firstname'};
952
                    $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'};
1035
                    $needsconfirmation{'ressurname'} =
953
                    $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'};
1036
                      $resborrower->{'surname'};
1037
                    $needsconfirmation{'rescardnumber'} =
1038
                      $resborrower->{'cardnumber'};
1039
                    $needsconfirmation{'resborrowernumber'} =
1040
                      $resborrower->{'borrowernumber'};
954
                    $needsconfirmation{'resbranchname'} = $branchname;
1041
                    $needsconfirmation{'resbranchname'} = $branchname;
955
                    $needsconfirmation{'reswaitingdate'} = format_date($res->{'waitingdate'});
1042
                    $needsconfirmation{'reswaitingdate'} =
1043
                      format_date( $res->{'waitingdate'} );
956
                }
1044
                }
957
                elsif ( $restype eq "Reserved" ) {
1045
                elsif ( $restype eq "Reserved" ) {
1046
958
                    # The item is on reserve for someone else.
1047
                    # The item is on reserve for someone else.
959
                    $needsconfirmation{RESERVED} = 1;
1048
                    $needsconfirmation{RESERVED} = 1;
960
                    $needsconfirmation{'resfirstname'} = $resborrower->{'firstname'};
1049
                    $needsconfirmation{'resfirstname'} =
961
                    $needsconfirmation{'ressurname'} = $resborrower->{'surname'};
1050
                      $resborrower->{'firstname'};
962
                    $needsconfirmation{'rescardnumber'} = $resborrower->{'cardnumber'};
1051
                    $needsconfirmation{'ressurname'} =
963
                    $needsconfirmation{'resborrowernumber'} = $resborrower->{'borrowernumber'};
1052
                      $resborrower->{'surname'};
1053
                    $needsconfirmation{'rescardnumber'} =
1054
                      $resborrower->{'cardnumber'};
1055
                    $needsconfirmation{'resborrowernumber'} =
1056
                      $resborrower->{'borrowernumber'};
964
                    $needsconfirmation{'resbranchname'} = $branchname;
1057
                    $needsconfirmation{'resbranchname'} = $branchname;
965
                    $needsconfirmation{'resreservedate'} = format_date($res->{'reservedate'});
1058
                    $needsconfirmation{'resreservedate'} =
1059
                      format_date( $res->{'reservedate'} );
966
                }
1060
                }
967
            }
1061
            }
968
        }
1062
        }
Lines 971-1019 sub CanBookBeIssued { Link Here
971
    # CHECK AGE RESTRICTION
1065
    # CHECK AGE RESTRICTION
972
    #
1066
    #
973
1067
974
    # get $marker from preferences. Could be something like "FSK|PEGI|Alter|Age:"
1068
   # get $marker from preferences. Could be something like "FSK|PEGI|Alter|Age:"
975
    my $markers = C4::Context->preference('AgeRestrictionMarker' );
1069
    my $markers   = C4::Context->preference('AgeRestrictionMarker');
976
    my $bibvalues = $biblioitem->{'agerestriction'};
1070
    my $bibvalues = $biblioitem->{'agerestriction'};
977
    if (($markers)&&($bibvalues))
1071
    if ( ($markers) && ($bibvalues) ) {
978
    {
1072
979
        # Split $bibvalues to something like FSK 16 or PEGI 6
1073
        # Split $bibvalues to something like FSK 16 or PEGI 6
980
        my @values = split ' ', $bibvalues;
1074
        my @values = split ' ', $bibvalues;
981
1075
982
        # Search first occurence of one of the markers
1076
        # Search first occurence of one of the markers
983
        my @markers = split /\|/, $markers;
1077
        my @markers = split /\|/, $markers;
984
        my $index = 0;
1078
        my $index   = 0;
985
        my $take = -1;
1079
        my $take    = -1;
986
        for my $value (@values) {
1080
        for my $value (@values) {
987
            $index ++;
1081
            $index++;
988
            for my $marker (@markers) {
1082
            for my $marker (@markers) {
989
                $marker =~ s/^\s+//; #remove leading spaces
1083
                $marker =~ s/^\s+//;    #remove leading spaces
990
                $marker =~ s/\s+$//; #remove trailing spaces
1084
                $marker =~ s/\s+$//;    #remove trailing spaces
991
                if (uc($marker) eq uc($value)) {
1085
                if ( uc($marker) eq uc($value) ) {
992
                    $take = $index;
1086
                    $take = $index;
993
                    last;
1087
                    last;
994
                }
1088
                }
995
            }
1089
            }
996
            if ($take > -1) {
1090
            if ( $take > -1 ) {
997
                last;
1091
                last;
998
            }
1092
            }
999
        }
1093
        }
1094
1000
        # Index points to the next value
1095
        # Index points to the next value
1001
        my $restrictionyear = 0;
1096
        my $restrictionyear = 0;
1002
        if (($take <= $#values) && ($take >= 0)){
1097
        if ( ( $take <= $#values ) && ( $take >= 0 ) ) {
1003
            $restrictionyear += $values[$take];
1098
            $restrictionyear += $values[$take];
1004
        }
1099
        }
1005
1100
1006
        if ($restrictionyear > 0) {
1101
        if ( $restrictionyear > 0 ) {
1007
            if ( $borrower->{'dateofbirth'}  ) {
1102
            if ( $borrower->{'dateofbirth'} ) {
1008
                my @alloweddate =  split /-/,$borrower->{'dateofbirth'} ;
1103
                my @alloweddate = split /-/, $borrower->{'dateofbirth'};
1009
                $alloweddate[0] += $restrictionyear;
1104
                $alloweddate[0] += $restrictionyear;
1105
1010
                #Prevent runime eror on leap year (invalid date)
1106
                #Prevent runime eror on leap year (invalid date)
1011
                if (($alloweddate[1] == 2) && ($alloweddate[2] == 29)) {
1107
                if ( ( $alloweddate[1] == 2 ) && ( $alloweddate[2] == 29 ) ) {
1012
                    $alloweddate[2] = 28;
1108
                    $alloweddate[2] = 28;
1013
                }
1109
                }
1014
1110
1015
                if ( Date_to_Days(Today) <  Date_to_Days(@alloweddate) -1  ) {
1111
                if ( Date_to_Days(Today) < Date_to_Days(@alloweddate) - 1 ) {
1016
                    if (C4::Context->preference('AgeRestrictionOverride' )) {
1112
                    if ( C4::Context->preference('AgeRestrictionOverride') ) {
1017
                        $needsconfirmation{AGE_RESTRICTION} = "$bibvalues";
1113
                        $needsconfirmation{AGE_RESTRICTION} = "$bibvalues";
1018
                    }
1114
                    }
1019
                    else {
1115
                    else {
Lines 1069-1094 Returns: Link Here
1069
=cut
1165
=cut
1070
1166
1071
sub CanBookBeReturned {
1167
sub CanBookBeReturned {
1072
  my ($item, $branch) = @_;
1168
    my ( $item, $branch ) = @_;
1073
  my $allowreturntobranch = C4::Context->preference("AllowReturnToBranch") || 'anywhere';
1169
    my $allowreturntobranch =
1074
1170
      C4::Context->preference("AllowReturnToBranch") || 'anywhere';
1075
  # assume return is allowed to start
1171
1076
  my $allowed = 1;
1172
    # assume return is allowed to start
1077
  my $message;
1173
    my $allowed = 1;
1078
1174
    my $message;
1079
  # identify all cases where return is forbidden
1175
1080
  if ($allowreturntobranch eq 'homebranch' && $branch ne $item->{'homebranch'}) {
1176
    # identify all cases where return is forbidden
1081
     $allowed = 0;
1177
    if (   $allowreturntobranch eq 'homebranch'
1082
     $message = $item->{'homebranch'};
1178
        && $branch ne $item->{'homebranch'} )
1083
  } elsif ($allowreturntobranch eq 'holdingbranch' && $branch ne $item->{'holdingbranch'}) {
1179
    {
1084
     $allowed = 0;
1180
        $allowed = 0;
1085
     $message = $item->{'holdingbranch'};
1181
        $message = $item->{'homebranch'};
1086
  } elsif ($allowreturntobranch eq 'homeorholdingbranch' && $branch ne $item->{'homebranch'} && $branch ne $item->{'holdingbranch'}) {
1182
    }
1087
     $allowed = 0;
1183
    elsif ($allowreturntobranch eq 'holdingbranch'
1088
     $message = $item->{'homebranch'}; # FIXME: choice of homebranch is arbitrary
1184
        && $branch ne $item->{'holdingbranch'} )
1089
  }
1185
    {
1090
1186
        $allowed = 0;
1091
  return ($allowed, $message);
1187
        $message = $item->{'holdingbranch'};
1188
    }
1189
    elsif ($allowreturntobranch eq 'homeorholdingbranch'
1190
        && $branch ne $item->{'homebranch'}
1191
        && $branch ne $item->{'holdingbranch'} )
1192
    {
1193
        $allowed = 0;
1194
        $message =
1195
          $item->{'homebranch'};    # FIXME: choice of homebranch is arbitrary
1196
    }
1197
1198
    return ( $allowed, $message );
1092
}
1199
}
1093
1200
1094
=head2 CheckHighHolds
1201
=head2 CheckHighHolds
Lines 1174-1331 AddIssue does the following things : Link Here
1174
=cut
1281
=cut
1175
1282
1176
sub AddIssue {
1283
sub AddIssue {
1177
    my ( $borrower, $barcode, $datedue, $cancelreserve, $issuedate, $sipmode) = @_;
1284
    my ( $borrower, $barcode, $datedue, $cancelreserve, $issuedate, $sipmode )
1178
    my $dbh = C4::Context->dbh;
1285
      = @_;
1179
    my $barcodecheck=CheckValidBarcode($barcode);
1286
    my $dbh          = C4::Context->dbh;
1180
    if ($datedue && ref $datedue ne 'DateTime') {
1287
    my $barcodecheck = CheckValidBarcode($barcode);
1288
    if ( $datedue && ref $datedue ne 'DateTime' ) {
1181
        $datedue = dt_from_string($datedue);
1289
        $datedue = dt_from_string($datedue);
1182
    }
1290
    }
1291
1183
    # $issuedate defaults to today.
1292
    # $issuedate defaults to today.
1184
    if ( ! defined $issuedate ) {
1293
    if ( !defined $issuedate ) {
1185
        $issuedate = DateTime->now(time_zone => C4::Context->tz());
1294
        $issuedate = DateTime->now( time_zone => C4::Context->tz() );
1186
    }
1295
    }
1187
    else {
1296
    else {
1188
        if ( ref $issuedate ne 'DateTime') {
1297
        if ( ref $issuedate ne 'DateTime' ) {
1189
            $issuedate = dt_from_string($issuedate);
1298
            $issuedate = dt_from_string($issuedate);
1190
1299
1191
        }
1300
        }
1192
    }
1301
    }
1193
    if ($borrower and $barcode and $barcodecheck ne '0'){#??? wtf
1302
    if ( $borrower and $barcode and $barcodecheck ne '0' ) {    #??? wtf
1194
        # find which item we issue
1303
            # find which item we issue
1195
        my $item = GetItem('', $barcode) or return;	# if we don't get an Item, abort.
1304
        my $item = GetItem( '', $barcode )
1196
        my $branch = _GetCircControlBranch($item,$borrower);
1305
          or return;    # if we don't get an Item, abort.
1306
        my $branch = _GetCircControlBranch( $item, $borrower );
1197
1307
1198
        # get actual issuing if there is one
1308
        # get actual issuing if there is one
1199
        my $actualissue = GetItemIssue( $item->{itemnumber});
1309
        my $actualissue = GetItemIssue( $item->{itemnumber} );
1200
1310
1201
        # get biblioinformation for this item
1311
        # get biblioinformation for this item
1202
        my $biblio = GetBiblioFromItemNumber($item->{itemnumber});
1312
        my $biblio = GetBiblioFromItemNumber( $item->{itemnumber} );
1203
1313
1204
        #
1314
        #
1205
        # check if we just renew the issue.
1315
        # check if we just renew the issue.
1206
        #
1316
        #
1207
        if ($actualissue->{borrowernumber} eq $borrower->{'borrowernumber'}) {
1317
        if ( $actualissue->{borrowernumber} eq $borrower->{'borrowernumber'} ) {
1208
            $datedue = AddRenewal(
1318
            $datedue = AddRenewal(
1209
            $borrower->{'borrowernumber'},
1319
                $borrower->{'borrowernumber'},
1210
            $item->{'itemnumber'},
1320
                $item->{'itemnumber'},
1211
            $branch,
1321
                $branch,
1212
            $datedue,
1322
                $datedue,
1213
            $issuedate, # here interpreted as the renewal date
1323
                $issuedate,    # here interpreted as the renewal date
1214
            );
1324
            );
1215
        }
1325
        }
1216
        else {
1326
        else {
1217
        # it's NOT a renewal
1327
            # it's NOT a renewal
1218
            if ( $actualissue->{borrowernumber}) {
1328
            if ( $actualissue->{borrowernumber} ) {
1219
                # This book is currently on loan, but not to the person
1329
1220
                # who wants to borrow it now. mark it returned before issuing to the new borrower
1330
# This book is currently on loan, but not to the person
1221
                AddReturn(
1331
# who wants to borrow it now. mark it returned before issuing to the new borrower
1222
                    $item->{'barcode'},
1332
                AddReturn( $item->{'barcode'},
1223
                    C4::Context->userenv->{'branch'}
1333
                    C4::Context->userenv->{'branch'} );
1224
                );
1225
            }
1334
            }
1226
1335
1227
            MoveReserve( $item->{'itemnumber'}, $borrower->{'borrowernumber'}, $cancelreserve );
1336
            MoveReserve( $item->{'itemnumber'}, $borrower->{'borrowernumber'},
1228
            # Starting process for transfer job (checking transfert and validate it if we have one)
1337
                $cancelreserve );
1229
            my ($datesent) = GetTransfers($item->{'itemnumber'});
1338
1339
# Starting process for transfer job (checking transfert and validate it if we have one)
1340
            my ($datesent) = GetTransfers( $item->{'itemnumber'} );
1230
            if ($datesent) {
1341
            if ($datesent) {
1231
        # 	updating line of branchtranfert to finish it, and changing the to branch value, implement a comment for visibility of this case (maybe for stats ....)
1342
1232
                my $sth =
1343
# 	updating line of branchtranfert to finish it, and changing the to branch value, implement a comment for visibility of this case (maybe for stats ....)
1233
                    $dbh->prepare(
1344
                my $sth = $dbh->prepare(
1234
                    "UPDATE branchtransfers 
1345
                    "UPDATE branchtransfers 
1235
                        SET datearrived = now(),
1346
                        SET datearrived = now(),
1236
                        tobranch = ?,
1347
                        tobranch = ?,
1237
                        comments = 'Forced branchtransfer'
1348
                        comments = 'Forced branchtransfer'
1238
                    WHERE itemnumber= ? AND datearrived IS NULL"
1349
                    WHERE itemnumber= ? AND datearrived IS NULL"
1239
                    );
1350
                );
1240
                $sth->execute(C4::Context->userenv->{'branch'},$item->{'itemnumber'});
1351
                $sth->execute( C4::Context->userenv->{'branch'},
1352
                    $item->{'itemnumber'} );
1241
            }
1353
            }
1242
1354
1243
        # Record in the database the fact that the book was issued.
1355
            # Record in the database the fact that the book was issued.
1244
        my $sth =
1356
            my $sth = $dbh->prepare(
1245
          $dbh->prepare(
1246
                "INSERT INTO issues
1357
                "INSERT INTO issues
1247
                    (borrowernumber, itemnumber,issuedate, date_due, branchcode)
1358
                    (borrowernumber, itemnumber,issuedate, date_due, branchcode)
1248
                VALUES (?,?,?,?,?)"
1359
                VALUES (?,?,?,?,?)"
1249
          );
1360
            );
1250
        unless ($datedue) {
1361
            unless ($datedue) {
1251
            my $itype = ( C4::Context->preference('item-level_itypes') ) ? $biblio->{'itype'} : $biblio->{'itemtype'};
1362
                my $itype =
1252
            $datedue = CalcDateDue( $issuedate, $itype, $branch, $borrower );
1363
                  ( C4::Context->preference('item-level_itypes') )
1253
1364
                  ? $biblio->{'itype'}
1254
        }
1365
                  : $biblio->{'itemtype'};
1255
        $datedue->truncate( to => 'minute');
1366
                $datedue =
1256
        $sth->execute(
1367
                  CalcDateDue( $issuedate, $itype, $branch, $borrower );
1257
            $borrower->{'borrowernumber'},      # borrowernumber
1258
            $item->{'itemnumber'},              # itemnumber
1259
            $issuedate->strftime('%Y-%m-%d %H:%M:00'), # issuedate
1260
            $datedue->strftime('%Y-%m-%d %H:%M:00'),   # date_due
1261
            C4::Context->userenv->{'branch'}    # branchcode
1262
        );
1263
        if ( C4::Context->preference('ReturnToShelvingCart') ) { ## ReturnToShelvingCart is on, anything issued should be taken off the cart.
1264
          CartToShelf( $item->{'itemnumber'} );
1265
        }
1266
        $item->{'issues'}++;
1267
        if ( C4::Context->preference('UpdateTotalIssuesOnCirc') ) {
1268
            UpdateTotalIssues($item->{'biblionumber'}, 1);
1269
        }
1270
1368
1271
        ## If item was lost, it has now been found, reverse any list item charges if neccessary.
1272
        if ( $item->{'itemlost'} ) {
1273
            if ( C4::Context->preference('RefundLostItemFeeOnReturn' ) ) {
1274
                _FixAccountForLostAndReturned( $item->{'itemnumber'}, undef, $item->{'barcode'} );
1275
            }
1369
            }
1276
        }
1370
            $datedue->truncate( to => 'minute' );
1371
            $sth->execute(
1372
                $borrower->{'borrowernumber'},                # borrowernumber
1373
                $item->{'itemnumber'},                        # itemnumber
1374
                $issuedate->strftime('%Y-%m-%d %H:%M:00'),    # issuedate
1375
                $datedue->strftime('%Y-%m-%d %H:%M:00'),      # date_due
1376
                C4::Context->userenv->{'branch'}              # branchcode
1377
            );
1378
            if ( C4::Context->preference('ReturnToShelvingCart') )
1379
            { ## ReturnToShelvingCart is on, anything issued should be taken off the cart.
1380
                CartToShelf( $item->{'itemnumber'} );
1381
            }
1382
            $item->{'issues'}++;
1383
            if ( C4::Context->preference('UpdateTotalIssuesOnCirc') ) {
1384
                UpdateTotalIssues( $item->{'biblionumber'}, 1 );
1385
            }
1277
1386
1278
        ModItem({ issues           => $item->{'issues'},
1387
            ## If item was lost, it has now been found, reverse any list item charges if neccessary.
1279
                  holdingbranch    => C4::Context->userenv->{'branch'},
1388
            if ( $item->{'itemlost'} ) {
1280
                  itemlost         => 0,
1389
                if ( C4::Context->preference('RefundLostItemFeeOnReturn') ) {
1281
                  datelastborrowed => DateTime->now(time_zone => C4::Context->tz())->ymd(),
1390
                    _FixAccountForLostAndReturned( $item->{'itemnumber'},
1282
                  onloan           => $datedue->ymd(),
1391
                        undef, $item->{'barcode'} );
1283
                }, $item->{'biblionumber'}, $item->{'itemnumber'});
1392
                }
1284
        ModDateLastSeen( $item->{'itemnumber'} );
1393
            }
1285
1394
1286
        # If it costs to borrow this book, charge it to the patron's account.
1395
            ModItem(
1287
        my ( $charge, $itemtype ) = GetIssuingCharges(
1396
                {
1288
            $item->{'itemnumber'},
1397
                    issues        => $item->{'issues'},
1289
            $borrower->{'borrowernumber'}
1398
                    holdingbranch => C4::Context->userenv->{'branch'},
1290
        );
1399
                    itemlost      => 0,
1291
        if ( $charge > 0 ) {
1400
                    datelastborrowed =>
1292
            AddIssuingCharge(
1401
                      DateTime->now( time_zone => C4::Context->tz() )->ymd(),
1293
                $item->{'itemnumber'},
1402
                    onloan => $datedue->ymd(),
1294
                $borrower->{'borrowernumber'}, $charge
1403
                },
1404
                $item->{'biblionumber'},
1405
                $item->{'itemnumber'}
1295
            );
1406
            );
1296
            $item->{'charge'} = $charge;
1407
            ModDateLastSeen( $item->{'itemnumber'} );
1297
        }
1408
1409
           # If it costs to borrow this book, charge it to the patron's account.
1410
            my ( $charge, $itemtype ) =
1411
              GetIssuingCharges( $item->{'itemnumber'},
1412
                $borrower->{'borrowernumber'} );
1413
            if ( $charge > 0 ) {
1414
                AddIssuingCharge( $item->{'itemnumber'},
1415
                    $borrower->{'borrowernumber'}, $charge );
1416
                $item->{'charge'} = $charge;
1417
            }
1298
1418
1299
        # Record the fact that this book was issued.
1419
            # Record the fact that this book was issued.
1300
        &UpdateStats(
1420
            &UpdateStats(
1301
            C4::Context->userenv->{'branch'},
1421
                C4::Context->userenv->{'branch'},
1302
            'issue', $charge,
1422
                'issue',
1303
            ($sipmode ? "SIP-$sipmode" : ''), $item->{'itemnumber'},
1423
                $charge,
1304
            $item->{'itype'}, $borrower->{'borrowernumber'}, undef, $item->{'ccode'}
1424
                ( $sipmode ? "SIP-$sipmode" : '' ),
1305
        );
1425
                $item->{'itemnumber'},
1426
                $item->{'itype'},
1427
                $borrower->{'borrowernumber'},
1428
                undef,
1429
                $item->{'ccode'}
1430
            );
1306
1431
1307
        # Send a checkout slip.
1432
            # Send a checkout slip.
1308
        my $circulation_alert = 'C4::ItemCirculationAlertPreference';
1433
            my $circulation_alert = 'C4::ItemCirculationAlertPreference';
1309
        my %conditions = (
1434
            my %conditions        = (
1310
            branchcode   => $branch,
1435
                branchcode   => $branch,
1311
            categorycode => $borrower->{categorycode},
1436
                categorycode => $borrower->{categorycode},
1312
            item_type    => $item->{itype},
1437
                item_type    => $item->{itype},
1313
            notification => 'CHECKOUT',
1438
                notification => 'CHECKOUT',
1314
        );
1439
            );
1315
        if ($circulation_alert->is_enabled_for(\%conditions)) {
1440
            if ( $circulation_alert->is_enabled_for( \%conditions ) ) {
1316
            SendCirculationAlert({
1441
                SendCirculationAlert(
1317
                type     => 'CHECKOUT',
1442
                    {
1318
                item     => $item,
1443
                        type     => 'CHECKOUT',
1319
                borrower => $borrower,
1444
                        item     => $item,
1320
                branch   => $branch,
1445
                        borrower => $borrower,
1321
            });
1446
                        branch   => $branch,
1447
                    }
1448
                );
1449
            }
1322
        }
1450
        }
1323
    }
1324
1451
1325
    logaction("CIRCULATION", "ISSUE", $borrower->{'borrowernumber'}, $biblio->{'itemnumber'})
1452
        logaction(
1326
        if C4::Context->preference("IssueLog");
1453
            "CIRCULATION", "ISSUE",
1327
  }
1454
            $borrower->{'borrowernumber'},
1328
  return ($datedue);	# not necessarily the same as when it came in!
1455
            $biblio->{'itemnumber'}
1456
        ) if C4::Context->preference("IssueLog");
1457
    }
1458
    return ($datedue);    # not necessarily the same as when it came in!
1329
}
1459
}
1330
1460
1331
=head2 GetLoanLength
1461
=head2 GetLoanLength
Lines 1339-1355 Get loan length for an itemtype, a borrower type and a branch Link Here
1339
sub GetLoanLength {
1469
sub GetLoanLength {
1340
    my ( $borrowertype, $itemtype, $branchcode ) = @_;
1470
    my ( $borrowertype, $itemtype, $branchcode ) = @_;
1341
    my $dbh = C4::Context->dbh;
1471
    my $dbh = C4::Context->dbh;
1342
    my $sth = $dbh->prepare(qq{
1472
    my $sth = $dbh->prepare(
1473
        qq{
1343
        SELECT issuelength, lengthunit, renewalperiod
1474
        SELECT issuelength, lengthunit, renewalperiod
1344
        FROM issuingrules
1475
        FROM issuingrules
1345
        WHERE   categorycode=?
1476
        WHERE   categorycode=?
1346
            AND itemtype=?
1477
            AND itemtype=?
1347
            AND branchcode=?
1478
            AND branchcode=?
1348
            AND issuelength IS NOT NULL
1479
            AND issuelength IS NOT NULL
1349
    });
1480
    }
1481
    );
1350
1482
1351
    # try to find issuelength & return the 1st available.
1483
# try to find issuelength & return the 1st available.
1352
    # check with borrowertype, itemtype and branchcode, then without one of those parameters
1484
# check with borrowertype, itemtype and branchcode, then without one of those parameters
1353
    $sth->execute( $borrowertype, $itemtype, $branchcode );
1485
    $sth->execute( $borrowertype, $itemtype, $branchcode );
1354
    my $loanlength = $sth->fetchrow_hashref;
1486
    my $loanlength = $sth->fetchrow_hashref;
1355
1487
Lines 1393-1406 sub GetLoanLength { Link Here
1393
1525
1394
    # if no rule is set => 21 days (hardcoded)
1526
    # if no rule is set => 21 days (hardcoded)
1395
    return {
1527
    return {
1396
        issuelength => 21,
1528
        issuelength   => 21,
1397
        renewalperiod => 21,
1529
        renewalperiod => 21,
1398
        lengthunit => 'days',
1530
        lengthunit    => 'days',
1399
    };
1531
    };
1400
1532
1401
}
1533
}
1402
1534
1403
1404
=head2 GetHardDueDate
1535
=head2 GetHardDueDate
1405
1536
1406
  my ($hardduedate,$hardduedatecompare) = &GetHardDueDate($borrowertype,$itemtype,branchcode)
1537
  my ($hardduedate,$hardduedatecompare) = &GetHardDueDate($borrowertype,$itemtype,branchcode)
Lines 1414-1424 sub GetHardDueDate { Link Here
1414
1545
1415
    my $rule = GetIssuingRule( $borrowertype, $itemtype, $branchcode );
1546
    my $rule = GetIssuingRule( $borrowertype, $itemtype, $branchcode );
1416
1547
1417
    if ( defined( $rule ) ) {
1548
    if ( defined($rule) ) {
1418
        if ( $rule->{hardduedate} ) {
1549
        if ( $rule->{hardduedate} ) {
1419
            return (dt_from_string($rule->{hardduedate}, 'iso'),$rule->{hardduedatecompare});
1550
            return ( dt_from_string( $rule->{hardduedate}, 'iso' ),
1420
        } else {
1551
                $rule->{hardduedatecompare} );
1421
            return (undef, undef);
1552
        }
1553
        else {
1554
            return ( undef, undef );
1422
        }
1555
        }
1423
    }
1556
    }
1424
}
1557
}
Lines 1439-1478 Returns a hashref from the issuingrules table. Link Here
1439
sub GetIssuingRule {
1572
sub GetIssuingRule {
1440
    my ( $borrowertype, $itemtype, $branchcode ) = @_;
1573
    my ( $borrowertype, $itemtype, $branchcode ) = @_;
1441
    my $dbh = C4::Context->dbh;
1574
    my $dbh = C4::Context->dbh;
1442
    my $sth =  $dbh->prepare( "select * from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null"  );
1575
    my $sth = $dbh->prepare(
1576
"select * from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null"
1577
    );
1443
    my $irule;
1578
    my $irule;
1444
1579
1445
    $sth->execute( $borrowertype, $itemtype, $branchcode );
1580
    $sth->execute( $borrowertype, $itemtype, $branchcode );
1446
    $irule = $sth->fetchrow_hashref;
1581
    $irule = $sth->fetchrow_hashref;
1447
    return $irule if defined($irule) ;
1582
    return $irule if defined($irule);
1448
1583
1449
    $sth->execute( $borrowertype, "*", $branchcode );
1584
    $sth->execute( $borrowertype, "*", $branchcode );
1450
    $irule = $sth->fetchrow_hashref;
1585
    $irule = $sth->fetchrow_hashref;
1451
    return $irule if defined($irule) ;
1586
    return $irule if defined($irule);
1452
1587
1453
    $sth->execute( "*", $itemtype, $branchcode );
1588
    $sth->execute( "*", $itemtype, $branchcode );
1454
    $irule = $sth->fetchrow_hashref;
1589
    $irule = $sth->fetchrow_hashref;
1455
    return $irule if defined($irule) ;
1590
    return $irule if defined($irule);
1456
1591
1457
    $sth->execute( "*", "*", $branchcode );
1592
    $sth->execute( "*", "*", $branchcode );
1458
    $irule = $sth->fetchrow_hashref;
1593
    $irule = $sth->fetchrow_hashref;
1459
    return $irule if defined($irule) ;
1594
    return $irule if defined($irule);
1460
1595
1461
    $sth->execute( $borrowertype, $itemtype, "*" );
1596
    $sth->execute( $borrowertype, $itemtype, "*" );
1462
    $irule = $sth->fetchrow_hashref;
1597
    $irule = $sth->fetchrow_hashref;
1463
    return $irule if defined($irule) ;
1598
    return $irule if defined($irule);
1464
1599
1465
    $sth->execute( $borrowertype, "*", "*" );
1600
    $sth->execute( $borrowertype, "*", "*" );
1466
    $irule = $sth->fetchrow_hashref;
1601
    $irule = $sth->fetchrow_hashref;
1467
    return $irule if defined($irule) ;
1602
    return $irule if defined($irule);
1468
1603
1469
    $sth->execute( "*", $itemtype, "*" );
1604
    $sth->execute( "*", $itemtype, "*" );
1470
    $irule = $sth->fetchrow_hashref;
1605
    $irule = $sth->fetchrow_hashref;
1471
    return $irule if defined($irule) ;
1606
    return $irule if defined($irule);
1472
1607
1473
    $sth->execute( "*", "*", "*" );
1608
    $sth->execute( "*", "*", "*" );
1474
    $irule = $sth->fetchrow_hashref;
1609
    $irule = $sth->fetchrow_hashref;
1475
    return $irule if defined($irule) ;
1610
    return $irule if defined($irule);
1476
1611
1477
    # if no rule matches,
1612
    # if no rule matches,
1478
    return;
1613
    return;
Lines 1511-1517 wildcards. Link Here
1511
=cut
1646
=cut
1512
1647
1513
sub GetBranchBorrowerCircRule {
1648
sub GetBranchBorrowerCircRule {
1514
    my $branchcode = shift;
1649
    my $branchcode   = shift;
1515
    my $categorycode = shift;
1650
    my $categorycode = shift;
1516
1651
1517
    my $branch_cat_query = "SELECT maxissueqty
1652
    my $branch_cat_query = "SELECT maxissueqty
Lines 1520-1528 sub GetBranchBorrowerCircRule { Link Here
1520
                            AND   categorycode = ?";
1655
                            AND   categorycode = ?";
1521
    my $dbh = C4::Context->dbh();
1656
    my $dbh = C4::Context->dbh();
1522
    my $sth = $dbh->prepare($branch_cat_query);
1657
    my $sth = $dbh->prepare($branch_cat_query);
1523
    $sth->execute($branchcode, $categorycode);
1658
    $sth->execute( $branchcode, $categorycode );
1524
    my $result;
1659
    my $result;
1525
    if ($result = $sth->fetchrow_hashref()) {
1660
    if ( $result = $sth->fetchrow_hashref() ) {
1526
        return $result;
1661
        return $result;
1527
    }
1662
    }
1528
1663
Lines 1532-1538 sub GetBranchBorrowerCircRule { Link Here
1532
                        WHERE branchcode = ?";
1667
                        WHERE branchcode = ?";
1533
    $sth = $dbh->prepare($branch_query);
1668
    $sth = $dbh->prepare($branch_query);
1534
    $sth->execute($branchcode);
1669
    $sth->execute($branchcode);
1535
    if ($result = $sth->fetchrow_hashref()) {
1670
    if ( $result = $sth->fetchrow_hashref() ) {
1536
        return $result;
1671
        return $result;
1537
    }
1672
    }
1538
1673
Lines 1542-1564 sub GetBranchBorrowerCircRule { Link Here
1542
                          WHERE categorycode = ?";
1677
                          WHERE categorycode = ?";
1543
    $sth = $dbh->prepare($category_query);
1678
    $sth = $dbh->prepare($category_query);
1544
    $sth->execute($categorycode);
1679
    $sth->execute($categorycode);
1545
    if ($result = $sth->fetchrow_hashref()) {
1680
    if ( $result = $sth->fetchrow_hashref() ) {
1546
        return $result;
1681
        return $result;
1547
    }
1682
    }
1548
  
1683
1549
    # try default branch, default borrower category
1684
    # try default branch, default borrower category
1550
    my $default_query = "SELECT maxissueqty
1685
    my $default_query = "SELECT maxissueqty
1551
                          FROM default_circ_rules";
1686
                          FROM default_circ_rules";
1552
    $sth = $dbh->prepare($default_query);
1687
    $sth = $dbh->prepare($default_query);
1553
    $sth->execute();
1688
    $sth->execute();
1554
    if ($result = $sth->fetchrow_hashref()) {
1689
    if ( $result = $sth->fetchrow_hashref() ) {
1555
        return $result;
1690
        return $result;
1556
    }
1691
    }
1557
    
1692
1558
    # built-in default circulation rule
1693
    # built-in default circulation rule
1559
    return {
1694
    return { maxissueqty => undef, };
1560
        maxissueqty => undef,
1561
    };
1562
}
1695
}
1563
1696
1564
=head2 GetBranchItemRule
1697
=head2 GetBranchItemRule
Lines 1592-1630 Neither C<$branchcode> nor C<$itemtype> should be '*'. Link Here
1592
1725
1593
sub GetBranchItemRule {
1726
sub GetBranchItemRule {
1594
    my ( $branchcode, $itemtype ) = @_;
1727
    my ( $branchcode, $itemtype ) = @_;
1595
    my $dbh = C4::Context->dbh();
1728
    my $dbh    = C4::Context->dbh();
1596
    my $result = {};
1729
    my $result = {};
1597
1730
1598
    my @attempts = (
1731
    my @attempts = (
1599
        ['SELECT holdallowed, returnbranch
1732
        [
1733
            'SELECT holdallowed, returnbranch
1600
            FROM branch_item_rules
1734
            FROM branch_item_rules
1601
            WHERE branchcode = ?
1735
            WHERE branchcode = ?
1602
              AND itemtype = ?', $branchcode, $itemtype],
1736
              AND itemtype = ?', $branchcode, $itemtype
1603
        ['SELECT holdallowed, returnbranch
1737
        ],
1738
        [
1739
            'SELECT holdallowed, returnbranch
1604
            FROM default_branch_circ_rules
1740
            FROM default_branch_circ_rules
1605
            WHERE branchcode = ?', $branchcode],
1741
            WHERE branchcode = ?', $branchcode
1606
        ['SELECT holdallowed, returnbranch
1742
        ],
1743
        [
1744
            'SELECT holdallowed, returnbranch
1607
            FROM default_branch_item_rules
1745
            FROM default_branch_item_rules
1608
            WHERE itemtype = ?', $itemtype],
1746
            WHERE itemtype = ?', $itemtype
1609
        ['SELECT holdallowed, returnbranch
1747
        ],
1610
            FROM default_circ_rules'],
1748
        [
1749
            'SELECT holdallowed, returnbranch
1750
            FROM default_circ_rules'
1751
        ],
1611
    );
1752
    );
1612
1753
1613
    foreach my $attempt (@attempts) {
1754
    foreach my $attempt (@attempts) {
1614
        my ($query, @bind_params) = @{$attempt};
1755
        my ( $query, @bind_params ) = @{$attempt};
1615
        my $search_result = $dbh->selectrow_hashref ( $query , {}, @bind_params )
1756
        my $search_result = $dbh->selectrow_hashref( $query, {}, @bind_params )
1616
          or next;
1757
          or next;
1617
1758
1618
        # Since branch/category and branch/itemtype use the same per-branch
1759
        # Since branch/category and branch/itemtype use the same per-branch
1619
        # defaults tables, we have to check that the key we want is set, not
1760
        # defaults tables, we have to check that the key we want is set, not
1620
        # just that a row was returned
1761
        # just that a row was returned
1621
        $result->{'holdallowed'}  = $search_result->{'holdallowed'}  unless ( defined $result->{'holdallowed'} );
1762
        $result->{'holdallowed'} = $search_result->{'holdallowed'}
1622
        $result->{'returnbranch'} = $search_result->{'returnbranch'} unless ( defined $result->{'returnbranch'} );
1763
          unless ( defined $result->{'holdallowed'} );
1764
        $result->{'returnbranch'} = $search_result->{'returnbranch'}
1765
          unless ( defined $result->{'returnbranch'} );
1623
    }
1766
    }
1624
    
1767
1625
    # built-in default circulation rule
1768
    # built-in default circulation rule
1626
    $result->{'holdallowed'} = 2 unless ( defined $result->{'holdallowed'} );
1769
    $result->{'holdallowed'} = 2 unless ( defined $result->{'holdallowed'} );
1627
    $result->{'returnbranch'} = 'homebranch' unless ( defined $result->{'returnbranch'} );
1770
    $result->{'returnbranch'} = 'homebranch'
1771
      unless ( defined $result->{'returnbranch'} );
1628
1772
1629
    return $result;
1773
    return $result;
1630
}
1774
}
Lines 1706-1766 patron who last borrowed the book. Link Here
1706
sub AddReturn {
1850
sub AddReturn {
1707
    my ( $barcode, $branch, $exemptfine, $dropbox ) = @_;
1851
    my ( $barcode, $branch, $exemptfine, $dropbox ) = @_;
1708
1852
1709
    if ($branch and not GetBranchDetail($branch)) {
1853
    if ( $branch and not GetBranchDetail($branch) ) {
1710
        warn "AddReturn error: branch '$branch' not found.  Reverting to " . C4::Context->userenv->{'branch'};
1854
        warn "AddReturn error: branch '$branch' not found.  Reverting to "
1855
          . C4::Context->userenv->{'branch'};
1711
        undef $branch;
1856
        undef $branch;
1712
    }
1857
    }
1713
    $branch = C4::Context->userenv->{'branch'} unless $branch;  # we trust userenv to be a safe fallback/default
1858
    $branch = C4::Context->userenv->{'branch'}
1859
      unless $branch;    # we trust userenv to be a safe fallback/default
1714
    my $messages;
1860
    my $messages;
1715
    my $borrower;
1861
    my $borrower;
1716
    my $biblio;
1862
    my $biblio;
1717
    my $doreturn       = 1;
1863
    my $doreturn       = 1;
1718
    my $validTransfert = 0;
1864
    my $validTransfert = 0;
1719
    my $stat_type = 'return';    
1865
    my $stat_type      = 'return';
1720
1866
1721
    # get information on item
1867
    # get information on item
1722
    my $itemnumber = GetItemnumberFromBarcode( $barcode );
1868
    my $itemnumber = GetItemnumberFromBarcode($barcode);
1723
    unless ($itemnumber) {
1869
    unless ($itemnumber) {
1724
        return (0, { BadBarcode => $barcode }); # no barcode means no item or borrower.  bail out.
1870
        return ( 0, { BadBarcode => $barcode } )
1725
    }
1871
          ;              # no barcode means no item or borrower.  bail out.
1726
    my $issue  = GetItemIssue($itemnumber);
1872
    }
1727
#   warn Dumper($iteminformation);
1873
    my $issue = GetItemIssue($itemnumber);
1728
    if ($issue and $issue->{borrowernumber}) {
1874
1729
        $borrower = C4::Members::GetMemberDetails($issue->{borrowernumber})
1875
    #   warn Dumper($iteminformation);
1730
            or die "Data inconsistency: barcode $barcode (itemnumber:$itemnumber) claims to be issued to non-existant borrowernumber '$issue->{borrowernumber}'\n"
1876
    if ( $issue and $issue->{borrowernumber} ) {
1731
                . Dumper($issue) . "\n";
1877
        $borrower = C4::Members::GetMemberDetails( $issue->{borrowernumber} )
1732
    } else {
1878
          or die
1879
"Data inconsistency: barcode $barcode (itemnumber:$itemnumber) claims to be issued to non-existant borrowernumber '$issue->{borrowernumber}'\n"
1880
          . Dumper($issue) . "\n";
1881
    }
1882
    else {
1733
        $messages->{'NotIssued'} = $barcode;
1883
        $messages->{'NotIssued'} = $barcode;
1734
        # even though item is not on loan, it may still be transferred;  therefore, get current branch info
1884
1885
# even though item is not on loan, it may still be transferred;  therefore, get current branch info
1735
        $doreturn = 0;
1886
        $doreturn = 0;
1736
        # No issue, no borrowernumber.  ONLY if $doreturn, *might* you have a $borrower later.
1887
1737
        # Record this as a local use, instead of a return, if the RecordLocalUseOnReturn is on
1888
# No issue, no borrowernumber.  ONLY if $doreturn, *might* you have a $borrower later.
1738
        if (C4::Context->preference("RecordLocalUseOnReturn")) {
1889
# Record this as a local use, instead of a return, if the RecordLocalUseOnReturn is on
1739
           $messages->{'LocalUse'} = 1;
1890
        if ( C4::Context->preference("RecordLocalUseOnReturn") ) {
1740
           $stat_type = 'localuse';
1891
            $messages->{'LocalUse'} = 1;
1892
            $stat_type = 'localuse';
1741
        }
1893
        }
1742
    }
1894
    }
1743
1895
1744
    my $item = GetItem($itemnumber) or die "GetItem($itemnumber) failed";
1896
    my $item = GetItem($itemnumber) or die "GetItem($itemnumber) failed";
1745
        # full item data, but no borrowernumber or checkout info (no issue)
1897
1746
        # we know GetItem should work because GetItemnumberFromBarcode worked
1898
    # full item data, but no borrowernumber or checkout info (no issue)
1747
    my $hbr      = GetBranchItemRule($item->{'homebranch'}, $item->{'itype'})->{'returnbranch'} || "homebranch";
1899
    # we know GetItem should work because GetItemnumberFromBarcode worked
1748
        # get the proper branch to which to return the item
1900
    my $hbr = GetBranchItemRule( $item->{'homebranch'}, $item->{'itype'} )
1749
    $hbr = $item->{$hbr} || $branch ;
1901
      ->{'returnbranch'} || "homebranch";
1750
        # if $hbr was "noreturn" or any other non-item table value, then it should 'float' (i.e. stay at this branch)
1902
1751
1903
    # get the proper branch to which to return the item
1752
    my $borrowernumber = $borrower->{'borrowernumber'} || undef;    # we don't know if we had a borrower or not
1904
    $hbr = $item->{$hbr} || $branch;
1753
1905
1754
    # check if the book is in a permanent collection....
1906
# if $hbr was "noreturn" or any other non-item table value, then it should 'float' (i.e. stay at this branch)
1755
    # FIXME -- This 'PE' attribute is largely undocumented.  afaict, there's no user interface that reflects this functionality.
1907
1756
    if ( $hbr ) {
1908
    my $borrowernumber = $borrower->{'borrowernumber'}
1757
        my $branches = GetBranches();    # a potentially expensive call for a non-feature.
1909
      || undef;    # we don't know if we had a borrower or not
1910
1911
# check if the book is in a permanent collection....
1912
# FIXME -- This 'PE' attribute is largely undocumented.  afaict, there's no user interface that reflects this functionality.
1913
    if ($hbr) {
1914
        my $branches =
1915
          GetBranches();    # a potentially expensive call for a non-feature.
1758
        $branches->{$hbr}->{PE} and $messages->{'IsPermanent'} = $hbr;
1916
        $branches->{$hbr}->{PE} and $messages->{'IsPermanent'} = $hbr;
1759
    }
1917
    }
1760
1918
1761
    # check if the return is allowed at this branch
1919
    # check if the return is allowed at this branch
1762
    my ($returnallowed, $message) = CanBookBeReturned($item, $branch);
1920
    my ( $returnallowed, $message ) = CanBookBeReturned( $item, $branch );
1763
    unless ($returnallowed){
1921
    unless ($returnallowed) {
1764
        $messages->{'Wrongbranch'} = {
1922
        $messages->{'Wrongbranch'} = {
1765
            Wrongbranch => $branch,
1923
            Wrongbranch => $branch,
1766
            Rightbranch => $message
1924
            Rightbranch => $message
Lines 1769-1775 sub AddReturn { Link Here
1769
        return ( $doreturn, $messages, $issue, $borrower );
1927
        return ( $doreturn, $messages, $issue, $borrower );
1770
    }
1928
    }
1771
1929
1772
    if ( $item->{'withdrawn'} ) { # book has been cancelled
1930
    if ( $item->{'withdrawn'} ) {    # book has been cancelled
1773
        $messages->{'withdrawn'} = 1;
1931
        $messages->{'withdrawn'} = 1;
1774
        $doreturn = 0 if C4::Context->preference("BlockReturnOfWithdrawnItems");
1932
        $doreturn = 0 if C4::Context->preference("BlockReturnOfWithdrawnItems");
1775
    }
1933
    }
Lines 1777-1798 sub AddReturn { Link Here
1777
    # case of a return of document (deal with issues and holdingbranch)
1935
    # case of a return of document (deal with issues and holdingbranch)
1778
    my $today = DateTime->now( time_zone => C4::Context->tz() );
1936
    my $today = DateTime->now( time_zone => C4::Context->tz() );
1779
    if ($doreturn) {
1937
    if ($doreturn) {
1780
    my $datedue = $issue->{date_due};
1938
        my $datedue = $issue->{date_due};
1781
        $borrower or warn "AddReturn without current borrower";
1939
        $borrower or warn "AddReturn without current borrower";
1782
        my $circControlBranch;
1940
        my $circControlBranch;
1783
        if ($dropbox) {
1941
        if ($dropbox) {
1784
            # define circControlBranch only if dropbox mode is set
1942
1785
            # don't allow dropbox mode to create an invalid entry in issues (issuedate > today)
1943
# define circControlBranch only if dropbox mode is set
1786
            # FIXME: check issuedate > returndate, factoring in holidays
1944
# don't allow dropbox mode to create an invalid entry in issues (issuedate > today)
1787
            #$circControlBranch = _GetCircControlBranch($item,$borrower) unless ( $item->{'issuedate'} eq C4::Dates->today('iso') );;
1945
# FIXME: check issuedate > returndate, factoring in holidays
1788
            $circControlBranch = _GetCircControlBranch($item,$borrower);
1946
#$circControlBranch = _GetCircControlBranch($item,$borrower) unless ( $item->{'issuedate'} eq C4::Dates->today('iso') );;
1789
        $issue->{'overdue'} = DateTime->compare($issue->{'date_due'}, $today ) == -1 ? 1 : 0;
1947
            $circControlBranch = _GetCircControlBranch( $item, $borrower );
1948
            $issue->{'overdue'} =
1949
              DateTime->compare( $issue->{'date_due'}, $today ) == -1 ? 1 : 0;
1790
        }
1950
        }
1791
1951
1792
        if ($borrowernumber) {
1952
        if ($borrowernumber) {
1793
            if( C4::Context->preference('CalculateFinesOnReturn') && $issue->{'overdue'}){
1953
            if ( C4::Context->preference('CalculateFinesOnReturn')
1794
            # we only need to calculate and change the fines if we want to do that on return
1954
                && $issue->{'overdue'} )
1795
            # Should be on for hourly loans
1955
            {
1956
# we only need to calculate and change the fines if we want to do that on return
1957
# Should be on for hourly loans
1796
                my $control = C4::Context->preference('CircControl');
1958
                my $control = C4::Context->preference('CircControl');
1797
                my $control_branchcode =
1959
                my $control_branchcode =
1798
                    ( $control eq 'ItemHomeLibrary' ) ? $item->{homebranch}
1960
                    ( $control eq 'ItemHomeLibrary' ) ? $item->{homebranch}
Lines 1822-1947 sub AddReturn { Link Here
1822
1984
1823
        }
1985
        }
1824
1986
1825
        ModItem({ onloan => undef }, $issue->{'biblionumber'}, $item->{'itemnumber'});
1987
        ModItem(
1988
            { onloan => undef },
1989
            $issue->{'biblionumber'},
1990
            $item->{'itemnumber'}
1991
        );
1826
    }
1992
    }
1827
1993
1828
    # the holdingbranch is updated if the document is returned to another location.
1994
 # the holdingbranch is updated if the document is returned to another location.
1829
    # this is always done regardless of whether the item was on loan or not
1995
 # this is always done regardless of whether the item was on loan or not
1830
    if ($item->{'holdingbranch'} ne $branch) {
1996
    if ( $item->{'holdingbranch'} ne $branch ) {
1831
        UpdateHoldingbranch($branch, $item->{'itemnumber'});
1997
        UpdateHoldingbranch( $branch, $item->{'itemnumber'} );
1832
        $item->{'holdingbranch'} = $branch; # update item data holdingbranch too
1998
        $item->{'holdingbranch'} = $branch; # update item data holdingbranch too
1833
    }
1999
    }
1834
    ModDateLastSeen( $item->{'itemnumber'} );
2000
    ModDateLastSeen( $item->{'itemnumber'} );
1835
2001
1836
    # check if we have a transfer for this document
2002
    # check if we have a transfer for this document
1837
    my ($datesent,$frombranch,$tobranch) = GetTransfers( $item->{'itemnumber'} );
2003
    my ( $datesent, $frombranch, $tobranch ) =
2004
      GetTransfers( $item->{'itemnumber'} );
1838
2005
1839
    # if we have a transfer to do, we update the line of transfers with the datearrived
2006
# if we have a transfer to do, we update the line of transfers with the datearrived
1840
    if ($datesent) {
2007
    if ($datesent) {
1841
        if ( $tobranch eq $branch ) {
2008
        if ( $tobranch eq $branch ) {
1842
            my $sth = C4::Context->dbh->prepare(
2009
            my $sth = C4::Context->dbh->prepare(
1843
                "UPDATE branchtransfers SET datearrived = now() WHERE itemnumber= ? AND datearrived IS NULL"
2010
"UPDATE branchtransfers SET datearrived = now() WHERE itemnumber= ? AND datearrived IS NULL"
1844
            );
2011
            );
1845
            $sth->execute( $item->{'itemnumber'} );
2012
            $sth->execute( $item->{'itemnumber'} );
1846
            # if we have a reservation with valid transfer, we can set it's status to 'W'
2013
1847
            ShelfToCart( $item->{'itemnumber'} ) if ( C4::Context->preference("ReturnToShelvingCart") );
2014
   # if we have a reservation with valid transfer, we can set it's status to 'W'
1848
            C4::Reserves::ModReserveStatus($item->{'itemnumber'}, 'W');
2015
            ShelfToCart( $item->{'itemnumber'} )
1849
        } else {
2016
              if ( C4::Context->preference("ReturnToShelvingCart") );
2017
            C4::Reserves::ModReserveStatus( $item->{'itemnumber'}, 'W' );
2018
        }
2019
        else {
1850
            $messages->{'WrongTransfer'}     = $tobranch;
2020
            $messages->{'WrongTransfer'}     = $tobranch;
1851
            $messages->{'WrongTransferItem'} = $item->{'itemnumber'};
2021
            $messages->{'WrongTransferItem'} = $item->{'itemnumber'};
1852
        }
2022
        }
1853
        $validTransfert = 1;
2023
        $validTransfert = 1;
1854
    } else {
2024
    }
1855
        ShelfToCart( $item->{'itemnumber'} ) if ( C4::Context->preference("ReturnToShelvingCart") );
2025
    else {
2026
        ShelfToCart( $item->{'itemnumber'} )
2027
          if ( C4::Context->preference("ReturnToShelvingCart") );
1856
    }
2028
    }
1857
2029
1858
    # fix up the accounts.....
2030
    # fix up the accounts.....
1859
    if ( $item->{'itemlost'} ) {
2031
    if ( $item->{'itemlost'} ) {
1860
        $messages->{'WasLost'} = 1;
2032
        $messages->{'WasLost'} = 1;
1861
2033
1862
        if ( C4::Context->preference('RefundLostItemFeeOnReturn' ) ) {
2034
        if ( C4::Context->preference('RefundLostItemFeeOnReturn') ) {
1863
            _FixAccountForLostAndReturned($item->{'itemnumber'}, $borrowernumber, $barcode);    # can tolerate undef $borrowernumber
2035
            _FixAccountForLostAndReturned( $item->{'itemnumber'},
2036
                $borrowernumber, $barcode )
2037
              ;    # can tolerate undef $borrowernumber
1864
            $messages->{'LostItemFeeRefunded'} = 1;
2038
            $messages->{'LostItemFeeRefunded'} = 1;
1865
        }
2039
        }
1866
    }
2040
    }
1867
2041
1868
    # fix up the overdues in accounts...
2042
    # fix up the overdues in accounts...
1869
    if ($borrowernumber) {
2043
    if ($borrowernumber) {
1870
        my $fix = _FixOverduesOnReturn($borrowernumber, $item->{itemnumber}, $exemptfine, $dropbox);
2044
        my $fix = _FixOverduesOnReturn( $borrowernumber, $item->{itemnumber},
1871
        defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->{itemnumber}...) failed!";  # zero is OK, check defined
2045
            $exemptfine, $dropbox );
1872
        
2046
        defined($fix)
2047
          or warn
2048
"_FixOverduesOnReturn($borrowernumber, $item->{itemnumber}...) failed!"
2049
          ;        # zero is OK, check defined
2050
1873
        if ( $issue->{overdue} && $issue->{date_due} ) {
2051
        if ( $issue->{overdue} && $issue->{date_due} ) {
1874
# fix fine days
2052
2053
            # fix fine days
1875
            my $debardate =
2054
            my $debardate =
1876
              _debar_user_on_return( $borrower, $item, $issue->{date_due}, $today );
2055
              _debar_user_on_return( $borrower, $item, $issue->{date_due},
2056
                $today );
1877
            $messages->{Debarred} = $debardate if ($debardate);
2057
            $messages->{Debarred} = $debardate if ($debardate);
1878
        }
2058
        }
1879
    }
2059
    }
1880
2060
1881
    # find reserves.....
2061
# find reserves.....
1882
    # if we don't have a reserve with the status W, we launch the Checkreserves routine
2062
# if we don't have a reserve with the status W, we launch the Checkreserves routine
1883
    my ($resfound, $resrec);
2063
    my ( $resfound, $resrec );
1884
    my $lookahead= C4::Context->preference('ConfirmFutureHolds'); #number of days to look for future holds
2064
    my $lookahead = C4::Context->preference('ConfirmFutureHolds')
1885
    ($resfound, $resrec, undef) = C4::Reserves::CheckReserves( $item->{'itemnumber'}, undef, $lookahead ) unless ( $item->{'withdrawn'} );
2065
      ;    #number of days to look for future holds
2066
    ( $resfound, $resrec, undef ) =
2067
      C4::Reserves::CheckReserves( $item->{'itemnumber'}, undef, $lookahead )
2068
      unless ( $item->{'withdrawn'} );
1886
    if ($resfound) {
2069
    if ($resfound) {
1887
          $resrec->{'ResFound'} = $resfound;
2070
        $resrec->{'ResFound'}   = $resfound;
1888
        $messages->{'ResFound'} = $resrec;
2071
        $messages->{'ResFound'} = $resrec;
1889
    }
2072
    }
1890
2073
1891
    # update stats?
2074
    # update stats?
1892
    # Record the fact that this book was returned.
2075
    # Record the fact that this book was returned.
1893
    UpdateStats(
2076
    UpdateStats( $branch, $stat_type, '0', '', $item->{'itemnumber'},
1894
        $branch, $stat_type, '0', '',
2077
        $biblio->{'itemtype'}, $borrowernumber, undef, $item->{'ccode'} );
1895
        $item->{'itemnumber'},
1896
        $biblio->{'itemtype'},
1897
        $borrowernumber, undef, $item->{'ccode'}
1898
    );
1899
2078
1900
    # Send a check-in slip. # NOTE: borrower may be undef.  probably shouldn't try to send messages then.
2079
# Send a check-in slip. # NOTE: borrower may be undef.  probably shouldn't try to send messages then.
1901
    my $circulation_alert = 'C4::ItemCirculationAlertPreference';
2080
    my $circulation_alert = 'C4::ItemCirculationAlertPreference';
1902
    my %conditions = (
2081
    my %conditions        = (
1903
        branchcode   => $branch,
2082
        branchcode   => $branch,
1904
        categorycode => $borrower->{categorycode},
2083
        categorycode => $borrower->{categorycode},
1905
        item_type    => $item->{itype},
2084
        item_type    => $item->{itype},
1906
        notification => 'CHECKIN',
2085
        notification => 'CHECKIN',
1907
    );
2086
    );
1908
    if ($doreturn && $circulation_alert->is_enabled_for(\%conditions)) {
2087
    if ( $doreturn && $circulation_alert->is_enabled_for( \%conditions ) ) {
1909
        SendCirculationAlert({
2088
        SendCirculationAlert(
1910
            type     => 'CHECKIN',
2089
            {
1911
            item     => $item,
2090
                type     => 'CHECKIN',
1912
            borrower => $borrower,
2091
                item     => $item,
1913
            branch   => $branch,
2092
                borrower => $borrower,
1914
        });
2093
                branch   => $branch,
1915
    }
2094
            }
1916
    
2095
        );
1917
    logaction("CIRCULATION", "RETURN", $borrowernumber, $item->{'itemnumber'})
2096
    }
1918
        if C4::Context->preference("ReturnLog");
2097
1919
    
2098
    logaction( "CIRCULATION", "RETURN", $borrowernumber, $item->{'itemnumber'} )
2099
      if C4::Context->preference("ReturnLog");
2100
1920
    # Remove any OVERDUES related debarment if the borrower has no overdues
2101
    # Remove any OVERDUES related debarment if the borrower has no overdues
1921
    if ( $borrowernumber
2102
    if (
1922
      && $borrower->{'debarred'}
2103
           $borrowernumber
1923
      && C4::Context->preference('AutoRemoveOverduesRestrictions')
2104
        && $borrower->{'debarred'}
1924
      && !HasOverdues( $borrowernumber )
2105
        && C4::Context->preference('AutoRemoveOverduesRestrictions')
1925
      && @{ GetDebarments({ borrowernumber => $borrowernumber, type => 'OVERDUES' }) }
2106
        && !HasOverdues($borrowernumber)
1926
    ) {
2107
        && @{
1927
        DelUniqueDebarment({ borrowernumber => $borrowernumber, type => 'OVERDUES' });
2108
            GetDebarments(
1928
    }
2109
                { borrowernumber => $borrowernumber, type => 'OVERDUES' }
1929
2110
            )
1930
    # FIXME: make this comment intelligible.
2111
        }
1931
    #adding message if holdingbranch is non equal a userenv branch to return the document to homebranch
2112
      )
1932
    #we check, if we don't have reserv or transfert for this document, if not, return it to homebranch .
2113
    {
1933
2114
        DelUniqueDebarment(
1934
    if (($doreturn or $messages->{'NotIssued'}) and !$resfound and ($branch ne $hbr) and not $messages->{'WrongTransfer'}){
2115
            { borrowernumber => $borrowernumber, type => 'OVERDUES' } );
1935
        if ( C4::Context->preference("AutomaticItemReturn"    ) or
2116
    }
1936
            (C4::Context->preference("UseBranchTransferLimits") and
2117
1937
             ! IsBranchTransferAllowed($branch, $hbr, $item->{C4::Context->preference("BranchTransferLimitsType")} )
2118
# FIXME: make this comment intelligible.
1938
           )) {
2119
#adding message if holdingbranch is non equal a userenv branch to return the document to homebranch
1939
            $debug and warn sprintf "about to call ModItemTransfer(%s, %s, %s)", $item->{'itemnumber'},$branch, $hbr;
2120
#we check, if we don't have reserv or transfert for this document, if not, return it to homebranch .
2121
2122
    if (    ( $doreturn or $messages->{'NotIssued'} )
2123
        and !$resfound
2124
        and ( $branch ne $hbr )
2125
        and not $messages->{'WrongTransfer'} )
2126
    {
2127
        if (
2128
            C4::Context->preference("AutomaticItemReturn")
2129
            or (
2130
                C4::Context->preference("UseBranchTransferLimits")
2131
                and !IsBranchTransferAllowed(
2132
                    $branch,
2133
                    $hbr,
2134
                    $item->{ C4::Context->preference("BranchTransferLimitsType")
2135
                    }
2136
                )
2137
            )
2138
          )
2139
        {
2140
            $debug
2141
              and warn sprintf "about to call ModItemTransfer(%s, %s, %s)",
2142
              $item->{'itemnumber'}, $branch, $hbr;
1940
            $debug and warn "item: " . Dumper($item);
2143
            $debug and warn "item: " . Dumper($item);
1941
            ModItemTransfer($item->{'itemnumber'}, $branch, $hbr);
2144
            ModItemTransfer( $item->{'itemnumber'}, $branch, $hbr );
1942
            $messages->{'WasTransfered'} = 1;
2145
            $messages->{'WasTransfered'} = 1;
1943
        } else {
2146
        }
1944
            $messages->{'NeedsTransfer'} = 1;   # TODO: instead of 1, specify branchcode that the transfer SHOULD go to, $item->{homebranch}
2147
        else {
2148
            $messages->{'NeedsTransfer'} = 1
2149
              ; # TODO: instead of 1, specify branchcode that the transfer SHOULD go to, $item->{homebranch}
1945
        }
2150
        }
1946
    }
2151
    }
1947
    return ( $doreturn, $messages, $issue, $borrower );
2152
    return ( $doreturn, $messages, $issue, $borrower );
Lines 1971-2016 routine in C<C4::Accounts>. Link Here
1971
=cut
2176
=cut
1972
2177
1973
sub MarkIssueReturned {
2178
sub MarkIssueReturned {
1974
    my ( $borrowernumber, $itemnumber, $dropbox_branch, $returndate, $privacy ) = @_;
2179
    my ( $borrowernumber, $itemnumber, $dropbox_branch, $returndate, $privacy )
2180
      = @_;
1975
2181
1976
    my $dbh   = C4::Context->dbh;
2182
    my $dbh   = C4::Context->dbh;
1977
    my $query = 'UPDATE issues SET returndate=';
2183
    my $query = 'UPDATE issues SET returndate=';
1978
    my @bind;
2184
    my @bind;
1979
    if ($dropbox_branch) {
2185
    if ($dropbox_branch) {
1980
        my $calendar = Koha::Calendar->new( branchcode => $dropbox_branch );
2186
        my $calendar = Koha::Calendar->new( branchcode => $dropbox_branch );
1981
        my $dropboxdate = $calendar->addDate( DateTime->now( time_zone => C4::Context->tz), -1 );
2187
        my $dropboxdate =
2188
          $calendar->addDate( DateTime->now( time_zone => C4::Context->tz ),
2189
            -1 );
1982
        $query .= ' ? ';
2190
        $query .= ' ? ';
1983
        push @bind, $dropboxdate->strftime('%Y-%m-%d %H:%M');
2191
        push @bind, $dropboxdate->strftime('%Y-%m-%d %H:%M');
1984
    } elsif ($returndate) {
2192
    }
2193
    elsif ($returndate) {
1985
        $query .= ' ? ';
2194
        $query .= ' ? ';
1986
        push @bind, $returndate;
2195
        push @bind, $returndate;
1987
    } else {
2196
    }
2197
    else {
1988
        $query .= ' now() ';
2198
        $query .= ' now() ';
1989
    }
2199
    }
1990
    $query .= ' WHERE  borrowernumber = ?  AND itemnumber = ?';
2200
    $query .= ' WHERE  borrowernumber = ?  AND itemnumber = ?';
1991
    push @bind, $borrowernumber, $itemnumber;
2201
    push @bind, $borrowernumber, $itemnumber;
2202
1992
    # FIXME transaction
2203
    # FIXME transaction
1993
    my $sth_upd  = $dbh->prepare($query);
2204
    my $sth_upd = $dbh->prepare($query);
1994
    $sth_upd->execute(@bind);
2205
    $sth_upd->execute(@bind);
1995
    my $sth_copy = $dbh->prepare('INSERT INTO old_issues SELECT * FROM issues
2206
    my $sth_copy = $dbh->prepare(
2207
        'INSERT INTO old_issues SELECT * FROM issues
1996
                                  WHERE borrowernumber = ?
2208
                                  WHERE borrowernumber = ?
1997
                                  AND itemnumber = ?');
2209
                                  AND itemnumber = ?'
1998
    $sth_copy->execute($borrowernumber, $itemnumber);
2210
    );
1999
    # anonymise patron checkout immediately if $privacy set to 2 and AnonymousPatron is set to a valid borrowernumber
2211
    $sth_copy->execute( $borrowernumber, $itemnumber );
2000
    if ( $privacy == 2) {
2212
2001
        # The default of 0 does not work due to foreign key constraints
2213
# anonymise patron checkout immediately if $privacy set to 2 and AnonymousPatron is set to a valid borrowernumber
2002
        # The anonymisation will fail quietly if AnonymousPatron is not a valid entry
2214
    if ( $privacy == 2 ) {
2003
        # FIXME the above is unacceptable - bug 9942 relates
2215
2004
        my $anonymouspatron = (C4::Context->preference('AnonymousPatron')) ? C4::Context->preference('AnonymousPatron') : 0;
2216
   # The default of 0 does not work due to foreign key constraints
2005
        my $sth_ano = $dbh->prepare("UPDATE old_issues SET borrowernumber=?
2217
   # The anonymisation will fail quietly if AnonymousPatron is not a valid entry
2218
   # FIXME the above is unacceptable - bug 9942 relates
2219
        my $anonymouspatron =
2220
          ( C4::Context->preference('AnonymousPatron') )
2221
          ? C4::Context->preference('AnonymousPatron')
2222
          : 0;
2223
        my $sth_ano = $dbh->prepare(
2224
            "UPDATE old_issues SET borrowernumber=?
2006
                                  WHERE borrowernumber = ?
2225
                                  WHERE borrowernumber = ?
2007
                                  AND itemnumber = ?");
2226
                                  AND itemnumber = ?"
2008
       $sth_ano->execute($anonymouspatron, $borrowernumber, $itemnumber);
2227
        );
2228
        $sth_ano->execute( $anonymouspatron, $borrowernumber, $itemnumber );
2009
    }
2229
    }
2010
    my $sth_del  = $dbh->prepare("DELETE FROM issues
2230
    my $sth_del = $dbh->prepare(
2231
        "DELETE FROM issues
2011
                                  WHERE borrowernumber = ?
2232
                                  WHERE borrowernumber = ?
2012
                                  AND itemnumber = ?");
2233
                                  AND itemnumber = ?"
2013
    $sth_del->execute($borrowernumber, $itemnumber);
2234
    );
2235
    $sth_del->execute( $borrowernumber, $itemnumber );
2014
}
2236
}
2015
2237
2016
=head2 _debar_user_on_return
2238
=head2 _debar_user_on_return
Lines 2061-2071 sub _debar_user_on_return { Link Here
2061
            my $new_debar_dt =
2283
            my $new_debar_dt =
2062
              $dt_today->clone()->add_duration( $deltadays * $finedays );
2284
              $dt_today->clone()->add_duration( $deltadays * $finedays );
2063
2285
2064
            Koha::Borrower::Debarments::AddUniqueDebarment({
2286
            Koha::Borrower::Debarments::AddUniqueDebarment(
2065
                borrowernumber => $borrower->{borrowernumber},
2287
                {
2066
                expiration     => $new_debar_dt->ymd(),
2288
                    borrowernumber => $borrower->{borrowernumber},
2067
                type           => 'SUSPENSION',
2289
                    expiration     => $new_debar_dt->ymd(),
2068
            });
2290
                    type           => 'SUSPENSION',
2291
                }
2292
            );
2069
2293
2070
            return $new_debar_dt->ymd();
2294
            return $new_debar_dt->ymd();
2071
        }
2295
        }
Lines 2089-2104 Internal function, called only by AddReturn Link Here
2089
=cut
2313
=cut
2090
2314
2091
sub _FixOverduesOnReturn {
2315
sub _FixOverduesOnReturn {
2092
    my ($borrowernumber, $item);
2316
    my ( $borrowernumber, $item );
2093
    unless ($borrowernumber = shift) {
2317
    unless ( $borrowernumber = shift ) {
2094
        warn "_FixOverduesOnReturn() not supplied valid borrowernumber";
2318
        warn "_FixOverduesOnReturn() not supplied valid borrowernumber";
2095
        return;
2319
        return;
2096
    }
2320
    }
2097
    unless ($item = shift) {
2321
    unless ( $item = shift ) {
2098
        warn "_FixOverduesOnReturn() not supplied valid itemnumber";
2322
        warn "_FixOverduesOnReturn() not supplied valid itemnumber";
2099
        return;
2323
        return;
2100
    }
2324
    }
2101
    my ($exemptfine, $dropbox) = @_;
2325
    my ( $exemptfine, $dropbox ) = @_;
2102
    my $dbh = C4::Context->dbh;
2326
    my $dbh = C4::Context->dbh;
2103
2327
2104
    # check for overdue fine
2328
    # check for overdue fine
Lines 2112-2135 sub _FixOverduesOnReturn { Link Here
2112
    return 0 unless $data;    # no warning, there's just nothing to fix
2336
    return 0 unless $data;    # no warning, there's just nothing to fix
2113
2337
2114
    my $uquery;
2338
    my $uquery;
2115
    my @bind = ($data->{'accountlines_id'});
2339
    my @bind = ( $data->{'accountlines_id'} );
2116
    if ($exemptfine) {
2340
    if ($exemptfine) {
2117
        $uquery = "update accountlines set accounttype='FFOR', amountoutstanding=0";
2341
        $uquery =
2118
        if (C4::Context->preference("FinesLog")) {
2342
          "update accountlines set accounttype='FFOR', amountoutstanding=0";
2119
            &logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item");
2343
        if ( C4::Context->preference("FinesLog") ) {
2120
        }
2344
            &logaction( "FINES", 'MODIFY', $borrowernumber,
2121
    } elsif ($dropbox && $data->{lastincrement}) {
2345
                "Overdue forgiven: item $item" );
2122
        my $outstanding = $data->{amountoutstanding} - $data->{lastincrement} ;
2346
        }
2123
        my $amt = $data->{amount} - $data->{lastincrement} ;
2347
    }
2124
        if (C4::Context->preference("FinesLog")) {
2348
    elsif ( $dropbox && $data->{lastincrement} ) {
2125
            &logaction("FINES", 'MODIFY',$borrowernumber,"Dropbox adjustment $amt, item $item");
2349
        my $outstanding = $data->{amountoutstanding} - $data->{lastincrement};
2126
        }
2350
        my $amt         = $data->{amount} - $data->{lastincrement};
2127
         $uquery = "update accountlines set accounttype='F' ";
2351
        if ( C4::Context->preference("FinesLog") ) {
2128
         if($outstanding  >= 0 && $amt >=0) {
2352
            &logaction( "FINES", 'MODIFY', $borrowernumber,
2353
                "Dropbox adjustment $amt, item $item" );
2354
        }
2355
        $uquery = "update accountlines set accounttype='F' ";
2356
        if ( $outstanding >= 0 && $amt >= 0 ) {
2129
            $uquery .= ", amount = ? , amountoutstanding=? ";
2357
            $uquery .= ", amount = ? , amountoutstanding=? ";
2130
            unshift @bind, ($amt, $outstanding) ;
2358
            unshift @bind, ( $amt, $outstanding );
2131
        }
2359
        }
2132
    } else {
2360
    }
2361
    else {
2133
        $uquery = "update accountlines set accounttype='F' ";
2362
        $uquery = "update accountlines set accounttype='F' ";
2134
    }
2363
    }
2135
    $uquery .= " where (accountlines_id = ?)";
2364
    $uquery .= " where (accountlines_id = ?)";
Lines 2151-2162 FIXME: Give a positive return value on success. It might be the $borrowernumber Link Here
2151
=cut
2380
=cut
2152
2381
2153
sub _FixAccountForLostAndReturned {
2382
sub _FixAccountForLostAndReturned {
2154
    my $itemnumber     = shift or return;
2383
    my $itemnumber = shift or return;
2155
    my $borrowernumber = @_ ? shift : undef;
2384
    my $borrowernumber = @_ ? shift : undef;
2156
    my $item_id        = @_ ? shift : $itemnumber;  # Send the barcode if you want that logged in the description
2385
    my $item_id =
2386
      @_
2387
      ? shift
2388
      : $itemnumber
2389
      ;    # Send the barcode if you want that logged in the description
2157
    my $dbh = C4::Context->dbh;
2390
    my $dbh = C4::Context->dbh;
2391
2158
    # check for charge made for lost book
2392
    # check for charge made for lost book
2159
    my $sth = $dbh->prepare("SELECT * FROM accountlines WHERE itemnumber = ? AND accounttype IN ('L', 'Rep', 'W') ORDER BY date DESC, accountno DESC");
2393
    my $sth = $dbh->prepare(
2394
"SELECT * FROM accountlines WHERE itemnumber = ? AND accounttype IN ('L', 'Rep', 'W') ORDER BY date DESC, accountno DESC"
2395
    );
2160
    $sth->execute($itemnumber);
2396
    $sth->execute($itemnumber);
2161
    my $data = $sth->fetchrow_hashref;
2397
    my $data = $sth->fetchrow_hashref;
2162
    $data or return;    # bail if there is nothing to do
2398
    $data or return;    # bail if there is nothing to do
Lines 2166-2226 sub _FixAccountForLostAndReturned { Link Here
2166
    my $offset;
2402
    my $offset;
2167
    my $amount = $data->{'amount'};
2403
    my $amount = $data->{'amount'};
2168
    my $acctno = $data->{'accountno'};
2404
    my $acctno = $data->{'accountno'};
2169
    my $amountleft;                                             # Starts off undef/zero.
2405
    my $amountleft;                            # Starts off undef/zero.
2170
    if ($data->{'amountoutstanding'} == $amount) {
2406
    if ( $data->{'amountoutstanding'} == $amount ) {
2171
        $offset     = $data->{'amount'};
2407
        $offset     = $data->{'amount'};
2172
        $amountleft = 0;                                        # Hey, it's zero here, too.
2408
        $amountleft = 0;                       # Hey, it's zero here, too.
2173
    } else {
2409
    }
2174
        $offset     = $amount - $data->{'amountoutstanding'};   # Um, isn't this the same as ZERO?  We just tested those two things are ==
2410
    else {
2175
        $amountleft = $data->{'amountoutstanding'} - $amount;   # Um, isn't this the same as ZERO?  We just tested those two things are ==
2411
        $offset = $amount - $data->{'amountoutstanding'}
2176
    }
2412
          ; # Um, isn't this the same as ZERO?  We just tested those two things are ==
2177
    my $usth = $dbh->prepare("UPDATE accountlines SET accounttype = 'LR',amountoutstanding='0'
2413
        $amountleft =
2178
        WHERE (accountlines_id = ?)");
2414
          $data->{'amountoutstanding'} -
2179
    $usth->execute($data->{'accountlines_id'});      # We might be adjusting an account for some OTHER borrowernumber now.  Not the one we passed in.
2415
          $amount
2180
    #check if any credit is left if so writeoff other accounts
2416
          ; # Um, isn't this the same as ZERO?  We just tested those two things are ==
2181
    my $nextaccntno = getnextacctno($data->{'borrowernumber'});
2417
    }
2182
    $amountleft *= -1 if ($amountleft < 0);
2418
    my $usth = $dbh->prepare(
2183
    if ($amountleft > 0) {
2419
        "UPDATE accountlines SET accounttype = 'LR',amountoutstanding='0'
2184
        my $msth = $dbh->prepare("SELECT * FROM accountlines WHERE (borrowernumber = ?)
2420
        WHERE (accountlines_id = ?)"
2185
                            AND (amountoutstanding >0) ORDER BY date");     # might want to order by amountoustanding ASC (pay smallest first)
2421
    );
2186
        $msth->execute($data->{'borrowernumber'});
2422
    $usth->execute( $data->{'accountlines_id'} )
2423
      ; # We might be adjusting an account for some OTHER borrowernumber now.  Not the one we passed in.
2424
        #check if any credit is left if so writeoff other accounts
2425
    my $nextaccntno = getnextacctno( $data->{'borrowernumber'} );
2426
    $amountleft *= -1 if ( $amountleft < 0 );
2427
    if ( $amountleft > 0 ) {
2428
        my $msth = $dbh->prepare(
2429
            "SELECT * FROM accountlines WHERE (borrowernumber = ?)
2430
                            AND (amountoutstanding >0) ORDER BY date"
2431
        );    # might want to order by amountoustanding ASC (pay smallest first)
2432
        $msth->execute( $data->{'borrowernumber'} );
2433
2187
        # offset transactions
2434
        # offset transactions
2188
        my $newamtos;
2435
        my $newamtos;
2189
        my $accdata;
2436
        my $accdata;
2190
        while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
2437
        while ( ( $accdata = $msth->fetchrow_hashref ) and ( $amountleft > 0 ) )
2191
            if ($accdata->{'amountoutstanding'} < $amountleft) {
2438
        {
2439
            if ( $accdata->{'amountoutstanding'} < $amountleft ) {
2192
                $newamtos = 0;
2440
                $newamtos = 0;
2193
                $amountleft -= $accdata->{'amountoutstanding'};
2441
                $amountleft -= $accdata->{'amountoutstanding'};
2194
            }  else {
2442
            }
2195
                $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
2443
            else {
2444
                $newamtos   = $accdata->{'amountoutstanding'} - $amountleft;
2196
                $amountleft = 0;
2445
                $amountleft = 0;
2197
            }
2446
            }
2198
            my $thisacct = $accdata->{'accountlines_id'};
2447
            my $thisacct = $accdata->{'accountlines_id'};
2448
2199
            # FIXME: move prepares outside while loop!
2449
            # FIXME: move prepares outside while loop!
2200
            my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
2450
            my $usth = $dbh->prepare(
2201
                    WHERE (accountlines_id = ?)");
2451
                "UPDATE accountlines SET amountoutstanding= ?
2202
            $usth->execute($newamtos,$thisacct);
2452
                    WHERE (accountlines_id = ?)"
2203
            $usth = $dbh->prepare("INSERT INTO accountoffsets
2453
            );
2454
            $usth->execute( $newamtos, $thisacct );
2455
            $usth = $dbh->prepare(
2456
                "INSERT INTO accountoffsets
2204
                (borrowernumber, accountno, offsetaccount,  offsetamount)
2457
                (borrowernumber, accountno, offsetaccount,  offsetamount)
2205
                VALUES
2458
                VALUES
2206
                (?,?,?,?)");
2459
                (?,?,?,?)"
2207
            $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos);
2460
            );
2461
            $usth->execute(
2462
                $data->{'borrowernumber'},
2463
                $accdata->{'accountno'},
2464
                $nextaccntno, $newamtos
2465
            );
2208
        }
2466
        }
2209
    }
2467
    }
2210
    $amountleft *= -1 if ($amountleft > 0);
2468
    $amountleft *= -1 if ( $amountleft > 0 );
2211
    my $desc = "Item Returned " . $item_id;
2469
    my $desc = "Item Returned " . $item_id;
2212
    $usth = $dbh->prepare("INSERT INTO accountlines
2470
    $usth = $dbh->prepare(
2471
        "INSERT INTO accountlines
2213
        (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
2472
        (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
2214
        VALUES (?,?,now(),?,?,'CR',?)");
2473
        VALUES (?,?,now(),?,?,'CR',?)"
2215
    $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft);
2474
    );
2475
    $usth->execute(
2476
        $data->{'borrowernumber'},
2477
        $nextaccntno, 0 - $amount,
2478
        $desc, $amountleft
2479
    );
2216
    if ($borrowernumber) {
2480
    if ($borrowernumber) {
2481
2217
        # FIXME: same as query above.  use 1 sth for both
2482
        # FIXME: same as query above.  use 1 sth for both
2218
        $usth = $dbh->prepare("INSERT INTO accountoffsets
2483
        $usth = $dbh->prepare(
2484
            "INSERT INTO accountoffsets
2219
            (borrowernumber, accountno, offsetaccount,  offsetamount)
2485
            (borrowernumber, accountno, offsetaccount,  offsetamount)
2220
            VALUES (?,?,?,?)");
2486
            VALUES (?,?,?,?)"
2221
        $usth->execute($borrowernumber, $data->{'accountno'}, $nextaccntno, $offset);
2487
        );
2488
        $usth->execute( $borrowernumber, $data->{'accountno'}, $nextaccntno,
2489
            $offset );
2222
    }
2490
    }
2223
    ModItem({ paidfor => '' }, undef, $itemnumber);
2491
    ModItem( { paidfor => '' }, undef, $itemnumber );
2224
    return;
2492
    return;
2225
}
2493
}
2226
2494
Lines 2241-2271 C<$borrower> is a hashref to borrower. Only {branchcode} is used. Link Here
2241
=cut
2509
=cut
2242
2510
2243
sub _GetCircControlBranch {
2511
sub _GetCircControlBranch {
2244
    my ($item, $borrower) = @_;
2512
    my ( $item, $borrower ) = @_;
2245
    my $circcontrol = C4::Context->preference('CircControl');
2513
    my $circcontrol = C4::Context->preference('CircControl');
2246
    my $branch;
2514
    my $branch;
2247
2515
2248
    if ($circcontrol eq 'PickupLibrary' and (C4::Context->userenv and C4::Context->userenv->{'branch'}) ) {
2516
    if ( $circcontrol eq 'PickupLibrary'
2249
        $branch= C4::Context->userenv->{'branch'};
2517
        and ( C4::Context->userenv and C4::Context->userenv->{'branch'} ) )
2250
    } elsif ($circcontrol eq 'PatronLibrary') {
2518
    {
2251
        $branch=$borrower->{branchcode};
2519
        $branch = C4::Context->userenv->{'branch'};
2252
    } else {
2520
    }
2253
        my $branchfield = C4::Context->preference('HomeOrHoldingBranch') || 'homebranch';
2521
    elsif ( $circcontrol eq 'PatronLibrary' ) {
2522
        $branch = $borrower->{branchcode};
2523
    }
2524
    else {
2525
        my $branchfield =
2526
          C4::Context->preference('HomeOrHoldingBranch') || 'homebranch';
2254
        $branch = $item->{$branchfield};
2527
        $branch = $item->{$branchfield};
2528
2255
        # default to item home branch if holdingbranch is used
2529
        # default to item home branch if holdingbranch is used
2256
        # and is not defined
2530
        # and is not defined
2257
        if (!defined($branch) && $branchfield eq 'holdingbranch') {
2531
        if ( !defined($branch) && $branchfield eq 'holdingbranch' ) {
2258
            $branch = $item->{homebranch};
2532
            $branch = $item->{homebranch};
2259
        }
2533
        }
2260
    }
2534
    }
2261
    return $branch;
2535
    return $branch;
2262
}
2536
}
2263
2537
2264
2265
2266
2267
2268
2269
=head2 GetItemIssue
2538
=head2 GetItemIssue
2270
2539
2271
  $issue = &GetItemIssue($itemnumber);
2540
  $issue = &GetItemIssue($itemnumber);
Lines 2285-2300 sub GetItemIssue { Link Here
2285
        "SELECT items.*, issues.*
2554
        "SELECT items.*, issues.*
2286
        FROM issues
2555
        FROM issues
2287
        LEFT JOIN items ON issues.itemnumber=items.itemnumber
2556
        LEFT JOIN items ON issues.itemnumber=items.itemnumber
2288
        WHERE issues.itemnumber=?");
2557
        WHERE issues.itemnumber=?"
2558
    );
2289
    $sth->execute($itemnumber);
2559
    $sth->execute($itemnumber);
2290
    my $data = $sth->fetchrow_hashref;
2560
    my $data = $sth->fetchrow_hashref;
2291
    return unless $data;
2561
    return unless $data;
2292
    $data->{issuedate} = dt_from_string($data->{issuedate}, 'sql');
2562
    $data->{issuedate} = dt_from_string( $data->{issuedate}, 'sql' );
2293
    $data->{issuedate}->truncate(to => 'minute');
2563
    $data->{issuedate}->truncate( to => 'minute' );
2294
    $data->{date_due} = dt_from_string($data->{date_due}, 'sql');
2564
    $data->{date_due} = dt_from_string( $data->{date_due}, 'sql' );
2295
    $data->{date_due}->truncate(to => 'minute');
2565
    $data->{date_due}->truncate( to => 'minute' );
2296
    my $dt = DateTime->now( time_zone => C4::Context->tz)->truncate( to => 'minute');
2566
    my $dt =
2297
    $data->{'overdue'} = DateTime->compare($data->{'date_due'}, $dt ) == -1 ? 1 : 0;
2567
      DateTime->now( time_zone => C4::Context->tz )->truncate( to => 'minute' );
2568
    $data->{'overdue'} =
2569
      DateTime->compare( $data->{'date_due'}, $dt ) == -1 ? 1 : 0;
2298
    return $data;
2570
    return $data;
2299
}
2571
}
2300
2572
Lines 2311-2322 Returns a hashref Link Here
2311
=cut
2583
=cut
2312
2584
2313
sub GetOpenIssue {
2585
sub GetOpenIssue {
2314
  my ( $itemnumber ) = @_;
2586
    my ($itemnumber) = @_;
2315
  return unless $itemnumber;
2587
    return unless $itemnumber;
2316
  my $dbh = C4::Context->dbh;  
2588
    my $dbh = C4::Context->dbh;
2317
  my $sth = $dbh->prepare( "SELECT * FROM issues WHERE itemnumber = ? AND returndate IS NULL" );
2589
    my $sth = $dbh->prepare(
2318
  $sth->execute( $itemnumber );
2590
        "SELECT * FROM issues WHERE itemnumber = ? AND returndate IS NULL");
2319
  return $sth->fetchrow_hashref();
2591
    $sth->execute($itemnumber);
2592
    return $sth->fetchrow_hashref();
2320
2593
2321
}
2594
}
2322
2595
Lines 2336-2343 Returns reference to an array of hashes Link Here
2336
2609
2337
sub GetItemIssues {
2610
sub GetItemIssues {
2338
    my ( $itemnumber, $history ) = @_;
2611
    my ( $itemnumber, $history ) = @_;
2339
    
2612
2340
    my $today = DateTime->now( time_zome => C4::Context->tz);  # get today date
2613
    my $today = DateTime->now( time_zome => C4::Context->tz );  # get today date
2341
    $today->truncate( to => 'minute' );
2614
    $today->truncate( to => 'minute' );
2342
    my $sql = "SELECT * FROM issues
2615
    my $sql = "SELECT * FROM issues
2343
              JOIN borrowers USING (borrowernumber)
2616
              JOIN borrowers USING (borrowernumber)
Lines 2353-2368 sub GetItemIssues { Link Here
2353
    $sql .= "ORDER BY date_due DESC";
2626
    $sql .= "ORDER BY date_due DESC";
2354
    my $sth = C4::Context->dbh->prepare($sql);
2627
    my $sth = C4::Context->dbh->prepare($sql);
2355
    if ($history) {
2628
    if ($history) {
2356
        $sth->execute($itemnumber, $itemnumber);
2629
        $sth->execute( $itemnumber, $itemnumber );
2357
    } else {
2630
    }
2631
    else {
2358
        $sth->execute($itemnumber);
2632
        $sth->execute($itemnumber);
2359
    }
2633
    }
2360
    my $results = $sth->fetchall_arrayref({});
2634
    my $results = $sth->fetchall_arrayref( {} );
2361
    foreach (@$results) {
2635
    foreach (@$results) {
2362
        my $date_due = dt_from_string($_->{date_due},'sql');
2636
        my $date_due = dt_from_string( $_->{date_due}, 'sql' );
2363
        $date_due->truncate( to => 'minute' );
2637
        $date_due->truncate( to => 'minute' );
2364
2638
2365
        $_->{overdue} = (DateTime->compare($date_due, $today) == -1) ? 1 : 0;
2639
        $_->{overdue} =
2640
          ( DateTime->compare( $date_due, $today ) == -1 ) ? 1 : 0;
2366
    }
2641
    }
2367
    return $results;
2642
    return $results;
2368
}
2643
}
Lines 2402-2408 sub GetBiblioIssues { Link Here
2402
        ORDER BY timestamp
2677
        ORDER BY timestamp
2403
    ";
2678
    ";
2404
    my $sth = $dbh->prepare($query);
2679
    my $sth = $dbh->prepare($query);
2405
    $sth->execute($biblionumber, $biblionumber);
2680
    $sth->execute( $biblionumber, $biblionumber );
2406
2681
2407
    my @issues;
2682
    my @issues;
2408
    while ( my $data = $sth->fetchrow_hashref ) {
2683
    while ( my $data = $sth->fetchrow_hashref ) {
Lines 2433-2442 HAVING days_until_due >= 0 AND days_until_due <= ? Link Here
2433
END_SQL
2708
END_SQL
2434
2709
2435
    my @bind_parameters = ( $params->{'days_in_advance'} );
2710
    my @bind_parameters = ( $params->{'days_in_advance'} );
2436
    
2711
2437
    my $sth = $dbh->prepare( $statement );
2712
    my $sth = $dbh->prepare($statement);
2438
    $sth->execute( @bind_parameters );
2713
    $sth->execute(@bind_parameters);
2439
    my $upcoming_dues = $sth->fetchall_arrayref({});
2714
    my $upcoming_dues = $sth->fetchall_arrayref( {} );
2440
2715
2441
    return $upcoming_dues;
2716
    return $upcoming_dues;
2442
}
2717
}
Lines 2478-2498 sub CanBookBeRenewed { Link Here
2478
    my $borrower = C4::Members::GetMemberDetails($borrowernumber)
2753
    my $borrower = C4::Members::GetMemberDetails($borrowernumber)
2479
      or return;
2754
      or return;
2480
2755
2481
    my $branchcode  = _GetCircControlBranch($item, $borrower);
2756
    my $branchcode = _GetCircControlBranch( $item, $borrower );
2482
2757
2483
    my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{itype}, $branchcode);
2758
    my $issuingrule =
2759
      GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode );
2484
2760
2485
    if ( ( $issuingrule->{renewalsallowed} > $itemissue->{renewals} ) || $override_limit ) {
2761
    if ( ( $issuingrule->{renewalsallowed} > $itemissue->{renewals} )
2762
        || $override_limit )
2763
    {
2486
        $renewokay = 1;
2764
        $renewokay = 1;
2487
    } else {
2765
    }
2766
    else {
2488
        $error = "too_many";
2767
        $error = "too_many";
2489
    }
2768
    }
2490
2769
2491
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves( $itemnumber );
2770
    my ( $resfound, $resrec, undef ) = C4::Reserves::CheckReserves($itemnumber);
2492
2771
2493
    if ( $resfound ) { # '' when no hold was found
2772
    if ($resfound) {    # '' when no hold was found
2494
        $renewokay = 0;
2773
        $renewokay = 0;
2495
        $error = "on_reserve";
2774
        $error     = "on_reserve";
2496
    }
2775
    }
2497
2776
2498
    return ( $renewokay, $error );
2777
    return ( $renewokay, $error );
Lines 2523-2546 from the book's item type. Link Here
2523
=cut
2802
=cut
2524
2803
2525
sub AddRenewal {
2804
sub AddRenewal {
2526
    my $borrowernumber  = shift;
2805
    my $borrowernumber = shift;
2527
    my $itemnumber      = shift or return;
2806
    my $itemnumber     = shift or return;
2528
    my $branch          = shift;
2807
    my $branch         = shift;
2529
    my $datedue         = shift;
2808
    my $datedue        = shift;
2530
    my $lastreneweddate = shift || DateTime->now(time_zone => C4::Context->tz)->ymd();
2809
    my $lastreneweddate =
2810
      shift || DateTime->now( time_zone => C4::Context->tz )->ymd();
2531
2811
2532
    my $item   = GetItem($itemnumber) or return;
2812
    my $item = GetItem($itemnumber) or return;
2533
    my $biblio = GetBiblioFromItemNumber($itemnumber) or return;
2813
    my $biblio = GetBiblioFromItemNumber($itemnumber) or return;
2534
2814
2535
    my $dbh = C4::Context->dbh;
2815
    my $dbh = C4::Context->dbh;
2536
2816
2537
    # Find the issues record for this book
2817
    # Find the issues record for this book
2538
    my $sth =
2818
    my $sth = $dbh->prepare("SELECT * FROM issues WHERE itemnumber = ?");
2539
      $dbh->prepare("SELECT * FROM issues WHERE itemnumber = ?");
2819
    $sth->execute($itemnumber);
2540
    $sth->execute( $itemnumber );
2541
    my $issuedata = $sth->fetchrow_hashref;
2820
    my $issuedata = $sth->fetchrow_hashref;
2542
2821
2543
    return unless ( $issuedata );
2822
    return unless ($issuedata);
2544
2823
2545
    $borrowernumber ||= $issuedata->{borrowernumber};
2824
    $borrowernumber ||= $issuedata->{borrowernumber};
2546
2825
Lines 2554-2591 sub AddRenewal { Link Here
2554
    # based on the value of the RenewalPeriodBase syspref.
2833
    # based on the value of the RenewalPeriodBase syspref.
2555
    unless ($datedue) {
2834
    unless ($datedue) {
2556
2835
2557
        my $borrower = C4::Members::GetMember( borrowernumber => $borrowernumber ) or return;
2836
        my $borrower =
2558
        my $itemtype = (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'};
2837
          C4::Members::GetMember( borrowernumber => $borrowernumber )
2838
          or return;
2839
        my $itemtype =
2840
          ( C4::Context->preference('item-level_itypes') )
2841
          ? $biblio->{'itype'}
2842
          : $biblio->{'itemtype'};
2559
2843
2560
        $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ?
2844
        $datedue =
2561
                                        dt_from_string( $issuedata->{date_due} ) :
2845
          ( C4::Context->preference('RenewalPeriodBase') eq 'date_due' )
2562
                                        DateTime->now( time_zone => C4::Context->tz());
2846
          ? dt_from_string( $issuedata->{date_due} )
2563
        $datedue =  CalcDateDue($datedue, $itemtype, $issuedata->{'branchcode'}, $borrower, 'is a renewal');
2847
          : DateTime->now( time_zone => C4::Context->tz() );
2848
        $datedue = CalcDateDue( $datedue, $itemtype, $issuedata->{'branchcode'},
2849
            $borrower, 'is a renewal' );
2564
    }
2850
    }
2565
2851
2566
    # Update the issues record to have the new due date, and a new count
2852
    # Update the issues record to have the new due date, and a new count
2567
    # of how many times it has been renewed.
2853
    # of how many times it has been renewed.
2568
    my $renews = $issuedata->{'renewals'} + 1;
2854
    my $renews = $issuedata->{'renewals'} + 1;
2569
    $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ?
2855
    $sth = $dbh->prepare(
2856
        "UPDATE issues SET date_due = ?, renewals = ?, lastreneweddate = ?
2570
                            WHERE borrowernumber=? 
2857
                            WHERE borrowernumber=? 
2571
                            AND itemnumber=?"
2858
                            AND itemnumber=?"
2572
    );
2859
    );
2573
2860
2574
    $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'), $renews, $lastreneweddate, $borrowernumber, $itemnumber );
2861
    $sth->execute( $datedue->strftime('%Y-%m-%d %H:%M'),
2862
        $renews, $lastreneweddate, $borrowernumber, $itemnumber );
2575
2863
2576
    # Update the renewal count on the item, and tell zebra to reindex
2864
    # Update the renewal count on the item, and tell zebra to reindex
2577
    $renews = $biblio->{'renewals'} + 1;
2865
    $renews = $biblio->{'renewals'} + 1;
2578
    ModItem({ renewals => $renews, onloan => $datedue->strftime('%Y-%m-%d %H:%M')}, $biblio->{'biblionumber'}, $itemnumber);
2866
    ModItem(
2867
        { renewals => $renews, onloan => $datedue->strftime('%Y-%m-%d %H:%M') },
2868
        $biblio->{'biblionumber'}, $itemnumber
2869
    );
2579
2870
2580
    # Charge a new rental fee, if applicable?
2871
    # Charge a new rental fee, if applicable?
2581
    my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber );
2872
    my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber );
2582
    if ( $charge > 0 ) {
2873
    if ( $charge > 0 ) {
2583
        my $accountno = getnextacctno( $borrowernumber );
2874
        my $accountno  = getnextacctno($borrowernumber);
2584
        my $item = GetBiblioFromItemNumber($itemnumber);
2875
        my $item       = GetBiblioFromItemNumber($itemnumber);
2585
        my $manager_id = 0;
2876
        my $manager_id = 0;
2586
        $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv; 
2877
        $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
2587
        $sth = $dbh->prepare(
2878
        $sth = $dbh->prepare(
2588
                "INSERT INTO accountlines
2879
            "INSERT INTO accountlines
2589
                    (date, borrowernumber, accountno, amount, manager_id,
2880
                    (date, borrowernumber, accountno, amount, manager_id,
2590
                    description,accounttype, amountoutstanding, itemnumber)
2881
                    description,accounttype, amountoutstanding, itemnumber)
2591
                    VALUES (now(),?,?,?,?,?,?,?,?)"
2882
                    VALUES (now(),?,?,?,?,?,?,?,?)"
Lines 2596-2636 sub AddRenewal { Link Here
2596
    }
2887
    }
2597
2888
2598
    # Send a renewal slip according to checkout alert preferencei
2889
    # Send a renewal slip according to checkout alert preferencei
2599
    if ( C4::Context->preference('RenewalSendNotice') eq '1') {
2890
    if ( C4::Context->preference('RenewalSendNotice') eq '1' ) {
2600
    my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 );
2891
        my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 );
2601
    my $circulation_alert = 'C4::ItemCirculationAlertPreference';
2892
        my $circulation_alert = 'C4::ItemCirculationAlertPreference';
2602
    my %conditions = (
2893
        my %conditions        = (
2603
        branchcode   => $branch,
2894
            branchcode   => $branch,
2604
        categorycode => $borrower->{categorycode},
2895
            categorycode => $borrower->{categorycode},
2605
        item_type    => $item->{itype},
2896
            item_type    => $item->{itype},
2606
        notification => 'CHECKOUT',
2897
            notification => 'CHECKOUT',
2607
    );
2898
        );
2608
    if ($circulation_alert->is_enabled_for(\%conditions)) {
2899
        if ( $circulation_alert->is_enabled_for( \%conditions ) ) {
2609
        SendCirculationAlert({
2900
            SendCirculationAlert(
2610
            type     => 'RENEWAL',
2901
                {
2611
            item     => $item,
2902
                    type     => 'RENEWAL',
2612
        borrower => $borrower,
2903
                    item     => $item,
2613
        branch   => $branch,
2904
                    borrower => $borrower,
2614
        });
2905
                    branch   => $branch,
2615
    }
2906
                }
2907
            );
2908
        }
2616
    }
2909
    }
2617
2910
2618
    # Remove any OVERDUES related debarment if the borrower has no overdues
2911
    # Remove any OVERDUES related debarment if the borrower has no overdues
2619
    my $borrower = C4::Members::GetMember( borrowernumber => $borrowernumber );
2912
    my $borrower = C4::Members::GetMember( borrowernumber => $borrowernumber );
2620
    if ( $borrowernumber
2913
    if (
2621
      && $borrower->{'debarred'}
2914
           $borrowernumber
2622
      && !HasOverdues( $borrowernumber )
2915
        && $borrower->{'debarred'}
2623
      && @{ GetDebarments({ borrowernumber => $borrowernumber, type => 'OVERDUES' }) }
2916
        && !HasOverdues($borrowernumber)
2624
    ) {
2917
        && @{
2625
        DelUniqueDebarment({ borrowernumber => $borrowernumber, type => 'OVERDUES' });
2918
            GetDebarments(
2919
                { borrowernumber => $borrowernumber, type => 'OVERDUES' }
2920
            )
2921
        }
2922
      )
2923
    {
2924
        DelUniqueDebarment(
2925
            { borrowernumber => $borrowernumber, type => 'OVERDUES' } );
2626
    }
2926
    }
2627
2927
2628
    # Log the renewal
2928
    # Log the renewal
2629
    UpdateStats( $branch, 'renew', $charge, '', $itemnumber, $item->{itype}, $borrowernumber, undef, $item->{'ccode'});
2929
    UpdateStats(
2930
        $branch,     'renew',        $charge,         '',
2931
        $itemnumber, $item->{itype}, $borrowernumber, undef,
2932
        $item->{'ccode'}
2933
    );
2630
    return $datedue;
2934
    return $datedue;
2631
}
2935
}
2632
2936
2633
sub GetRenewCount {
2937
sub GetRenewCount {
2938
2634
    # check renewal status
2939
    # check renewal status
2635
    my ( $bornum, $itemno ) = @_;
2940
    my ( $bornum, $itemno ) = @_;
2636
    my $dbh           = C4::Context->dbh;
2941
    my $dbh           = C4::Context->dbh;
Lines 2638-2645 sub GetRenewCount { Link Here
2638
    my $renewsallowed = 0;
2943
    my $renewsallowed = 0;
2639
    my $renewsleft    = 0;
2944
    my $renewsleft    = 0;
2640
2945
2641
    my $borrower = C4::Members::GetMember( borrowernumber => $bornum);
2946
    my $borrower = C4::Members::GetMember( borrowernumber => $bornum );
2642
    my $item     = GetItem($itemno); 
2947
    my $item = GetItem($itemno);
2643
2948
2644
    # Look in the issues table for this item, lent to this borrower,
2949
    # Look in the issues table for this item, lent to this borrower,
2645
    # and not yet returned.
2950
    # and not yet returned.
Lines 2653-2666 sub GetRenewCount { Link Here
2653
    $sth->execute( $bornum, $itemno );
2958
    $sth->execute( $bornum, $itemno );
2654
    my $data = $sth->fetchrow_hashref;
2959
    my $data = $sth->fetchrow_hashref;
2655
    $renewcount = $data->{'renewals'} if $data->{'renewals'};
2960
    $renewcount = $data->{'renewals'} if $data->{'renewals'};
2961
2656
    # $item and $borrower should be calculated
2962
    # $item and $borrower should be calculated
2657
    my $branchcode = _GetCircControlBranch($item, $borrower);
2963
    my $branchcode = _GetCircControlBranch( $item, $borrower );
2658
    
2964
2659
    my $issuingrule = GetIssuingRule($borrower->{categorycode}, $item->{itype}, $branchcode);
2965
    my $issuingrule =
2660
    
2966
      GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode );
2967
2661
    $renewsallowed = $issuingrule->{'renewalsallowed'};
2968
    $renewsallowed = $issuingrule->{'renewalsallowed'};
2662
    $renewsleft    = $renewsallowed - $renewcount;
2969
    $renewsleft    = $renewsallowed - $renewcount;
2663
    if($renewsleft < 0){ $renewsleft = 0; }
2970
    if ( $renewsleft < 0 ) { $renewsleft = 0; }
2664
    return ( $renewcount, $renewsallowed, $renewsleft );
2971
    return ( $renewcount, $renewsallowed, $renewsleft );
2665
}
2972
}
2666
2973
Lines 2692-2700 sub GetIssuingCharges { Link Here
2692
    # Get the book's item type and rental charge (via its biblioitem).
2999
    # Get the book's item type and rental charge (via its biblioitem).
2693
    my $charge_query = 'SELECT itemtypes.itemtype,rentalcharge FROM items
3000
    my $charge_query = 'SELECT itemtypes.itemtype,rentalcharge FROM items
2694
        LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber';
3001
        LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber';
2695
    $charge_query .= (C4::Context->preference('item-level_itypes'))
3002
    $charge_query .=
2696
        ? ' LEFT JOIN itemtypes ON items.itype = itemtypes.itemtype'
3003
      ( C4::Context->preference('item-level_itypes') )
2697
        : ' LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype';
3004
      ? ' LEFT JOIN itemtypes ON items.itype = itemtypes.itemtype'
3005
      : ' LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype';
2698
3006
2699
    $charge_query .= ' WHERE items.itemnumber =?';
3007
    $charge_query .= ' WHERE items.itemnumber =?';
2700
3008
Lines 2703-2709 sub GetIssuingCharges { Link Here
2703
    if ( my $item_data = $sth->fetchrow_hashref ) {
3011
    if ( my $item_data = $sth->fetchrow_hashref ) {
2704
        $item_type = $item_data->{itemtype};
3012
        $item_type = $item_data->{itemtype};
2705
        $charge    = $item_data->{rentalcharge};
3013
        $charge    = $item_data->{rentalcharge};
2706
        my $branch = C4::Branch::mybranch();
3014
        my $branch         = C4::Branch::mybranch();
2707
        my $discount_query = q|SELECT rentaldiscount,
3015
        my $discount_query = q|SELECT rentaldiscount,
2708
            issuingrules.itemtype, issuingrules.branchcode
3016
            issuingrules.itemtype, issuingrules.branchcode
2709
            FROM borrowers
3017
            FROM borrowers
Lines 2713-2722 sub GetIssuingCharges { Link Here
2713
            AND (issuingrules.branchcode = ? OR issuingrules.branchcode = '*')|;
3021
            AND (issuingrules.branchcode = ? OR issuingrules.branchcode = '*')|;
2714
        my $discount_sth = $dbh->prepare($discount_query);
3022
        my $discount_sth = $dbh->prepare($discount_query);
2715
        $discount_sth->execute( $borrowernumber, $item_type, $branch );
3023
        $discount_sth->execute( $borrowernumber, $item_type, $branch );
2716
        my $discount_rules = $discount_sth->fetchall_arrayref({});
3024
        my $discount_rules = $discount_sth->fetchall_arrayref( {} );
2717
        if (@{$discount_rules}) {
3025
3026
        if ( @{$discount_rules} ) {
3027
2718
            # We may have multiple rules so get the most specific
3028
            # We may have multiple rules so get the most specific
2719
            my $discount = _get_discount_from_rule($discount_rules, $branch, $item_type);
3029
            my $discount =
3030
              _get_discount_from_rule( $discount_rules, $branch, $item_type );
2720
            $charge = ( $charge * ( 100 - $discount ) ) / 100;
3031
            $charge = ( $charge * ( 100 - $discount ) ) / 100;
2721
        }
3032
        }
2722
    }
3033
    }
Lines 2726-2762 sub GetIssuingCharges { Link Here
2726
3037
2727
# Select most appropriate discount rule from those returned
3038
# Select most appropriate discount rule from those returned
2728
sub _get_discount_from_rule {
3039
sub _get_discount_from_rule {
2729
    my ($rules_ref, $branch, $itemtype) = @_;
3040
    my ( $rules_ref, $branch, $itemtype ) = @_;
2730
    my $discount;
3041
    my $discount;
2731
3042
2732
    if (@{$rules_ref} == 1) { # only 1 applicable rule use it
3043
    if ( @{$rules_ref} == 1 ) {    # only 1 applicable rule use it
2733
        $discount = $rules_ref->[0]->{rentaldiscount};
3044
        $discount = $rules_ref->[0]->{rentaldiscount};
2734
        return (defined $discount) ? $discount : 0;
3045
        return ( defined $discount ) ? $discount : 0;
2735
    }
3046
    }
3047
2736
    # could have up to 4 does one match $branch and $itemtype
3048
    # could have up to 4 does one match $branch and $itemtype
2737
    my @d = grep { $_->{branchcode} eq $branch && $_->{itemtype} eq $itemtype } @{$rules_ref};
3049
    my @d = grep { $_->{branchcode} eq $branch && $_->{itemtype} eq $itemtype }
3050
      @{$rules_ref};
2738
    if (@d) {
3051
    if (@d) {
2739
        $discount = $d[0]->{rentaldiscount};
3052
        $discount = $d[0]->{rentaldiscount};
2740
        return (defined $discount) ? $discount : 0;
3053
        return ( defined $discount ) ? $discount : 0;
2741
    }
3054
    }
3055
2742
    # do we have item type + all branches
3056
    # do we have item type + all branches
2743
    @d = grep { $_->{branchcode} eq q{*} && $_->{itemtype} eq $itemtype } @{$rules_ref};
3057
    @d = grep { $_->{branchcode} eq q{*} && $_->{itemtype} eq $itemtype }
3058
      @{$rules_ref};
2744
    if (@d) {
3059
    if (@d) {
2745
        $discount = $d[0]->{rentaldiscount};
3060
        $discount = $d[0]->{rentaldiscount};
2746
        return (defined $discount) ? $discount : 0;
3061
        return ( defined $discount ) ? $discount : 0;
2747
    }
3062
    }
3063
2748
    # do we all item types + this branch
3064
    # do we all item types + this branch
2749
    @d = grep { $_->{branchcode} eq $branch && $_->{itemtype} eq q{*} } @{$rules_ref};
3065
    @d = grep { $_->{branchcode} eq $branch && $_->{itemtype} eq q{*} }
3066
      @{$rules_ref};
2750
    if (@d) {
3067
    if (@d) {
2751
        $discount = $d[0]->{rentaldiscount};
3068
        $discount = $d[0]->{rentaldiscount};
2752
        return (defined $discount) ? $discount : 0;
3069
        return ( defined $discount ) ? $discount : 0;
2753
    }
3070
    }
3071
2754
    # so all and all (surely we wont get here)
3072
    # so all and all (surely we wont get here)
2755
    @d = grep { $_->{branchcode} eq q{*} && $_->{itemtype} eq q{*} } @{$rules_ref};
3073
    @d =
3074
      grep { $_->{branchcode} eq q{*} && $_->{itemtype} eq q{*} } @{$rules_ref};
2756
    if (@d) {
3075
    if (@d) {
2757
        $discount = $d[0]->{rentaldiscount};
3076
        $discount = $d[0]->{rentaldiscount};
2758
        return (defined $discount) ? $discount : 0;
3077
        return ( defined $discount ) ? $discount : 0;
2759
    }
3078
    }
3079
2760
    # none of the above
3080
    # none of the above
2761
    return 0;
3081
    return 0;
2762
}
3082
}
Lines 2769-2779 sub _get_discount_from_rule { Link Here
2769
3089
2770
sub AddIssuingCharge {
3090
sub AddIssuingCharge {
2771
    my ( $itemnumber, $borrowernumber, $charge ) = @_;
3091
    my ( $itemnumber, $borrowernumber, $charge ) = @_;
2772
    my $dbh = C4::Context->dbh;
3092
    my $dbh         = C4::Context->dbh;
2773
    my $nextaccntno = getnextacctno( $borrowernumber );
3093
    my $nextaccntno = getnextacctno($borrowernumber);
2774
    my $manager_id = 0;
3094
    my $manager_id  = 0;
2775
    $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
3095
    $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
2776
    my $query ="
3096
    my $query = "
2777
        INSERT INTO accountlines
3097
        INSERT INTO accountlines
2778
            (borrowernumber, itemnumber, accountno,
3098
            (borrowernumber, itemnumber, accountno,
2779
            date, amount, description, accounttype,
3099
            date, amount, description, accounttype,
Lines 2781-2787 sub AddIssuingCharge { Link Here
2781
        VALUES (?, ?, ?,now(), ?, 'Rental', 'Rent',?,?)
3101
        VALUES (?, ?, ?,now(), ?, 'Rental', 'Rent',?,?)
2782
    ";
3102
    ";
2783
    my $sth = $dbh->prepare($query);
3103
    my $sth = $dbh->prepare($query);
2784
    $sth->execute( $borrowernumber, $itemnumber, $nextaccntno, $charge, $charge, $manager_id );
3104
    $sth->execute( $borrowernumber, $itemnumber, $nextaccntno, $charge,
3105
        $charge, $manager_id );
2785
}
3106
}
2786
3107
2787
=head2 GetTransfers
3108
=head2 GetTransfers
Lines 2847-2854 sub GetTransfersFromTo { Link Here
2847
sub DeleteTransfer {
3168
sub DeleteTransfer {
2848
    my ($itemnumber) = @_;
3169
    my ($itemnumber) = @_;
2849
    return unless $itemnumber;
3170
    return unless $itemnumber;
2850
    my $dbh          = C4::Context->dbh;
3171
    my $dbh = C4::Context->dbh;
2851
    my $sth          = $dbh->prepare(
3172
    my $sth = $dbh->prepare(
2852
        "DELETE FROM branchtransfers
3173
        "DELETE FROM branchtransfers
2853
         WHERE itemnumber=?
3174
         WHERE itemnumber=?
2854
         AND datearrived IS NULL "
3175
         AND datearrived IS NULL "
Lines 2881-2901 sub AnonymiseIssueHistory { Link Here
2881
          AND borrowernumber IS NOT NULL
3202
          AND borrowernumber IS NOT NULL
2882
    ";
3203
    ";
2883
3204
2884
    # The default of 0 does not work due to foreign key constraints
3205
   # The default of 0 does not work due to foreign key constraints
2885
    # The anonymisation will fail quietly if AnonymousPatron is not a valid entry
3206
   # The anonymisation will fail quietly if AnonymousPatron is not a valid entry
2886
    my $anonymouspatron = (C4::Context->preference('AnonymousPatron')) ? C4::Context->preference('AnonymousPatron') : 0;
3207
    my $anonymouspatron =
2887
    my @bind_params = ($anonymouspatron, $date);
3208
      ( C4::Context->preference('AnonymousPatron') )
2888
    if (defined $borrowernumber) {
3209
      ? C4::Context->preference('AnonymousPatron')
2889
       $query .= " AND borrowernumber = ?";
3210
      : 0;
2890
       push @bind_params, $borrowernumber;
3211
    my @bind_params = ( $anonymouspatron, $date );
2891
    } else {
3212
    if ( defined $borrowernumber ) {
2892
       $query .= " AND (SELECT privacy FROM borrowers WHERE borrowers.borrowernumber=old_issues.borrowernumber) <> 0";
3213
        $query .= " AND borrowernumber = ?";
3214
        push @bind_params, $borrowernumber;
3215
    }
3216
    else {
3217
        $query .=
3218
" AND (SELECT privacy FROM borrowers WHERE borrowers.borrowernumber=old_issues.borrowernumber) <> 0";
2893
    }
3219
    }
2894
    my $sth = $dbh->prepare($query);
3220
    my $sth = $dbh->prepare($query);
2895
    $sth->execute(@bind_params);
3221
    $sth->execute(@bind_params);
2896
    my $anonymisation_err = $dbh->err;
3222
    my $anonymisation_err = $dbh->err;
2897
    my $rows_affected = $sth->rows;  ### doublecheck row count return function
3223
    my $rows_affected = $sth->rows;    ### doublecheck row count return function
2898
    return ($rows_affected, $anonymisation_err);
3224
    return ( $rows_affected, $anonymisation_err );
2899
}
3225
}
2900
3226
2901
=head2 SendCirculationAlert
3227
=head2 SendCirculationAlert
Lines 2937-2959 B<Example>: Link Here
2937
3263
2938
sub SendCirculationAlert {
3264
sub SendCirculationAlert {
2939
    my ($opts) = @_;
3265
    my ($opts) = @_;
2940
    my ($type, $item, $borrower, $branch) =
3266
    my ( $type, $item, $borrower, $branch ) =
2941
        ($opts->{type}, $opts->{item}, $opts->{borrower}, $opts->{branch});
3267
      ( $opts->{type}, $opts->{item}, $opts->{borrower}, $opts->{branch} );
2942
    my %message_name = (
3268
    my %message_name = (
2943
        CHECKIN  => 'Item_Check_in',
3269
        CHECKIN  => 'Item_Check_in',
2944
        CHECKOUT => 'Item_Checkout',
3270
        CHECKOUT => 'Item_Checkout',
2945
    RENEWAL  => 'Item_Checkout',
3271
        RENEWAL  => 'Item_Checkout',
2946
    );
3272
    );
2947
    my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences({
3273
    my $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences(
2948
        borrowernumber => $borrower->{borrowernumber},
3274
        {
2949
        message_name   => $message_name{$type},
3275
            borrowernumber => $borrower->{borrowernumber},
2950
    });
3276
            message_name   => $message_name{$type},
2951
    my $issues_table = ( $type eq 'CHECKOUT' || $type eq 'RENEWAL' ) ? 'issues' : 'old_issues';
3277
        }
2952
    my $letter =  C4::Letters::GetPreparedLetter (
3278
    );
2953
        module => 'circulation',
3279
    my $issues_table =
3280
      ( $type eq 'CHECKOUT' || $type eq 'RENEWAL' ) ? 'issues' : 'old_issues';
3281
    my $letter = C4::Letters::GetPreparedLetter(
3282
        module      => 'circulation',
2954
        letter_code => $type,
3283
        letter_code => $type,
2955
        branchcode => $branch,
3284
        branchcode  => $branch,
2956
        tables => {
3285
        tables      => {
2957
            $issues_table => $item->{itemnumber},
3286
            $issues_table => $item->{itemnumber},
2958
            'items'       => $item->{itemnumber},
3287
            'items'       => $item->{itemnumber},
2959
            'biblio'      => $item->{biblionumber},
3288
            'biblio'      => $item->{biblionumber},
Lines 2964-2977 sub SendCirculationAlert { Link Here
2964
    ) or return;
3293
    ) or return;
2965
3294
2966
    my @transports = keys %{ $borrower_preferences->{transports} };
3295
    my @transports = keys %{ $borrower_preferences->{transports} };
3296
2967
    # warn "no transports" unless @transports;
3297
    # warn "no transports" unless @transports;
2968
    for (@transports) {
3298
    for (@transports) {
3299
2969
        # warn "transport: $_";
3300
        # warn "transport: $_";
2970
        my $message = C4::Message->find_last_message($borrower, $type, $_);
3301
        my $message = C4::Message->find_last_message( $borrower, $type, $_ );
2971
        if (!$message) {
3302
        if ( !$message ) {
3303
2972
            #warn "create new message";
3304
            #warn "create new message";
2973
            C4::Message->enqueue($letter, $borrower, $_);
3305
            C4::Message->enqueue( $letter, $borrower, $_ );
2974
        } else {
3306
        }
3307
        else {
2975
            #warn "append to old message";
3308
            #warn "append to old message";
2976
            $message->append($letter);
3309
            $message->append($letter);
2977
            $message->update;
3310
            $message->update;
Lines 2990-3009 This function validate the line of brachtransfer but with the wrong destination Link Here
2990
=cut
3323
=cut
2991
3324
2992
sub updateWrongTransfer {
3325
sub updateWrongTransfer {
2993
    my ( $itemNumber,$waitingAtLibrary,$FromLibrary ) = @_;
3326
    my ( $itemNumber, $waitingAtLibrary, $FromLibrary ) = @_;
2994
    my $dbh = C4::Context->dbh;
3327
    my $dbh = C4::Context->dbh;
2995
# first step validate the actual line of transfert .
3328
3329
    # first step validate the actual line of transfert .
2996
    my $sth =
3330
    my $sth =
2997
            $dbh->prepare(
3331
      $dbh->prepare(
2998
            "update branchtransfers set datearrived = now(),tobranch=?,comments='wrongtransfer' where itemnumber= ? AND datearrived IS NULL"
3332
"update branchtransfers set datearrived = now(),tobranch=?,comments='wrongtransfer' where itemnumber= ? AND datearrived IS NULL"
2999
            );
3333
      );
3000
            $sth->execute($FromLibrary,$itemNumber);
3334
    $sth->execute( $FromLibrary, $itemNumber );
3001
3335
3002
# second step create a new line of branchtransfer to the right location .
3336
    # second step create a new line of branchtransfer to the right location .
3003
    ModItemTransfer($itemNumber, $FromLibrary, $waitingAtLibrary);
3337
    ModItemTransfer( $itemNumber, $FromLibrary, $waitingAtLibrary );
3004
3338
3005
#third step changing holdingbranch of item
3339
    #third step changing holdingbranch of item
3006
    UpdateHoldingbranch($FromLibrary,$itemNumber);
3340
    UpdateHoldingbranch( $FromLibrary, $itemNumber );
3007
}
3341
}
3008
3342
3009
=head2 UpdateHoldingbranch
3343
=head2 UpdateHoldingbranch
Lines 3015-3022 Simple methode for updating hodlingbranch in items BDD line Link Here
3015
=cut
3349
=cut
3016
3350
3017
sub UpdateHoldingbranch {
3351
sub UpdateHoldingbranch {
3018
    my ( $branch,$itemnumber ) = @_;
3352
    my ( $branch, $itemnumber ) = @_;
3019
    ModItem({ holdingbranch => $branch }, undef, $itemnumber);
3353
    ModItem( { holdingbranch => $branch }, undef, $itemnumber );
3020
}
3354
}
3021
3355
3022
=head2 CalcDateDue
3356
=head2 CalcDateDue
Lines 3040-3086 sub CalcDateDue { Link Here
3040
3374
3041
    # loanlength now a href
3375
    # loanlength now a href
3042
    my $loanlength =
3376
    my $loanlength =
3043
            GetLoanLength( $borrower->{'categorycode'}, $itemtype, $branch );
3377
      GetLoanLength( $borrower->{'categorycode'}, $itemtype, $branch );
3044
3378
3045
    my $length_key = ( $isrenewal and defined $loanlength->{renewalperiod} )
3379
    my $length_key =
3046
            ? qq{renewalperiod}
3380
      ( $isrenewal and defined $loanlength->{renewalperiod} )
3047
            : qq{issuelength};
3381
      ? qq{renewalperiod}
3382
      : qq{issuelength};
3048
3383
3049
    my $datedue;
3384
    my $datedue;
3050
    if ( $startdate ) {
3385
    if ($startdate) {
3051
        if (ref $startdate ne 'DateTime' ) {
3386
        if ( ref $startdate ne 'DateTime' ) {
3052
            $datedue = dt_from_string($datedue);
3387
            $datedue = dt_from_string($datedue);
3053
        } else {
3388
        }
3389
        else {
3054
            $datedue = $startdate->clone;
3390
            $datedue = $startdate->clone;
3055
        }
3391
        }
3056
    } else {
3392
    }
3393
    else {
3057
        $datedue =
3394
        $datedue =
3058
          DateTime->now( time_zone => C4::Context->tz() )
3395
          DateTime->now( time_zone => C4::Context->tz() )
3059
          ->truncate( to => 'minute' );
3396
          ->truncate( to => 'minute' );
3060
    }
3397
    }
3061
3398
3062
3063
    # calculate the datedue as normal
3399
    # calculate the datedue as normal
3064
    if ( C4::Context->preference('useDaysMode') eq 'Days' )
3400
    if ( C4::Context->preference('useDaysMode') eq 'Days' )
3065
    {    # ignoring calendar
3401
    {    # ignoring calendar
3066
        if ( $loanlength->{lengthunit} eq 'hours' ) {
3402
        if ( $loanlength->{lengthunit} eq 'hours' ) {
3067
            $datedue->add( hours => $loanlength->{$length_key} );
3403
            $datedue->add( hours => $loanlength->{$length_key} );
3068
        } else {    # days
3404
        }
3405
        else {    # days
3069
            $datedue->add( days => $loanlength->{$length_key} );
3406
            $datedue->add( days => $loanlength->{$length_key} );
3070
            $datedue->set_hour(23);
3407
            $datedue->set_hour(23);
3071
            $datedue->set_minute(59);
3408
            $datedue->set_minute(59);
3072
        }
3409
        }
3073
    } else {
3410
    }
3411
    else {
3074
        my $dur;
3412
        my $dur;
3075
        if ($loanlength->{lengthunit} eq 'hours') {
3413
        if ( $loanlength->{lengthunit} eq 'hours' ) {
3076
            $dur = DateTime::Duration->new( hours => $loanlength->{$length_key});
3414
            $dur =
3415
              DateTime::Duration->new( hours => $loanlength->{$length_key} );
3077
        }
3416
        }
3078
        else { # days
3417
        else {    # days
3079
            $dur = DateTime::Duration->new( days => $loanlength->{$length_key});
3418
            $dur =
3419
              DateTime::Duration->new( days => $loanlength->{$length_key} );
3080
        }
3420
        }
3081
        my $calendar = Koha::Calendar->new( branchcode => $branch );
3421
        my $calendar = Koha::Calendar->new( branchcode => $branch );
3082
        $datedue = $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} );
3422
        $datedue =
3083
        if ($loanlength->{lengthunit} eq 'days') {
3423
          $calendar->addDate( $datedue, $dur, $loanlength->{lengthunit} );
3424
        if ( $loanlength->{lengthunit} eq 'days' ) {
3084
            $datedue->set_hour(23);
3425
            $datedue->set_hour(23);
3085
            $datedue->set_minute(59);
3426
            $datedue->set_minute(59);
3086
        }
3427
        }
Lines 3109-3115 sub CalcDateDue { Link Here
3109
    # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
3450
    # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
3110
    if ( C4::Context->preference('ReturnBeforeExpiry') ) {
3451
    if ( C4::Context->preference('ReturnBeforeExpiry') ) {
3111
        my $expiry_dt = dt_from_string( $borrower->{dateexpiry}, 'iso' );
3452
        my $expiry_dt = dt_from_string( $borrower->{dateexpiry}, 'iso' );
3112
        $expiry_dt->set( hour => 23, minute => 59);
3453
        $expiry_dt->set( hour => 23, minute => 59 );
3113
        if ( DateTime->compare( $datedue, $expiry_dt ) == 1 ) {
3454
        if ( DateTime->compare( $datedue, $expiry_dt ) == 1 ) {
3114
            $datedue = $expiry_dt->clone;
3455
            $datedue = $expiry_dt->clone;
3115
        }
3456
        }
Lines 3118-3124 sub CalcDateDue { Link Here
3118
    return $datedue;
3459
    return $datedue;
3119
}
3460
}
3120
3461
3121
3122
=head2 CheckRepeatableHolidays
3462
=head2 CheckRepeatableHolidays
3123
3463
3124
  $countrepeatable = CheckRepeatableHoliday($itemnumber,$week_day,$branchcode);
3464
  $countrepeatable = CheckRepeatableHoliday($itemnumber,$week_day,$branchcode);
Lines 3131-3150 C<$branchcode> = localisation of issue Link Here
3131
3471
3132
=cut
3472
=cut
3133
3473
3134
sub CheckRepeatableHolidays{
3474
sub CheckRepeatableHolidays {
3135
my($itemnumber,$week_day,$branchcode)=@_;
3475
    my ( $itemnumber, $week_day, $branchcode ) = @_;
3136
my $dbh = C4::Context->dbh;
3476
    my $dbh   = C4::Context->dbh;
3137
my $query = qq|SELECT count(*)  
3477
    my $query = qq|SELECT count(*)  
3138
    FROM repeatable_holidays
3478
    FROM repeatable_holidays
3139
    WHERE branchcode=?
3479
    WHERE branchcode=?
3140
    AND weekday=?|;
3480
    AND weekday=?|;
3141
my $sth = $dbh->prepare($query);
3481
    my $sth = $dbh->prepare($query);
3142
$sth->execute($branchcode,$week_day);
3482
    $sth->execute( $branchcode, $week_day );
3143
my $result=$sth->fetchrow;
3483
    my $result = $sth->fetchrow;
3144
return $result;
3484
    return $result;
3145
}
3485
}
3146
3486
3147
3148
=head2 CheckSpecialHolidays
3487
=head2 CheckSpecialHolidays
3149
3488
3150
  $countspecial = CheckSpecialHolidays($years,$month,$day,$itemnumber,$branchcode);
3489
  $countspecial = CheckSpecialHolidays($years,$month,$day,$itemnumber,$branchcode);
Lines 3159-3178 C<$branchcode> = localisation of issue Link Here
3159
3498
3160
=cut
3499
=cut
3161
3500
3162
sub CheckSpecialHolidays{
3501
sub CheckSpecialHolidays {
3163
my ($years,$month,$day,$itemnumber,$branchcode) = @_;
3502
    my ( $years, $month, $day, $itemnumber, $branchcode ) = @_;
3164
my $dbh = C4::Context->dbh;
3503
    my $dbh   = C4::Context->dbh;
3165
my $query=qq|SELECT count(*) 
3504
    my $query = qq|SELECT count(*) 
3166
         FROM `special_holidays`
3505
         FROM `special_holidays`
3167
         WHERE year=?
3506
         WHERE year=?
3168
         AND month=?
3507
         AND month=?
3169
         AND day=?
3508
         AND day=?
3170
             AND branchcode=?
3509
             AND branchcode=?
3171
        |;
3510
        |;
3172
my $sth = $dbh->prepare($query);
3511
    my $sth = $dbh->prepare($query);
3173
$sth->execute($years,$month,$day,$branchcode);
3512
    $sth->execute( $years, $month, $day, $branchcode );
3174
my $countspecial=$sth->fetchrow ;
3513
    my $countspecial = $sth->fetchrow;
3175
return $countspecial;
3514
    return $countspecial;
3176
}
3515
}
3177
3516
3178
=head2 CheckRepeatableSpecialHolidays
3517
=head2 CheckRepeatableSpecialHolidays
Lines 3188-3224 C<$branchcode> = localisation of issue Link Here
3188
3527
3189
=cut
3528
=cut
3190
3529
3191
sub CheckRepeatableSpecialHolidays{
3530
sub CheckRepeatableSpecialHolidays {
3192
my ($month,$day,$itemnumber,$branchcode) = @_;
3531
    my ( $month, $day, $itemnumber, $branchcode ) = @_;
3193
my $dbh = C4::Context->dbh;
3532
    my $dbh   = C4::Context->dbh;
3194
my $query=qq|SELECT count(*) 
3533
    my $query = qq|SELECT count(*) 
3195
         FROM `repeatable_holidays`
3534
         FROM `repeatable_holidays`
3196
         WHERE month=?
3535
         WHERE month=?
3197
         AND day=?
3536
         AND day=?
3198
             AND branchcode=?
3537
             AND branchcode=?
3199
        |;
3538
        |;
3200
my $sth = $dbh->prepare($query);
3539
    my $sth = $dbh->prepare($query);
3201
$sth->execute($month,$day,$branchcode);
3540
    $sth->execute( $month, $day, $branchcode );
3202
my $countspecial=$sth->fetchrow ;
3541
    my $countspecial = $sth->fetchrow;
3203
return $countspecial;
3542
    return $countspecial;
3204
}
3543
}
3205
3544
3206
3545
sub CheckValidBarcode {
3207
3546
    my ($barcode) = @_;
3208
sub CheckValidBarcode{
3547
    my $dbh       = C4::Context->dbh;
3209
my ($barcode) = @_;
3548
    my $query     = qq|SELECT count(*) 
3210
my $dbh = C4::Context->dbh;
3211
my $query=qq|SELECT count(*) 
3212
         FROM items
3549
         FROM items
3213
             WHERE barcode=?
3550
             WHERE barcode=?
3214
        |;
3551
        |;
3215
my $sth = $dbh->prepare($query);
3552
    my $sth = $dbh->prepare($query);
3216
$sth->execute($barcode);
3553
    $sth->execute($barcode);
3217
my $exist=$sth->fetchrow ;
3554
    my $exist = $sth->fetchrow;
3218
return $exist;
3555
    return $exist;
3219
}
3556
}
3220
3557
3221
3222
=head2 CanItemBeTransferred
3558
=head2 CanItemBeTransferred
3223
3559
3224
A convenience function to easily check item transfer limits.
3560
A convenience function to easily check item transfer limits.
Lines 3248-3304 sub CanItemBeTransferred { Link Here
3248
    #When we check for BranchTransferLimit global settings centrally, it makes
3584
    #When we check for BranchTransferLimit global settings centrally, it makes
3249
    #  using this functionality much easier.
3585
    #  using this functionality much easier.
3250
    unless ( C4::Context->preference("UseBranchTransferLimits") == 1 ) {
3586
    unless ( C4::Context->preference("UseBranchTransferLimits") == 1 ) {
3251
           return 1;
3587
        return 1;
3252
    }
3588
    }
3253
3589
3254
    #Init parameter variables
3590
    #Init parameter variables
3255
    my ( $toBranch, $fromBranch, $item, $biblioitem ) = @_;
3591
    my ( $toBranch, $fromBranch, $item, $biblioitem ) = @_;
3256
3592
3257
    #Check the $fromBranch and set the DEFAULT value
3593
    #Check the $fromBranch and set the DEFAULT value
3258
    $fromBranch = $item->{holdingbranch} if ! defined $fromBranch;
3594
    $fromBranch = $item->{holdingbranch} if !defined $fromBranch;
3259
3595
3260
    if ( $toBranch eq $fromBranch ) { return 1; } ## Short circuit for speed.
3596
    if ( $toBranch eq $fromBranch ) { return 1; }    ## Short circuit for speed.
3261
3597
3262
    #This is the CCode or itemtype value used to find the correct transfer rule for this item.
3598
#This is the CCode or itemtype value used to find the correct transfer rule for this item.
3263
    my $code;
3599
    my $code;
3264
3600
3265
    ## Figure out the $code!
3601
    ## Figure out the $code!
3266
    ## First we need to figure out are we using CCODE or itemtype, this limits do we need $biblioitem or not.
3602
    ## First we need to figure out are we using CCODE or itemtype, this limits do we need $biblioitem or not.
3267
    ## Since $biblioitem can be optional, we cannot count that it is defined.
3603
    ## Since $biblioitem can be optional, we cannot count that it is defined.
3268
    if ( C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ) {
3604
    if ( C4::Context->preference("BranchTransferLimitsType") eq 'itemtype' ) {
3269
            if (C4::Context->preference("item-level_itypes") && defined $item->{'itype'}) {
3605
        if ( C4::Context->preference("item-level_itypes")
3270
                    $code = $item->{'itype'}; #Easiest way to get the $code is from the item.
3606
            && defined $item->{'itype'} )
3271
            }
3607
        {
3272
            elsif (defined $biblioitem->{itemtype}) {
3608
            $code =
3273
                    $code = $biblioitem->{itemtype}
3609
              $item->{'itype'};  #Easiest way to get the $code is from the item.
3274
            }
3610
        }
3275
            #If code cannot be resolved from $item or $biblioitem, we need to escalate to the DB
3611
        elsif ( defined $biblioitem->{itemtype} ) {
3276
            else {
3612
            $code = $biblioitem->{itemtype};
3277
                    $biblioitem = C4::Biblio::GetBiblioFromItemNumber( $item->{itemnumber} );
3613
        }
3278
                    $code = $biblioitem->{itemtype}
3279
            }
3280
3614
3281
    } else {
3615
#If code cannot be resolved from $item or $biblioitem, we need to escalate to the DB
3282
            my $limitType = C4::Context->preference("BranchTransferLimitsType");
3616
        else {
3283
            if (defined $item->{ $limitType }) {
3617
            $biblioitem =
3284
                    $code = $item->{ $limitType };
3618
              C4::Biblio::GetBiblioFromItemNumber( $item->{itemnumber} );
3285
            }
3619
            $code = $biblioitem->{itemtype};
3286
            else {
3620
        }
3287
                    #collection code (ccode) is not present in the Biblio, so no point looking there.
3621
3288
                    #  If the Item doesn't have a itemtype, then give up and let the transfer pass.
3622
    }
3289
            }
3623
    else {
3624
        my $limitType = C4::Context->preference("BranchTransferLimitsType");
3625
        if ( defined $item->{$limitType} ) {
3626
            $code = $item->{$limitType};
3627
        }
3628
        else {
3629
#collection code (ccode) is not present in the Biblio, so no point looking there.
3630
#  If the Item doesn't have a itemtype, then give up and let the transfer pass.
3631
        }
3290
    }
3632
    }
3291
    ## Phew we finally got the $code, now it's time to rumble!
3633
    ## Phew we finally got the $code, now it's time to rumble!
3292
3634
3293
    if (! IsBranchTransferAllowed($toBranch, $fromBranch, $code)) {
3635
    if ( !IsBranchTransferAllowed( $toBranch, $fromBranch, $code ) ) {
3294
            return 0, "$fromBranch->$toBranch->$code";
3636
        return 0, "$fromBranch->$toBranch->$code";
3295
    }
3637
    }
3296
    else {
3638
    else {
3297
            return 1;
3639
        return 1;
3298
    }
3640
    }
3299
}
3641
}
3300
3642
3301
3302
=head2 IsBranchTransferAllowed
3643
=head2 IsBranchTransferAllowed
3303
3644
3304
  $allowed = IsBranchTransferAllowed( $toBranch, $fromBranch, $code );
3645
  $allowed = IsBranchTransferAllowed( $toBranch, $fromBranch, $code );
Lines 3310-3331 Code is either an itemtype or collection doe depending on the pref BranchTransfe Link Here
3310
sub IsBranchTransferAllowed {
3651
sub IsBranchTransferAllowed {
3311
    my ( $toBranch, $fromBranch, $code ) = @_;
3652
    my ( $toBranch, $fromBranch, $code ) = @_;
3312
3653
3313
    if ( $toBranch eq $fromBranch ) { return 1; } ## Short circuit for speed.
3654
    if ( $toBranch eq $fromBranch ) { return 1; }    ## Short circuit for speed.
3314
        
3655
3315
    my $limitType = C4::Context->preference("BranchTransferLimitsType");
3656
    my $limitType = C4::Context->preference("BranchTransferLimitsType");
3316
    my $dbh = C4::Context->dbh;
3657
    my $dbh       = C4::Context->dbh;
3317
            
3658
3318
    my $sth = $dbh->prepare("SELECT * FROM branch_transfer_limits WHERE toBranch = ? AND fromBranch = ? AND $limitType = ?");
3659
    my $sth = $dbh->prepare(
3660
"SELECT * FROM branch_transfer_limits WHERE toBranch = ? AND fromBranch = ? AND $limitType = ?"
3661
    );
3319
    $sth->execute( $toBranch, $fromBranch, $code );
3662
    $sth->execute( $toBranch, $fromBranch, $code );
3320
    my $limit = $sth->fetchrow_hashref();
3663
    my $limit = $sth->fetchrow_hashref();
3321
                        
3664
3322
    ## If a row is found, then that combination is not allowed, if no matching row is found, then the combination *is allowed*
3665
    ## If a row is found, then that combination is not allowed, if no matching row is found, then the combination *is allowed*
3323
    if ( $limit->{'limitId'} ) {
3666
    if ( $limit->{'limitId'} ) {
3324
        return 0;
3667
        return 0;
3325
    } else {
3668
    }
3669
    else {
3326
        return 1;
3670
        return 1;
3327
    }
3671
    }
3328
}                                                        
3672
}
3329
3673
3330
=head2 CreateBranchTransferLimit
3674
=head2 CreateBranchTransferLimit
3331
3675
Lines 3336-3349 $code is either itemtype or collection code depending on what the pref BranchTra Link Here
3336
=cut
3680
=cut
3337
3681
3338
sub CreateBranchTransferLimit {
3682
sub CreateBranchTransferLimit {
3339
   my ( $toBranch, $fromBranch, $code ) = @_;
3683
    my ( $toBranch, $fromBranch, $code ) = @_;
3340
   return unless defined($toBranch) && defined($fromBranch);
3684
    return unless defined($toBranch) && defined($fromBranch);
3341
   my $limitType = C4::Context->preference("BranchTransferLimitsType");
3685
    my $limitType = C4::Context->preference("BranchTransferLimitsType");
3342
   
3686
3343
   my $dbh = C4::Context->dbh;
3687
    my $dbh = C4::Context->dbh;
3344
   
3688
3345
   my $sth = $dbh->prepare("INSERT INTO branch_transfer_limits ( $limitType, toBranch, fromBranch ) VALUES ( ?, ?, ? )");
3689
    my $sth = $dbh->prepare(
3346
   return $sth->execute( $code, $toBranch, $fromBranch );
3690
"INSERT INTO branch_transfer_limits ( $limitType, toBranch, fromBranch ) VALUES ( ?, ?, ? )"
3691
    );
3692
    return $sth->execute( $code, $toBranch, $fromBranch );
3347
}
3693
}
3348
3694
3349
=head2 DeleteBranchTransferLimits
3695
=head2 DeleteBranchTransferLimits
Lines 3359-3418 no arguments are supplied. Link Here
3359
sub DeleteBranchTransferLimits {
3705
sub DeleteBranchTransferLimits {
3360
    my $branch = shift;
3706
    my $branch = shift;
3361
    return unless defined $branch;
3707
    return unless defined $branch;
3362
    my $dbh    = C4::Context->dbh;
3708
    my $dbh = C4::Context->dbh;
3363
    my $sth    = $dbh->prepare("DELETE FROM branch_transfer_limits WHERE fromBranch = ?");
3709
    my $sth =
3710
      $dbh->prepare("DELETE FROM branch_transfer_limits WHERE fromBranch = ?");
3364
    return $sth->execute($branch);
3711
    return $sth->execute($branch);
3365
}
3712
}
3366
3713
3367
sub ReturnLostItem{
3714
sub ReturnLostItem {
3368
    my ( $borrowernumber, $itemnum ) = @_;
3715
    my ( $borrowernumber, $itemnum ) = @_;
3369
3716
3370
    MarkIssueReturned( $borrowernumber, $itemnum );
3717
    MarkIssueReturned( $borrowernumber, $itemnum );
3371
    my $borrower = C4::Members::GetMember( 'borrowernumber'=>$borrowernumber );
3718
    my $borrower =
3372
    my $item = C4::Items::GetItem( $itemnum );
3719
      C4::Members::GetMember( 'borrowernumber' => $borrowernumber );
3373
    my $old_note = ($item->{'paidfor'} && ($item->{'paidfor'} ne q{})) ? $item->{'paidfor'}.' / ' : q{};
3720
    my $item = C4::Items::GetItem($itemnum);
3721
    my $old_note =
3722
      ( $item->{'paidfor'} && ( $item->{'paidfor'} ne q{} ) )
3723
      ? $item->{'paidfor'} . ' / '
3724
      : q{};
3374
    my @datearr = localtime(time);
3725
    my @datearr = localtime(time);
3375
    my $date = ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
3726
    my $date =
3376
    my $bor = "$borrower->{'firstname'} $borrower->{'surname'} $borrower->{'cardnumber'}";
3727
      ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
3377
    ModItem({ paidfor =>  $old_note."Paid for by $bor $date" }, undef, $itemnum);
3728
    my $bor =
3729
"$borrower->{'firstname'} $borrower->{'surname'} $borrower->{'cardnumber'}";
3730
    ModItem( { paidfor => $old_note . "Paid for by $bor $date" },
3731
        undef, $itemnum );
3378
}
3732
}
3379
3733
3380
3734
sub LostItem {
3381
sub LostItem{
3735
    my ( $itemnumber, $mark_returned ) = @_;
3382
    my ($itemnumber, $mark_returned) = @_;
3383
3736
3384
    my $dbh = C4::Context->dbh();
3737
    my $dbh = C4::Context->dbh();
3385
    my $sth=$dbh->prepare("SELECT issues.*,items.*,biblio.title 
3738
    my $sth = $dbh->prepare(
3739
        "SELECT issues.*,items.*,biblio.title 
3386
                           FROM issues 
3740
                           FROM issues 
3387
                           JOIN items USING (itemnumber) 
3741
                           JOIN items USING (itemnumber) 
3388
                           JOIN biblio USING (biblionumber)
3742
                           JOIN biblio USING (biblionumber)
3389
                           WHERE issues.itemnumber=?");
3743
                           WHERE issues.itemnumber=?"
3744
    );
3390
    $sth->execute($itemnumber);
3745
    $sth->execute($itemnumber);
3391
    my $issues=$sth->fetchrow_hashref();
3746
    my $issues = $sth->fetchrow_hashref();
3392
3747
3393
    # If a borrower lost the item, add a replacement cost to the their record
3748
    # If a borrower lost the item, add a replacement cost to the their record
3394
    if ( my $borrowernumber = $issues->{borrowernumber} ){
3749
    if ( my $borrowernumber = $issues->{borrowernumber} ) {
3395
        my $borrower = C4::Members::GetMemberDetails( $borrowernumber );
3750
        my $borrower = C4::Members::GetMemberDetails($borrowernumber);
3751
3752
        if ( C4::Context->preference('WhenLostForgiveFine') ) {
3753
            my $fix = _FixOverduesOnReturn( $borrowernumber, $itemnumber, 1, 0 )
3754
              ;    # 1, 0 = exemptfine, no-dropbox
3755
            defined($fix)
3756
              or warn
3757
              "_FixOverduesOnReturn($borrowernumber, $itemnumber...) failed!"
3758
              ;    # zero is OK, check defined
3759
        }
3760
        if ( C4::Context->preference('WhenLostChargeReplacementFee') ) {
3761
            C4::Accounts::chargelostitem(
3762
                $borrowernumber, $itemnumber,
3763
                $issues->{'replacementprice'},
3764
                "Lost Item $issues->{'title'} $issues->{'barcode'}"
3765
            );
3396
3766
3397
        if (C4::Context->preference('WhenLostForgiveFine')){
3767
#FIXME : Should probably have a way to distinguish this from an item that really was returned.
3398
            my $fix = _FixOverduesOnReturn($borrowernumber, $itemnumber, 1, 0); # 1, 0 = exemptfine, no-dropbox
3768
#warn " $issues->{'borrowernumber'}  /  $itemnumber ";
3399
            defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $itemnumber...) failed!";  # zero is OK, check defined
3400
        }
3401
        if (C4::Context->preference('WhenLostChargeReplacementFee')){
3402
            C4::Accounts::chargelostitem($borrowernumber, $itemnumber, $issues->{'replacementprice'}, "Lost Item $issues->{'title'} $issues->{'barcode'}");
3403
            #FIXME : Should probably have a way to distinguish this from an item that really was returned.
3404
            #warn " $issues->{'borrowernumber'}  /  $itemnumber ";
3405
        }
3769
        }
3406
3770
3407
        MarkIssueReturned($borrowernumber,$itemnumber,undef,undef,$borrower->{'privacy'}) if $mark_returned;
3771
        MarkIssueReturned( $borrowernumber, $itemnumber, undef, undef,
3772
            $borrower->{'privacy'} )
3773
          if $mark_returned;
3408
    }
3774
    }
3409
}
3775
}
3410
3776
3411
sub GetOfflineOperations {
3777
sub GetOfflineOperations {
3412
    my $dbh = C4::Context->dbh;
3778
    my $dbh = C4::Context->dbh;
3413
    my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE branchcode=? ORDER BY timestamp");
3779
    my $sth = $dbh->prepare(
3414
    $sth->execute(C4::Context->userenv->{'branch'});
3780
"SELECT * FROM pending_offline_operations WHERE branchcode=? ORDER BY timestamp"
3415
    my $results = $sth->fetchall_arrayref({});
3781
    );
3782
    $sth->execute( C4::Context->userenv->{'branch'} );
3783
    my $results = $sth->fetchall_arrayref( {} );
3416
    return $results;
3784
    return $results;
3417
}
3785
}
3418
3786
Lines 3420-3442 sub GetOfflineOperation { Link Here
3420
    my $operationid = shift;
3788
    my $operationid = shift;
3421
    return unless $operationid;
3789
    return unless $operationid;
3422
    my $dbh = C4::Context->dbh;
3790
    my $dbh = C4::Context->dbh;
3423
    my $sth = $dbh->prepare("SELECT * FROM pending_offline_operations WHERE operationid=?");
3791
    my $sth = $dbh->prepare(
3424
    $sth->execute( $operationid );
3792
        "SELECT * FROM pending_offline_operations WHERE operationid=?");
3793
    $sth->execute($operationid);
3425
    return $sth->fetchrow_hashref;
3794
    return $sth->fetchrow_hashref;
3426
}
3795
}
3427
3796
3428
sub AddOfflineOperation {
3797
sub AddOfflineOperation {
3429
    my ( $userid, $branchcode, $timestamp, $action, $barcode, $cardnumber, $amount ) = @_;
3798
    my (
3799
        $userid,  $branchcode, $timestamp, $action,
3800
        $barcode, $cardnumber, $amount
3801
    ) = @_;
3430
    my $dbh = C4::Context->dbh;
3802
    my $dbh = C4::Context->dbh;
3431
    my $sth = $dbh->prepare("INSERT INTO pending_offline_operations (userid, branchcode, timestamp, action, barcode, cardnumber, amount) VALUES(?,?,?,?,?,?,?)");
3803
    my $sth = $dbh->prepare(
3432
    $sth->execute( $userid, $branchcode, $timestamp, $action, $barcode, $cardnumber, $amount );
3804
"INSERT INTO pending_offline_operations (userid, branchcode, timestamp, action, barcode, cardnumber, amount) VALUES(?,?,?,?,?,?,?)"
3805
    );
3806
    $sth->execute(
3807
        $userid,  $branchcode, $timestamp, $action,
3808
        $barcode, $cardnumber, $amount
3809
    );
3433
    return "Added.";
3810
    return "Added.";
3434
}
3811
}
3435
3812
3436
sub DeleteOfflineOperation {
3813
sub DeleteOfflineOperation {
3437
    my $dbh = C4::Context->dbh;
3814
    my $dbh = C4::Context->dbh;
3438
    my $sth = $dbh->prepare("DELETE FROM pending_offline_operations WHERE operationid=?");
3815
    my $sth = $dbh->prepare(
3439
    $sth->execute( shift );
3816
        "DELETE FROM pending_offline_operations WHERE operationid=?");
3817
    $sth->execute(shift);
3440
    return "Deleted.";
3818
    return "Deleted.";
3441
}
3819
}
3442
3820
Lines 3445-3458 sub ProcessOfflineOperation { Link Here
3445
3823
3446
    my $report;
3824
    my $report;
3447
    if ( $operation->{action} eq 'return' ) {
3825
    if ( $operation->{action} eq 'return' ) {
3448
        $report = ProcessOfflineReturn( $operation );
3826
        $report = ProcessOfflineReturn($operation);
3449
    } elsif ( $operation->{action} eq 'issue' ) {
3827
    }
3450
        $report = ProcessOfflineIssue( $operation );
3828
    elsif ( $operation->{action} eq 'issue' ) {
3451
    } elsif ( $operation->{action} eq 'payment' ) {
3829
        $report = ProcessOfflineIssue($operation);
3452
        $report = ProcessOfflinePayment( $operation );
3830
    }
3831
    elsif ( $operation->{action} eq 'payment' ) {
3832
        $report = ProcessOfflinePayment($operation);
3453
    }
3833
    }
3454
3834
3455
    DeleteOfflineOperation( $operation->{operationid} ) if $operation->{operationid};
3835
    DeleteOfflineOperation( $operation->{operationid} )
3836
      if $operation->{operationid};
3456
3837
3457
    return $report;
3838
    return $report;
3458
}
3839
}
Lines 3460-3486 sub ProcessOfflineOperation { Link Here
3460
sub ProcessOfflineReturn {
3841
sub ProcessOfflineReturn {
3461
    my $operation = shift;
3842
    my $operation = shift;
3462
3843
3463
    my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} );
3844
    my $itemnumber =
3845
      C4::Items::GetItemnumberFromBarcode( $operation->{barcode} );
3464
3846
3465
    if ( $itemnumber ) {
3847
    if ($itemnumber) {
3466
        my $issue = GetOpenIssue( $itemnumber );
3848
        my $issue = GetOpenIssue($itemnumber);
3467
        if ( $issue ) {
3849
        if ($issue) {
3468
            MarkIssueReturned(
3850
            MarkIssueReturned(
3469
                $issue->{borrowernumber},
3851
                $issue->{borrowernumber},
3470
                $itemnumber,
3852
                $itemnumber, undef, $operation->{timestamp},
3471
                undef,
3472
                $operation->{timestamp},
3473
            );
3474
            ModItem(
3475
                { renewals => 0, onloan => undef },
3476
                $issue->{'biblionumber'},
3477
                $itemnumber
3478
            );
3853
            );
3854
            ModItem( { renewals => 0, onloan => undef },
3855
                $issue->{'biblionumber'}, $itemnumber );
3479
            return "Success.";
3856
            return "Success.";
3480
        } else {
3857
        }
3858
        else {
3481
            return "Item not issued.";
3859
            return "Item not issued.";
3482
        }
3860
        }
3483
    } else {
3861
    }
3862
    else {
3484
        return "Item not found.";
3863
        return "Item not found.";
3485
    }
3864
    }
3486
}
3865
}
Lines 3488-3520 sub ProcessOfflineReturn { Link Here
3488
sub ProcessOfflineIssue {
3867
sub ProcessOfflineIssue {
3489
    my $operation = shift;
3868
    my $operation = shift;
3490
3869
3491
    my $borrower = C4::Members::GetMemberDetails( undef, $operation->{cardnumber} ); # Get borrower from operation cardnumber
3870
    my $borrower =
3871
      C4::Members::GetMemberDetails( undef, $operation->{cardnumber} )
3872
      ;    # Get borrower from operation cardnumber
3492
3873
3493
    if ( $borrower->{borrowernumber} ) {
3874
    if ( $borrower->{borrowernumber} ) {
3494
        my $itemnumber = C4::Items::GetItemnumberFromBarcode( $operation->{barcode} );
3875
        my $itemnumber =
3876
          C4::Items::GetItemnumberFromBarcode( $operation->{barcode} );
3495
        unless ($itemnumber) {
3877
        unless ($itemnumber) {
3496
            return "Barcode not found.";
3878
            return "Barcode not found.";
3497
        }
3879
        }
3498
        my $issue = GetOpenIssue( $itemnumber );
3880
        my $issue = GetOpenIssue($itemnumber);
3499
3881
3500
        if ( $issue and ( $issue->{borrowernumber} ne $borrower->{borrowernumber} ) ) { # Item already issued to another borrower, mark it returned
3882
        if ( $issue
3883
            and ( $issue->{borrowernumber} ne $borrower->{borrowernumber} ) )
3884
        {    # Item already issued to another borrower, mark it returned
3501
            MarkIssueReturned(
3885
            MarkIssueReturned(
3502
                $issue->{borrowernumber},
3886
                $issue->{borrowernumber},
3503
                $itemnumber,
3887
                $itemnumber, undef, $operation->{timestamp},
3504
                undef,
3505
                $operation->{timestamp},
3506
            );
3888
            );
3507
        }
3889
        }
3508
        AddIssue(
3890
        AddIssue( $borrower, $operation->{'barcode'},
3509
            $borrower,
3891
            undef, 1, $operation->{timestamp}, undef, );
3510
            $operation->{'barcode'},
3511
            undef,
3512
            1,
3513
            $operation->{timestamp},
3514
            undef,
3515
        );
3516
        return "Success.";
3892
        return "Success.";
3517
    } else {
3893
    }
3894
    else {
3518
        return "Borrower not found.";
3895
        return "Borrower not found.";
3519
    }
3896
    }
3520
}
3897
}
Lines 3522-3536 sub ProcessOfflineIssue { Link Here
3522
sub ProcessOfflinePayment {
3899
sub ProcessOfflinePayment {
3523
    my $operation = shift;
3900
    my $operation = shift;
3524
3901
3525
    my $borrower = C4::Members::GetMemberDetails( undef, $operation->{cardnumber} ); # Get borrower from operation cardnumber
3902
    my $borrower =
3903
      C4::Members::GetMemberDetails( undef, $operation->{cardnumber} )
3904
      ;    # Get borrower from operation cardnumber
3526
    my $amount = $operation->{amount};
3905
    my $amount = $operation->{amount};
3527
3906
3528
    recordpayment( $borrower->{borrowernumber}, $amount );
3907
    recordpayment( $borrower->{borrowernumber}, $amount );
3529
3908
3530
    return "Success."
3909
    return "Success.";
3531
}
3910
}
3532
3911
3533
3534
=head2 TransferSlip
3912
=head2 TransferSlip
3535
3913
3536
  TransferSlip($user_branch, $itemnumber, $to_branch)
3914
  TransferSlip($user_branch, $itemnumber, $to_branch)
Lines 3540-3560 sub ProcessOfflinePayment { Link Here
3540
=cut
3918
=cut
3541
3919
3542
sub TransferSlip {
3920
sub TransferSlip {
3543
    my ($branch, $itemnumber, $to_branch) = @_;
3921
    my ( $branch, $itemnumber, $to_branch ) = @_;
3544
3922
3545
    my $item =  GetItem( $itemnumber )
3923
    my $item = GetItem($itemnumber)
3546
      or return;
3924
      or return;
3547
3925
3548
    my $pulldate = C4::Dates->new();
3926
    my $pulldate = C4::Dates->new();
3549
3927
3550
    return C4::Letters::GetPreparedLetter (
3928
    return C4::Letters::GetPreparedLetter(
3551
        module => 'circulation',
3929
        module      => 'circulation',
3552
        letter_code => 'TRANSFERSLIP',
3930
        letter_code => 'TRANSFERSLIP',
3553
        branchcode => $branch,
3931
        branchcode  => $branch,
3554
        tables => {
3932
        tables      => {
3555
            'branches'    => $to_branch,
3933
            'branches' => $to_branch,
3556
            'biblio'      => $item->{biblionumber},
3934
            'biblio'   => $item->{biblionumber},
3557
            'items'       => $item,
3935
            'items'    => $item,
3558
        },
3936
        },
3559
    );
3937
    );
3560
}
3938
}
Lines 3568-3579 sub TransferSlip { Link Here
3568
=cut
3946
=cut
3569
3947
3570
sub CheckIfIssuedToPatron {
3948
sub CheckIfIssuedToPatron {
3571
    my ($borrowernumber, $biblionumber) = @_;
3949
    my ( $borrowernumber, $biblionumber ) = @_;
3572
3950
3573
    my $items = GetItemsByBiblioitemnumber($biblionumber);
3951
    my $items = GetItemsByBiblioitemnumber($biblionumber);
3574
3952
3575
    foreach my $item (@{$items}) {
3953
    foreach my $item ( @{$items} ) {
3576
        return 1 if ($item->{borrowernumber} && $item->{borrowernumber} eq $borrowernumber);
3954
        return 1
3955
          if ( $item->{borrowernumber}
3956
            && $item->{borrowernumber} eq $borrowernumber );
3577
    }
3957
    }
3578
3958
3579
    return;
3959
    return;
Lines 3589-3600 sub CheckIfIssuedToPatron { Link Here
3589
3969
3590
sub IsItemIssued {
3970
sub IsItemIssued {
3591
    my $itemnumber = shift;
3971
    my $itemnumber = shift;
3592
    my $dbh = C4::Context->dbh;
3972
    my $dbh        = C4::Context->dbh;
3593
    my $sth = $dbh->prepare(q{
3973
    my $sth        = $dbh->prepare(
3974
        q{
3594
        SELECT COUNT(*)
3975
        SELECT COUNT(*)
3595
        FROM issues
3976
        FROM issues
3596
        WHERE itemnumber = ?
3977
        WHERE itemnumber = ?
3597
    });
3978
    }
3979
    );
3598
    $sth->execute($itemnumber);
3980
    $sth->execute($itemnumber);
3599
    return $sth->fetchrow;
3981
    return $sth->fetchrow;
3600
}
3982
}
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tt (-26 / +59 lines)
Lines 5-10 Link Here
5
<script type="text/javascript">
5
<script type="text/javascript">
6
// <![CDATA[
6
// <![CDATA[
7
 var MSG_NO_COPY_SELECTED = _("Expecting a specific copy selection.");
7
 var MSG_NO_COPY_SELECTED = _("Expecting a specific copy selection.");
8
 var MSG_NO_TRANSFERRABLE_ITEMS = _("No item can be transferred to your pickup location. Please try another pickup location for ");
8
 var ForceHoldNotesReasons=new Array(
9
 var ForceHoldNotesReasons=new Array(
9
    _("This title consists of multiple volumes/parts. Please indicate which part you need. Clicking on specific copy information may be helpful."),
10
    _("This title consists of multiple volumes/parts. Please indicate which part you need. Clicking on specific copy information may be helpful."),
10
    "*** Add a new reason above this line ***" ); // NOTE: Do not renumber reasons; this will affect use of existing ones.
11
    "*** Add a new reason above this line ***" ); // NOTE: Do not renumber reasons; this will affect use of existing ones.
Lines 21-36 Link Here
21
 $(document).ready(function() {
22
 $(document).ready(function() {
22
    $('#hold-request-form').preventDoubleFormSubmit();
23
    $('#hold-request-form').preventDoubleFormSubmit();
23
24
25
    $("#hold-request-form").find("select[name='branch']").change( function() {
26
            var newSelection = $(this).val();
27
28
            //Make sure that when changing the pickup location by resubmitting the form,
29
            // user doesn't accidentally confirm his reservations.
30
            var place_reserveInput = $("#hold-request-form").find('[name="place_reserve"]');
31
            place_reserveInput.attr("value", 0);
32
33
            //Make sure the pickup location is changed to the first select as well.
34
            // The branch selection uses only the first given pickuplocations branch value.
35
            $("#hold-request-form").find("select[name='branch']").val(newSelection);
36
37
            $("#hold-request-form").submit();
38
    });
39
24
    var copiesRowId = null;
40
    var copiesRowId = null;
25
    var wasSpecific = false;
41
    var wasSpecific = false;
26
    var lastCopiesRowId = null;
42
    var lastCopiesRowId = null;
27
	$(".checkitem").parent().click(function(e){
43
    $(".checkitem").parent().click(function(e){
28
		if(e.target.tagName.toLowerCase() == 'td'){
44
        if(e.target.tagName.toLowerCase() == 'td'){
29
           $(this).find("input.checkitem").each( function() {
45
           $(this).find("input.checkitem").each( function() {
30
               $(this).attr('checked', !$(this).attr('checked'));
46
               $(this).attr('checked', !$(this).attr('checked'));
31
			});
47
            });
32
		}
48
        }
33
	});
49
    });
34
    // Hides all 'specific copy' table rows on load.
50
    // Hides all 'specific copy' table rows on load.
35
    $(".copiesrow").hide();
51
    $(".copiesrow").hide();
36
52
Lines 138-143 Link Here
138
            return false;
154
            return false;
139
        }
155
        }
140
156
157
        //Check for each biblio if there are no available Items that can be transferred to the pickup location,
158
        // fail the hold request and alert the user.
159
        var anotherPickupLocationSuggesters = $('#hold-request-form').find('div[id^="suggestAnotherPickupLocation_"]');
160
        if ( $(anotherPickupLocationSuggesters).size() > 0 ) {
161
            anotherPickupLocationSuggesters.each( function() {
162
                    //Extract the biblionumber from the id and fetch the Biblio title
163
                    var idString = $(this).attr('id');
164
                    var bibnumber = idString.replace('suggestAnotherPickupLocation_', '');
165
                    var title = $('#title_'+bibnumber).text();
166
167
                    alert(MSG_NO_TRANSFERRABLE_ITEMS + title);
168
            });
169
            return false;
170
        }
171
141
        // Find the items with the 'Hold' box checked
172
        // Find the items with the 'Hold' box checked
142
        var badBib = null;
173
        var badBib = null;
143
        $(".confirmjs:checked").each(function() {
174
        $(".confirmjs:checked").each(function() {
Lines 189-195 Link Here
189
220
190
221
191
222
192
193
[% FOREACH bibitemloo IN bibitemloop %]
223
[% FOREACH bibitemloo IN bibitemloop %]
194
    [% IF ( bibitemloo.holdable ) %]
224
    [% IF ( bibitemloo.holdable ) %]
195
        // http://jqueryui.com/demos/datepicker/#date-range
225
        // http://jqueryui.com/demos/datepicker/#date-range
Lines 220-226 Link Here
220
  <div id="doc3" class="yui-t7">
250
  <div id="doc3" class="yui-t7">
221
    <div id="bd">
251
    <div id="bd">
222
    [% INCLUDE 'masthead.inc' %]
252
    [% INCLUDE 'masthead.inc' %]
223
	  <div id="yui-g">
253
      <div id="yui-g">
224
        <div id="holds" class="container">
254
        <div id="holds" class="container">
225
        [% IF ( message ) %]
255
        [% IF ( message ) %]
226
            [% IF ( GNA ) %]
256
            [% IF ( GNA ) %]
Lines 273-297 Link Here
273
            [% IF ( none_available ) %]
303
            [% IF ( none_available ) %]
274
                <div id="none_available" class="dialog alert"><strong>Sorry</strong>, none of these items can be placed on hold.
304
                <div id="none_available" class="dialog alert"><strong>Sorry</strong>, none of these items can be placed on hold.
275
                </div>
305
                </div>
276
              [% END %]
306
            [% END %]
277
          [% END %]<!-- NAME="message" -->
307
          [% END %]<!-- NAME="message" -->
278
308
279
      [% UNLESS ( message ) %][% UNLESS ( none_available ) %]<h3>Confirm holds for:
309
        [% UNLESS ( message ) %][% IF ( 1 ) %]
280
                      [% FOREACH USER_INF IN USER_INFO %]
310
            <h3>Confirm holds for:
281
                        [% USER_INF.firstname %] [% USER_INF.surname %] ([% USER_INF.cardnumber %])
311
                [% FOREACH USER_INF IN USER_INFO %]
282
                      [% END %]
312
                    [% USER_INF.firstname %] [% USER_INF.surname %] ([% USER_INF.cardnumber %])
283
                    </h3>[% END %]
313
                [% END %]
284
	      [% IF (RESERVE_CHARGE) %]
314
            </h3>[% END %]
285
	      <div class="dialog alert" id="reserve_fee">
315
            [% IF (RESERVE_CHARGE) %]
286
	        There is a charge of [% RESERVE_CHARGE %] for placing this hold
316
                <div class="dialog alert" id="reserve_fee">
287
	      </div>
317
                    There is a charge of [% RESERVE_CHARGE %] for placing this hold
288
	      [% END %]
318
                </div>
319
            [% END %]
289
320
290
            <form action="/cgi-bin/koha/opac-reserve.pl" method="post" id="hold-request-form">
321
            <form action="/cgi-bin/koha/opac-reserve.pl" method="post" id="hold-request-form">
291
            <input type="hidden" name="place_reserve" value="1"/>
322
            <input type="hidden" name="place_reserve" value="1"/>
292
323
293
            <!-- These values are set dynamically by js -->
324
            <!-- These values are set dynamically by js. Biblionumbers is needed for the page reload when changing pickup branch -->
294
            <input type="hidden" name="biblionumbers" id="biblionumbers"/>
325
            <input type="hidden" name="biblionumbers" id="biblionumbers" value="[% biblionumbers %]"/>
295
            <input type="hidden" name="selecteditems" id="selections"/>
326
            <input type="hidden" name="selecteditems" id="selections"/>
296
            <div id="bigloop">
327
            <div id="bigloop">
297
328
Lines 314-320 Link Here
314
                        [% ELSE %]
345
                        [% ELSE %]
315
                        [% END %]
346
                        [% END %]
316
347
317
                            <a class="title" href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% bibitemloo.biblionumber %]">[% bibitemloo.title |html %][% IF ( bibitemloo.subtitle ) %] [% FOREACH subtitl IN bibitemloo.subtitle %][% subtitl.subfield %][% END %][% END %]</a>
348
                            <a class="title" id="title_[% bibitemloo.biblionumber %]" href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% bibitemloo.biblionumber %]">[% bibitemloo.title |html %][% IF ( bibitemloo.subtitle ) %] [% FOREACH subtitl IN bibitemloo.subtitle %][% subtitl.subfield %][% END %][% END %]</a>
318
                                            [% IF ( bibitemloo.author ) %],  by [% bibitemloo.author %][% END %]</p>
349
                                            [% IF ( bibitemloo.author ) %],  by [% bibitemloo.author %][% END %]</p>
319
350
320
351
Lines 359-364 Link Here
359
390
360
391
361
                            [% UNLESS ( singleBranchMode ) %]
392
                            [% UNLESS ( singleBranchMode ) %]
393
                                [% IF ( bibitemloo.suggestAnotherPickupLocation ) %]
394
                                    <div id="suggestAnotherPickupLocation_[% bibitemloo.biblionumber %]" class="dialog alert">None of the available items can be transferred to your pickup location. You could try another pickup location.
395
                                    </div>
396
                                [% END %]
362
                                [% IF ( bibitemloo.holdable ) %]
397
                                [% IF ( bibitemloo.holdable ) %]
363
                                    [% IF ( choose_branch ) %]
398
                                    [% IF ( choose_branch ) %]
364
                                        <li class="branch">
399
                                        <li class="branch">
Lines 398-405 Link Here
398
                                    <span class="date-format from" data-biblionumber="[% bibitemloo.biblionumber %]">[% INCLUDE 'date-format.inc' %]</span>
433
                                    <span class="date-format from" data-biblionumber="[% bibitemloo.biblionumber %]">[% INCLUDE 'date-format.inc' %]</span>
399
                                </li>
434
                                </li>
400
                            [% END %]
435
                            [% END %]
436
                        [% END %][%#End of IF ( bibitemloo.holdable ) %]
401
437
402
                        [% END %]
403
                        [% IF ( bibitemloo.holdable ) %]
438
                        [% IF ( bibitemloo.holdable ) %]
404
                            <li>
439
                            <li>
405
                                <label for="to[% bibitemloo.biblionumber %]">Hold not needed after:</label>
440
                                <label for="to[% bibitemloo.biblionumber %]">Hold not needed after:</label>
Lines 440-447 Link Here
440
                                           checked="checked"
475
                                           checked="checked"
441
                                    />
476
                                    />
442
                                  [% END %]
477
                                  [% END %]
443
444
                                  <label for="reqany_[% bibitemloo.biblionumber %]">Next available copy</label>
478
                                  <label for="reqany_[% bibitemloo.biblionumber %]">Next available copy</label>
479
445
                                  [% UNLESS ( bibitemloo.holdable ) %]
480
                                  [% UNLESS ( bibitemloo.holdable ) %]
446
                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
481
                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
447
                                           id="reqspecific_[% bibitemloo.biblionumber %]"
482
                                           id="reqspecific_[% bibitemloo.biblionumber %]"
Lines 500-506 Link Here
500
                                  [% UNLESS ( noItemTypeImages ) %]
535
                                  [% UNLESS ( noItemTypeImages ) %]
501
                                  [% IF ( itemLoo.imageurl ) %]<img src="[% itemLoo.imageurl %]" alt="" />[% END %]
536
                                  [% IF ( itemLoo.imageurl ) %]<img src="[% itemLoo.imageurl %]" alt="" />[% END %]
502
                                  [% END %]
537
                                  [% END %]
503
								[% itemLoo.description %]
538
                                [% itemLoo.description %]
504
                                </td>
539
                                </td>
505
                              [% END %]
540
                              [% END %]
506
                              <td class="barcode">[% itemLoo.barcode %]</td>
541
                              <td class="barcode">[% itemLoo.barcode %]</td>
Lines 552-561 Link Here
552
            </div><!-- bigloop -->
587
            </div><!-- bigloop -->
553
588
554
            [% UNLESS ( message ) %]
589
            [% UNLESS ( message ) %]
555
            [% UNLESS ( none_available ) %]
556
            <input type="submit" value="Place hold" class="placehold" />
590
            <input type="submit" value="Place hold" class="placehold" />
557
            [% END %]
591
            [% END %]
558
            [% END %]
559
592
560
            </form>
593
            </form>
561
594
(-)a/opac/opac-reserve.pl (-34 / +62 lines)
Lines 58-65 for ( C4::Context->preference("OPACShowHoldQueueDetails") ) { Link Here
58
}
58
}
59
59
60
sub get_out {
60
sub get_out {
61
	output_html_with_http_headers(shift,shift,shift); # $query, $cookie, $template->output;
61
    output_html_with_http_headers(shift,shift,shift); # $query, $cookie, $template->output;
62
	exit;
62
    exit;
63
}
63
}
64
64
65
# get borrower information ....
65
# get borrower information ....
Lines 91-97 if ((! $biblionumbers) && (! $query->param('place_reserve'))) { Link Here
91
}
91
}
92
92
93
# Pass the numbers to the page so they can be fed back
93
# Pass the numbers to the page so they can be fed back
94
# when the hold is confirmed. TODO: Not necessary?
94
# when the hold is confirmed. Biblionumbers are also needed if the
95
# pickup branch changes and the page needs to be reloaded.
95
$template->param( biblionumbers => $biblionumbers );
96
$template->param( biblionumbers => $biblionumbers );
96
97
97
# Each biblio number is suffixed with '/', e.g. "1/2/3/"
98
# Each biblio number is suffixed with '/', e.g. "1/2/3/"
Lines 103-114 if (($#biblionumbers < 0) && (! $query->param('place_reserve'))) { Link Here
103
}
104
}
104
105
105
# pass the pickup branch along....
106
# pass the pickup branch along....
106
my $branch = $query->param('branch') || $borr->{'branchcode'} || C4::Context->userenv->{branch} || '' ;
107
my $pickupBranch = $query->param('branch') || $borr->{'branchcode'} || C4::Context->userenv->{branch} || '' ;
107
($branches->{$branch}) or $branch = "";     # Confirm branch is real
108
($branches->{$pickupBranch}) or $pickupBranch = "";     # Confirm branch is real
108
$template->param( branch => $branch );
109
$template->param( branch => $pickupBranch );
109
110
110
# make branch selection options...
111
# make branch selection options...
111
my $branchloop = GetBranchesLoop($branch);
112
my $branchloop = GetBranchesLoop($pickupBranch);
112
113
113
# Is the person allowed to choose their branch
114
# Is the person allowed to choose their branch
114
my $OPACChooseBranch = (C4::Context->preference("OPACAllowUserToChooseBranch")) ? 1 : 0;
115
my $OPACChooseBranch = (C4::Context->preference("OPACAllowUserToChooseBranch")) ? 1 : 0;
Lines 202-220 if ( $query->param('place_reserve') ) { Link Here
202
    }
203
    }
203
204
204
    while (@selectedItems) {
205
    while (@selectedItems) {
205
        my $biblioNum = shift(@selectedItems);
206
        my $biblioNum 		= shift(@selectedItems);
206
        my $itemNum   = shift(@selectedItems);
207
        my $itemNum   		= shift(@selectedItems);
207
        my $branch    = shift(@selectedItems);    # i.e., branch code, not name
208
        my $pickupLocation  = shift(@selectedItems);    # i.e., branch code, not name,
208
209
209
        my $canreserve = 0;
210
        my $canreserve = 0;
210
211
211
        my $singleBranchMode = C4::Context->preference("singleBranchMode");
212
        my $singleBranchMode = C4::Context->preference("singleBranchMode");
212
        if ( $singleBranchMode || !$OPACChooseBranch )
213
        if ( $singleBranchMode || !$OPACChooseBranch )
213
        {    # single branch mode or disabled user choosing
214
        {    # single branch mode or disabled user choosing
214
            $branch = $borr->{'branchcode'};
215
            $pickupLocation = $borr->{'branchcode'};
215
        }
216
        }
216
217
217
#item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber
218
        #item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber
218
        if ( $itemNum ne '' ) {
219
        if ( $itemNum ne '' ) {
219
            my $hostbiblioNum = GetBiblionumberFromItemnumber($itemNum);
220
            my $hostbiblioNum = GetBiblionumberFromItemnumber($itemNum);
220
            if ( $hostbiblioNum ne $biblioNum ) {
221
            if ( $hostbiblioNum ne $biblioNum ) {
Lines 235-254 if ( $query->param('place_reserve') ) { Link Here
235
236
236
        my $expiration_date = $query->param("expiration_date_$biblioNum");
237
        my $expiration_date = $query->param("expiration_date_$biblioNum");
237
238
238
      # If a specific item was selected and the pickup branch is the same as the
239
        # If a specific item was selected and the pickup branch is the same as the
239
      # holdingbranch, force the value $rank and $found.
240
        # holdingbranch, force the value $rank and $found.
240
        my $rank = $biblioData->{rank};
241
        my $rank = $biblioData->{rank};
241
        if ( $itemNum ne '' ) {
242
        if ( $itemNum ne '' ) {
243
            my $item = GetItem($itemNum);
242
            $canreserve = 1 if CanItemBeReserved( $borrowernumber, $itemNum );
244
            $canreserve = 1 if CanItemBeReserved( $borrowernumber, $itemNum );
243
            $rank = '0' unless C4::Context->preference('ReservesNeedReturns');
245
            $rank = '0' unless C4::Context->preference('ReservesNeedReturns');
244
            my $item = GetItem($itemNum);
246
            if ( $item->{'holdingbranch'} eq $pickupLocation ) {
245
            if ( $item->{'holdingbranch'} eq $branch ) {
246
                $found = 'W'
247
                $found = 'W'
247
                  unless C4::Context->preference('ReservesNeedReturns');
248
                  unless C4::Context->preference('ReservesNeedReturns');
248
            }
249
            }
250
251
            # UseBranchTransferLimits checking.
252
            my ($transferOk, $message) = CheckBranchTransferAllowed( $pickupLocation, $item->{'holdingbranch'}, $item, undef );
253
            if (! $transferOk) {
254
                $canreserve = 0;
255
            }
249
        }
256
        }
250
        else {
257
        else {
251
            $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum );
258
            $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum, $pickupLocation );
252
259
253
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
260
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
254
            $itemNum = undef;
261
            $itemNum = undef;
Lines 264-270 if ( $query->param('place_reserve') ) { Link Here
264
        # Here we actually do the reserveration. Stage 3.
271
        # Here we actually do the reserveration. Stage 3.
265
        if ($canreserve) {
272
        if ($canreserve) {
266
            AddReserve(
273
            AddReserve(
267
                $branch,      $borrowernumber,
274
                $pickupLocation,      $borrowernumber,
268
                $biblioNum,   'a',
275
                $biblioNum,   'a',
269
                [$biblioNum], $rank,
276
                [$biblioNum], $rank,
270
                $startdate,   $expiration_date,
277
                $startdate,   $expiration_date,
Lines 398-403 foreach my $biblioNum (@biblionumbers) { Link Here
398
        }
405
        }
399
    }
406
    }
400
407
408
    #Collect the amout of items that pass the CheckBranchTransferAllowed-check. This is needed to tell
409
    #  the user if some or all Items cannot be transferred to the pickup location.
410
    my $branchTransferableItemsCount = 0;
411
401
    $biblioLoopIter{itemLoop} = [];
412
    $biblioLoopIter{itemLoop} = [];
402
    my $numCopiesAvailable = 0;
413
    my $numCopiesAvailable = 0;
403
    foreach my $itemInfo (@{$biblioData->{itemInfos}}) {
414
    foreach my $itemInfo (@{$biblioData->{itemInfos}}) {
Lines 434-443 foreach my $biblioNum (@biblionumbers) { Link Here
434
        my ($reservedate,$reservedfor,$expectedAt) = GetReservesFromItemnumber($itemNum);
445
        my ($reservedate,$reservedfor,$expectedAt) = GetReservesFromItemnumber($itemNum);
435
        my $ItemBorrowerReserveInfo = GetMemberDetails( $reservedfor, 0);
446
        my $ItemBorrowerReserveInfo = GetMemberDetails( $reservedfor, 0);
436
447
437
	# the item could be reserved for this borrower vi a host record, flag this
448
        # the item could be reserved for this borrower vi a host record, flag this
438
	if ($reservedfor eq $borrowernumber){
449
        if ($reservedfor eq $borrowernumber){
439
		$itemLoopIter->{already_reserved} = 1;
450
            $itemLoopIter->{already_reserved} = 1;
440
	}
451
        }
441
452
442
        if ( defined $reservedate ) {
453
        if ( defined $reservedate ) {
443
            $itemLoopIter->{backgroundcolor} = 'reserved';
454
            $itemLoopIter->{backgroundcolor} = 'reserved';
Lines 480-491 foreach my $biblioNum (@biblionumbers) { Link Here
480
            $itemLoopIter->{nocancel} = 1;
491
            $itemLoopIter->{nocancel} = 1;
481
        }
492
        }
482
493
483
	# if the items belongs to a host record, show link to host record
494
        # if the items belongs to a host record, show link to host record
484
	if ($itemInfo->{biblionumber} ne $biblioNum){
495
        if ($itemInfo->{biblionumber} ne $biblioNum){
485
		$biblioLoopIter{hostitemsflag} = 1;
496
            $biblioLoopIter{hostitemsflag} = 1;
486
		$itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
497
            $itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
487
		$itemLoopIter->{hosttitle} = GetBiblioData($itemInfo->{biblionumber})->{title};
498
            $itemLoopIter->{hosttitle} = GetBiblioData($itemInfo->{biblionumber})->{title};
488
	}
499
        }
489
500
490
        # If there is no loan, return and transfer, we show a checkbox.
501
        # If there is no loan, return and transfer, we show a checkbox.
491
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
502
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
Lines 501-520 foreach my $biblioNum (@biblionumbers) { Link Here
501
        }
512
        }
502
513
503
        if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed and CanItemBeReserved($borrowernumber,$itemNum) and ($itemLoopIter->{already_reserved} ne 1)) {
514
        if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed and CanItemBeReserved($borrowernumber,$itemNum) and ($itemLoopIter->{already_reserved} ne 1)) {
515
504
            $itemLoopIter->{available} = 1;
516
            $itemLoopIter->{available} = 1;
505
            $numCopiesAvailable++;
517
            $numCopiesAvailable++;
518
519
            #Check for UseBranchTransferLimit. $numCopiesAvailable is incremented because this Item
520
            #  could still be available from another pickup location
521
            my ($transferOk, $errorMsg) = CheckBranchTransferAllowed( $pickupBranch, undef, GetItem($itemNum), undef );
522
            if (! $transferOk) {
523
                $itemLoopIter->{available} = 0;
524
                $itemLoopIter->{branchTransferBlocked} = 1;
525
            }
526
            else {
527
                $branchTransferableItemsCount++;
528
            }
506
        }
529
        }
507
530
508
	# FIXME: move this to a pm
531
        # FIXME: move this to a pm
509
        my $dbh = C4::Context->dbh;
532
        my $dbh = C4::Context->dbh;
510
        my $sth2 = $dbh->prepare("SELECT * FROM reserves WHERE borrowernumber=? AND itemnumber=? AND found='W'");
533
        my $sth2 = $dbh->prepare("SELECT * FROM reserves WHERE borrowernumber=? AND itemnumber=? AND found='W'");
511
        $sth2->execute($itemLoopIter->{ReservedForBorrowernumber}, $itemNum);
534
        $sth2->execute($itemLoopIter->{ReservedForBorrowernumber}, $itemNum);
512
        while (my $wait_hashref = $sth2->fetchrow_hashref) {
535
        while (my $wait_hashref = $sth2->fetchrow_hashref) {
513
            $itemLoopIter->{waitingdate} = format_date($wait_hashref->{waitingdate});
536
            $itemLoopIter->{waitingdate} = format_date($wait_hashref->{waitingdate});
514
        }
537
        }
515
	$itemLoopIter->{imageurl} = getitemtypeimagelocation( 'opac', $itemTypes->{ $itemInfo->{itype} }{imageurl} );
538
        $itemLoopIter->{imageurl} = getitemtypeimagelocation( 'opac', $itemTypes->{ $itemInfo->{itype} }{imageurl} );
516
539
517
    # Show serial enumeration when needed
540
        # Show serial enumeration when needed
518
        if ($itemLoopIter->{enumchron}) {
541
        if ($itemLoopIter->{enumchron}) {
519
            $itemdata_enumchron = 1;
542
            $itemdata_enumchron = 1;
520
        }
543
        }
Lines 523-528 foreach my $biblioNum (@biblionumbers) { Link Here
523
    }
546
    }
524
    $template->param( itemdata_enumchron => $itemdata_enumchron );
547
    $template->param( itemdata_enumchron => $itemdata_enumchron );
525
548
549
    ## Set the behaviour flags for the template
526
    if ($numCopiesAvailable > 0) {
550
    if ($numCopiesAvailable > 0) {
527
        $numBibsAvailable++;
551
        $numBibsAvailable++;
528
        $biblioLoopIter{bib_available} = 1;
552
        $biblioLoopIter{bib_available} = 1;
Lines 538-550 foreach my $biblioNum (@biblionumbers) { Link Here
538
        $biblioLoopIter{holdable} = undef;
562
        $biblioLoopIter{holdable} = undef;
539
        $biblioLoopIter{already_patron_possession} = 1;
563
        $biblioLoopIter{already_patron_possession} = 1;
540
    }
564
    }
565
    if ($branchTransferableItemsCount == 0) {
566
        #We can tell our Borrowers that they can try another pickup location if they don't find what they need.
567
        $biblioLoopIter{suggestAnotherPickupLocation} = 1 ;
568
    }
569
541
570
542
    if( $biblioLoopIter{holdable} ){ $anyholdable++; }
571
    if( $biblioLoopIter{holdable} ){ $anyholdable++; }
543
572
544
    push @$biblioLoop, \%biblioLoopIter;
573
    push @$biblioLoop, \%biblioLoopIter;
545
}
574
}
546
575
547
if ( $numBibsAvailable == 0 || $anyholdable == 0) {
576
if ( $numBibsAvailable == 0 || $anyholdable == 0 ) {
548
    $template->param( none_available => 1 );
577
    $template->param( none_available => 1 );
549
}
578
}
550
579
Lines 571-577 if ( Link Here
571
    C4::Context->preference( 'OPACAllowHoldDateInFuture' )
600
    C4::Context->preference( 'OPACAllowHoldDateInFuture' )
572
    ) {
601
    ) {
573
    $template->param(
602
    $template->param(
574
	    reserve_in_future         => 1,
603
        reserve_in_future         => 1,
575
    );
604
    );
576
}
605
}
577
606
578
- 

Return to bug 10993