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

(-)a/Koha/Booking.pm (+96 lines)
Lines 21-28 use Modern::Perl; Link Here
21
21
22
use Koha::Exceptions::Booking;
22
use Koha::Exceptions::Booking;
23
use Koha::DateUtils qw( dt_from_string );
23
use Koha::DateUtils qw( dt_from_string );
24
use Koha::Bookings;
25
use Koha::CirculationRules;
26
use Koha::Cache::Memory::Lite;
27
28
use C4::Circulation;
29
use C4::Biblio;
24
30
25
use base qw(Koha::Object);
31
use base qw(Koha::Object);
32
use List::Util qw(min);
26
33
27
=head1 NAME
34
=head1 NAME
28
35
Lines 32-37 Koha::Booking - Koha Booking object class Link Here
32
39
33
=head2 Class methods
40
=head2 Class methods
34
41
42
=head2 can_be_booked_in_advance
43
44
  $canBeBooked = &can_be_booked_in_advance($patron, $item, $branchcode)
45
  if ($canBeBooked->{status} eq 'OK') { #We can booked this Item in advance! }
46
47
@RETURNS { status => OK },              if the Item can be booked.
48
         { status => tooManyBookings, limit => $limit }, if the borrower has exceeded their maximum booking amount.
49
         { status => tooLongBookingPeriod, limit => $limit }, if the borrower has exceeded their maximum booking period.
50
         { status => }
51
52
=cut
53
54
sub can_be_booked_in_advance {
55
    my ( $self, $params ) = @_;
56
    my $patron = $self->patron;
57
    my $item = $self->item;
58
59
    my $dbh = C4::Context->dbh;
60
61
    my $borrower = $patron->unblessed;
62
63
    if ( C4::Biblio->GetMarcFromKohaField('biblioitems.agerestriction') ) {
64
        my $biblio = $item->biblio;
65
66
        # Check for the age restriction
67
        my ( $ageRestriction, $daysToAgeRestriction ) =
68
            C4::Circulation::GetAgeRestriction( $biblio->biblioitem->agerestriction, $borrower );
69
        return { status => 'ageRestricted' } if $daysToAgeRestriction && $daysToAgeRestriction > 0;
70
    }
71
72
    # By default for now, control branch is the item homebranch
73
    my $bookings_control_branch = $item->homebranch;
74
75
    # we retrieve rights
76
    my $rights = Koha::CirculationRules->get_effective_rules(
77
        {
78
            categorycode => $borrower->{'categorycode'},
79
            itemtype     => $item->effective_itemtype,
80
            branchcode   => $bookings_control_branch,
81
            rules        =>
82
                [ 'bookings_allowed_total', 'bookings_per_item', 'bookings_per_itemtype', 'bookings_period_length' ]
83
        }
84
    );
85
86
    my $bookings_allowed_total = $rights->{bookings_allowed_total} // 0;
87
    my $bookings_per_item      = $rights->{bookings_per_item}      // 1;
88
    my $bookings_per_itemtype  = $rights->{bookings_per_itemtype}  // 1;
89
    my $bookings_period_length = $rights->{bookings_period_length};
90
    my $booking_limit;
91
92
    my $bookable_status = $item->bookable;
93
94
    if($bookable_status == 1) {
95
        $booking_limit = $bookings_per_item;
96
    } elsif (!defined($bookable_status)) {
97
        $booking_limit = $bookings_per_itemtype;
98
    } else {
99
        $booking_limit = 0;
100
    }
101
102
    if ( defined $bookings_allowed_total && $bookings_allowed_total ne '' ) {
103
        if ( $bookings_allowed_total == 0 ) {
104
            return { status => 'noBookingsAllowed' };
105
        } else {
106
            $booking_limit = min( $booking_limit, $bookings_allowed_total );
107
        }
108
    }
109
110
    if ( $booking_limit == 0 ) {
111
        return { status => "noBookingsAllowedOnThisItem" };
112
    }
113
114
    my $total_bookings_count = Koha::Bookings->search( { patron_id => $patron->borrowernumber } )->count();
115
    return { status => 'tooManyBookings', limit => $booking_limit } if $booking_limit <= $total_bookings_count;
116
117
    my $start_date = dt_from_string( $self->start_date );
118
    my $end_date   = dt_from_string( $self->end_date );
119
    my $duration = $end_date->delta_days($start_date);
120
121
    my $delta_days = $duration->in_units('days');
122
123
    return { status => 'tooLongBookingPeriod', limit => $bookings_period_length } if $delta_days > $bookings_period_length;
124
125
    return { status => 'OK' };
126
}
127
35
=head3 biblio
128
=head3 biblio
36
129
37
Returns the related Koha::Biblio object for this booking
130
Returns the related Koha::Biblio object for this booking
Lines 126-131 sub store { Link Here
126
219
127
            # FIXME: We should be able to combine the above two functions into one
220
            # FIXME: We should be able to combine the above two functions into one
128
221
222
            my $canBeBooked = can_be_booked_in_advance( $self );
223
            Koha::Exceptions::Booking::Rule->throw( $canBeBooked ) if $canBeBooked->{'status'} ne "OK";
224
129
            # Assign item at booking time
225
            # Assign item at booking time
130
            if ( !$self->item_id ) {
226
            if ( !$self->item_id ) {
131
                $self->_assign_item_for_booking;
227
                $self->_assign_item_for_booking;
(-)a/Koha/CirculationRules.pm (+12 lines)
Lines 216-221 our $RULE_KINDS = { Link Here
216
    holds_pickup_period => {
216
    holds_pickup_period => {
217
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
217
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
218
    },
218
    },
219
    bookings_allowed_total => {
220
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
221
    },
222
    bookings_per_item => {
223
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
224
    },
225
    bookings_per_itemtype => {
226
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
227
    },
228
    bookings_period_length => {
229
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
230
    },
219
    # Not included (deprecated?):
231
    # Not included (deprecated?):
220
    #   * accountsent
232
    #   * accountsent
221
    #   * reservecharge
233
    #   * reservecharge
(-)a/Koha/Exceptions/Booking.pm (+4 lines)
Lines 8-13 use Exception::Class ( Link Here
8
        isa         => 'Koha::Exceptions::Booking',
8
        isa         => 'Koha::Exceptions::Booking',
9
        description => "Adding or updating the booking would result in a clash"
9
        description => "Adding or updating the booking would result in a clash"
10
    },
10
    },
11
    'Koha::Exceptions::Booking::Rule' => {
12
        isa         => 'Koha::Exceptions::Booking',
13
        description => "Booking rejected by circulation rules"
14
    }
11
);
15
);
12
16
13
1;
17
1;
(-)a/Koha/REST/V1/Bookings.pm (+17 lines)
Lines 98-103 sub add { Link Here
98
                    error => "Duplicate booking_id",
98
                    error => "Duplicate booking_id",
99
                }
99
                }
100
            );
100
            );
101
        } elsif ( blessed $_ and $_->isa('Koha::Exceptions::Booking::Rule') ) {
102
            my $error_code = $_->{'message'}->{'status'};
103
            my $limit = $_->{'message'}->{'limit'} // '';
104
            my %error_strings = (
105
                'noBookingsAllowed' => 'Bookings are not allowed according to circulation rules',
106
                'noBookingsAllowedOnThisItem' => 'Bookings are not allowed on this item according to circulation rules',
107
                'tooManyBookings' => sprintf('Patron has reached the maximum of booking according to circulation rules : %s maximum', $limit),
108
                'tooLongBookingPeriod' => sprintf('Booking period exceed booking period limit according to circulation rules : %s day(s)', $limit),
109
                'ageRestricted' =>  "Age restricted",
110
            );
111
112
            return $c->render(
113
                status  => 403,
114
                openapi => {
115
                    error => $error_strings{$error_code},
116
                }
117
            );
101
        }
118
        }
102
119
103
        return $c->unhandled_exception($_);
120
        return $c->unhandled_exception($_);
(-)a/admin/smart-rules.pl (+12 lines)
Lines 117-122 if ($op eq 'cud-delete') { Link Here
117
                recall_shelf_time                => undef,
117
                recall_shelf_time                => undef,
118
                decreaseloanholds                => undef,
118
                decreaseloanholds                => undef,
119
                holds_pickup_period              => undef,
119
                holds_pickup_period              => undef,
120
                bookings_allowed_total           => undef,
121
                bookings_per_item                => undef,
122
                bookings_per_itemtype            => undef,
123
                bookings_period_length           => undef,
120
            }
124
            }
121
        }
125
        }
122
    );
126
    );
Lines 302-307 elsif ( $op eq 'cud-add' ) { Link Here
302
    my $recall_overdue_fine           = $input->param('recall_overdue_fine');
306
    my $recall_overdue_fine           = $input->param('recall_overdue_fine');
303
    my $recall_shelf_time             = $input->param('recall_shelf_time');
307
    my $recall_shelf_time             = $input->param('recall_shelf_time');
304
    my $holds_pickup_period           = strip_non_numeric( scalar $input->param('holds_pickup_period') );
308
    my $holds_pickup_period           = strip_non_numeric( scalar $input->param('holds_pickup_period') );
309
    my $bookings_allowed_total        = strip_non_numeric( scalar $input->param('bookings_allowed_total') );
310
    my $bookings_per_item             = strip_non_numeric( scalar $input->param('bookings_per_item') );
311
    my $bookings_per_itemtype         = strip_non_numeric( scalar $input->param('bookings_per_itemtype') );
312
    my $bookings_period_length        = $input->param('bookings_period_length') || 0;
305
313
306
    my $rules = {
314
    my $rules = {
307
        maxissueqty                      => $maxissueqty,
315
        maxissueqty                      => $maxissueqty,
Lines 344-349 elsif ( $op eq 'cud-add' ) { Link Here
344
        recall_overdue_fine              => $recall_overdue_fine,
352
        recall_overdue_fine              => $recall_overdue_fine,
345
        recall_shelf_time                => $recall_shelf_time,
353
        recall_shelf_time                => $recall_shelf_time,
346
        holds_pickup_period              => $holds_pickup_period,
354
        holds_pickup_period              => $holds_pickup_period,
355
        bookings_allowed_total           => $bookings_allowed_total,
356
        bookings_per_item                => $bookings_per_item,
357
        bookings_per_itemtype            => $bookings_per_itemtype,
358
        bookings_period_length           => $bookings_period_length,
347
    };
359
    };
348
360
349
    Koha::CirculationRules->set_rules(
361
    Koha::CirculationRules->set_rules(
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (-1 / +39 lines)
Lines 151-156 Link Here
151
                            <th>On shelf holds allowed</th>
151
                            <th>On shelf holds allowed</th>
152
                            <th>OPAC item level holds</th>
152
                            <th>OPAC item level holds</th>
153
                            <th>Holds pickup period (day)</th>
153
                            <th>Holds pickup period (day)</th>
154
                            <th>Bookings allowed (total)</th>
155
                            <th>Bookings per item (total)</th>
156
                            <th>Bookings per itemtype (total)</th>
157
                            <th>Bookings period length (day)</th>
154
                            [% IF Koha.Preference('ArticleRequests') %]
158
                            [% IF Koha.Preference('ArticleRequests') %]
155
                            <th>Article requests</th>
159
                            <th>Article requests</th>
156
                            [% END %]
160
                            [% END %]
Lines 212-219 Link Here
212
                                    [% SET recall_overdue_fine = all_rules.$c.$i.recall_overdue_fine %]
216
                                    [% SET recall_overdue_fine = all_rules.$c.$i.recall_overdue_fine %]
213
                                    [% SET recall_shelf_time = all_rules.$c.$i.recall_shelf_time %]
217
                                    [% SET recall_shelf_time = all_rules.$c.$i.recall_shelf_time %]
214
                                    [% SET holds_pickup_period = all_rules.$c.$i.holds_pickup_period %]
218
                                    [% SET holds_pickup_period = all_rules.$c.$i.holds_pickup_period %]
219
                                    [% SET bookings_allowed_total = all_rules.$c.$i.bookings_allowed_total %]
220
                                    [% SET bookings_per_item = all_rules.$c.$i.bookings_per_item %]
221
                                    [% SET bookings_per_itemtype = all_rules.$c.$i.bookings_per_itemtype %]
222
                                    [% SET bookings_period_length = all_rules.$c.$i.bookings_period_length %]
215
223
216
                                    [% SET show_rule = note || maxissueqty || maxonsiteissueqty || issuelength || daysmode || lengthunit || hardduedate || hardduedatecompare || fine || chargeperiod || chargeperiod_charge_at || firstremind || overduefinescap || cap_fine_to_replacement_price || finedays || maxsuspensiondays || suspension_chargeperiod || renewalsallowed || unseenrenewalsallowed || renewalperiod || norenewalbefore || noautorenewalbefore || auto_renew || no_auto_renewal_after || no_auto_renewal_after_hard_limit || reservesallowed || holds_per_day || holds_per_record || onshelfholds || opacitemholds || article_requests || rentaldiscount || decreaseloanholds || recalls_allowed || recalls_per_record || on_shelf_recalls || recall_due_date_interval || recall_overdue_fine || recall_shelf_time || holds_pickup_period %]
224
                                    [% SET show_rule = note || maxissueqty || maxonsiteissueqty || issuelength || daysmode || lengthunit || hardduedate || hardduedatecompare || fine || chargeperiod || chargeperiod_charge_at || firstremind || overduefinescap || cap_fine_to_replacement_price || finedays || maxsuspensiondays || suspension_chargeperiod || renewalsallowed || unseenrenewalsallowed || renewalperiod || norenewalbefore || noautorenewalbefore || auto_renew || no_auto_renewal_after || no_auto_renewal_after_hard_limit || reservesallowed || holds_per_day || holds_per_record || onshelfholds || opacitemholds || article_requests || rentaldiscount || decreaseloanholds || recalls_allowed || recalls_per_record || on_shelf_recalls || recall_due_date_interval || recall_overdue_fine || recall_shelf_time || holds_pickup_period || bookings_allowed_total || bookings_per_item || bookings_per_itemtype || bookings_period_length %]
217
                                    [% IF show_rule %]
225
                                    [% IF show_rule %]
218
                                        [% SET row_count = row_count + 1 %]
226
                                        [% SET row_count = row_count + 1 %]
219
                                        <tr row_countd="row_[% row_count | html %]">
227
                                        <tr row_countd="row_[% row_count | html %]">
Lines 380-385 Link Here
380
                                                        [% holds_pickup_period | html %]
388
                                                        [% holds_pickup_period | html %]
381
                                                    [% END %]
389
                                                    [% END %]
382
                                                </td>
390
                                                </td>
391
                                                <td>
392
                                                    [% IF bookings_allowed_total.defined && bookings_allowed_total != '' %]
393
                                                        [% bookings_allowed_total | html %]
394
                                                    [% ELSE %]
395
                                                        <span>Unlimited</span>
396
                                                    [% END %]
397
                                                </td>
398
                                                <td>
399
                                                    [% IF bookings_per_item.defined && bookings_per_item != '' %]
400
                                                        [% bookings_per_item | html %]
401
                                                    [% ELSE %]
402
                                                        <span>Unlimited</span>
403
                                                    [% END %]
404
                                                </td>
405
                                                <td>
406
                                                    [% IF bookings_per_itemtype.defined && bookings_per_itemtype != '' %]
407
                                                        [% bookings_per_itemtype | html %]
408
                                                    [% ELSE %]
409
                                                        <span>Unlimited</span>
410
                                                    [% END %]
411
                                                </td>
412
                                                <td>[% bookings_period_length | html %]</td>
383
                                                [% IF Koha.Preference('ArticleRequests') %]
413
                                                [% IF Koha.Preference('ArticleRequests') %]
384
                                                <td data-code="[% article_requests | html %]">
414
                                                <td data-code="[% article_requests | html %]">
385
                                                    [% IF article_requests == 'no' %]
415
                                                    [% IF article_requests == 'no' %]
Lines 530-535 Link Here
530
                                    </select>
560
                                    </select>
531
                                </td>
561
                                </td>
532
                                <td><input type="text" name="holds_pickup_period" id="holds_pickup_period" size="2" /></td>
562
                                <td><input type="text" name="holds_pickup_period" id="holds_pickup_period" size="2" /></td>
563
                                <td><input type="text" name="bookings_allowed_total" id="bookings_allowed_total" size="2" /></td>
564
                                <td><input type="text" name="bookings_per_item" id="bookings_per_item" size="2" /></td>
565
                                <td><input type="text" name="bookings_per_itemtype" id="bookings_per_itemtype" size="2" /></td>
566
                                <td><input type="text" name="bookings_period_length" id="bookings_period_length" size="3" /></td>
533
                                [% IF Koha.Preference('ArticleRequests') %]
567
                                [% IF Koha.Preference('ArticleRequests') %]
534
                                <td>
568
                                <td>
535
                                    <select id="article_requests" name="article_requests">
569
                                    <select id="article_requests" name="article_requests">
Lines 601-606 Link Here
601
                                  <th>On shelf holds allowed</th>
635
                                  <th>On shelf holds allowed</th>
602
                                  <th>OPAC item level holds</th>
636
                                  <th>OPAC item level holds</th>
603
                                  <th>Holds pickup period (day)</th>
637
                                  <th>Holds pickup period (day)</th>
638
                                  <th>Bookings allowed (total)</th>
639
                                  <th>Bookings per item (total)</th>
640
                                  <th>Bookings per itemtype (total)</th>
641
                                  <th>Bookings period length (day)</th>
604
                                  [% IF Koha.Preference('ArticleRequests') %]
642
                                  [% IF Koha.Preference('ArticleRequests') %]
605
                                  <th>Article requests</th>
643
                                  <th>Article requests</th>
606
                                  [% END %]
644
                                  [% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/js/place_booking_modal.js (-2 / +3 lines)
Lines 494-500 $("#placeBookingForm").on('submit', function(e) { Link Here
494
        });
494
        });
495
495
496
        posting.fail(function(data) {
496
        posting.fail(function(data) {
497
            $('#booking_result').replaceWith('<div id="booking_result" class="alert alert-danger">'+_("Failure")+'</div>');
497
            var error = data.responseJSON.error;
498
            var errorMessage = error ? error : "Failure";
499
            $('#booking_result').replaceWith('<div id="booking_result" class="alert alert-danger">'+_(errorMessage)+'</div>');
498
        });
500
        });
499
    } else {
501
    } else {
500
        url += '/' + booking_id;
502
        url += '/' + booking_id;
501
- 

Return to bug 36271