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

(-)a/Koha/Booking.pm (+75 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, rule => $rule }, if the borrower has exceeded their maximum booking amount.
49
         { status => tooLongBookingPeriod, limit => $limit }, if the borrower has exceeded their maximum booking period.
50
=cut
51
52
sub can_be_booked_in_advance {
53
    my ( $self, $params ) = @_;
54
    my $patron = $self->patron;
55
    my $item = $self->item;
56
57
    my $dbh = C4::Context->dbh;
58
59
    my $borrower = $patron->unblessed;
60
61
    if ( C4::Biblio->GetMarcFromKohaField('biblioitems.agerestriction') ) {
62
        my $biblio = $item->biblio;
63
64
        # Check for the age restriction
65
        my ( $ageRestriction, $daysToAgeRestriction ) =
66
            C4::Circulation::GetAgeRestriction( $biblio->biblioitem->agerestriction, $borrower );
67
        return { status => 'ageRestricted' } if $daysToAgeRestriction && $daysToAgeRestriction > 0;
68
    }
69
70
    # By default for now, control branch is the item homebranch
71
    my $bookings_control_branch = $item->homebranch;
72
73
    # we retrieve rights
74
    my $rights = Koha::CirculationRules->get_effective_rules(
75
        {
76
            categorycode => $borrower->{'categorycode'},
77
            itemtype     => $item->effective_itemtype,
78
            branchcode   => $bookings_control_branch,
79
            rules        =>
80
                [ 'bookings_allowed_total', 'bookings_per_item', 'bookings_period_length' ]
81
        }
82
    );
83
84
    my $bookings_allowed_total = $rights->{bookings_allowed_total} // 0;
85
    my $bookings_per_item      = $rights->{bookings_per_item}      // 1;
86
    my $bookings_period_length = $rights->{bookings_period_length};
87
88
    return { status => 'noBookingsAllowed' } if $bookings_allowed_total == 0;
89
90
    my $bookings_per_item_count = Koha::Bookings->search( { patron_id => $patron->borrowernumber, item_id => $item->itemnumber } )->count();
91
    return { status => 'tooManyBookings', limit => $bookings_per_item, rule => 'bookings_per_item' } if $bookings_per_item <= $bookings_per_item_count;
92
93
    my $total_bookings_count = Koha::Bookings->search( { patron_id => $patron->borrowernumber } )->count();
94
    return { status => 'tooManyBookings', limit => $bookings_allowed_total, rule => 'bookings_allowed_total' } if $bookings_allowed_total <= $total_bookings_count;
95
96
    my $start_date = dt_from_string( $self->start_date );
97
    my $end_date   = dt_from_string( $self->end_date );
98
    my $duration = $end_date->delta_days($start_date);
99
100
    my $delta_days = $duration->in_units('days');
101
102
    return { status => 'tooLongBookingPeriod', limit => $bookings_period_length } if $delta_days > $bookings_period_length;
103
104
    return { status => 'OK' };
105
}
106
35
=head3 biblio
107
=head3 biblio
36
108
37
Returns the related Koha::Biblio object for this booking
109
Returns the related Koha::Biblio object for this booking
Lines 126-131 sub store { Link Here
126
198
127
            # FIXME: We should be able to combine the above two functions into one
199
            # FIXME: We should be able to combine the above two functions into one
128
200
201
            my $canBeBooked = can_be_booked_in_advance( $self );
202
            Koha::Exceptions::Booking::Rule->throw( $canBeBooked ) if $canBeBooked->{'status'} ne "OK";
203
129
            # Assign item at booking time
204
            # Assign item at booking time
130
            if ( !$self->item_id ) {
205
            if ( !$self->item_id ) {
131
                $self->_assign_item_for_booking;
206
                $self->_assign_item_for_booking;
(-)a/Koha/CirculationRules.pm (+9 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_period_length => {
226
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
227
    },
219
    # Not included (deprecated?):
228
    # Not included (deprecated?):
220
    #   * accountsent
229
    #   * accountsent
221
    #   * reservecharge
230
    #   * 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 $rule = $_->{'message'}->{'rule'} // '';
105
            my %error_strings = (
106
                'noBookingsAllowed' => 'Bookings are not allowed according to circulation rules',
107
                'tooManyBookings' => sprintf('Patron has reached the maximum of booking according to circulation rules : %s maximum (%s)', $limit, $rule),
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 (+9 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_period_length           => undef,
120
            }
123
            }
121
        }
124
        }
122
    );
125
    );
Lines 302-307 elsif ( $op eq 'cud-add' ) { Link Here
302
    my $recall_overdue_fine           = $input->param('recall_overdue_fine');
305
    my $recall_overdue_fine           = $input->param('recall_overdue_fine');
303
    my $recall_shelf_time             = $input->param('recall_shelf_time');
306
    my $recall_shelf_time             = $input->param('recall_shelf_time');
304
    my $holds_pickup_period           = strip_non_numeric( scalar $input->param('holds_pickup_period') );
307
    my $holds_pickup_period           = strip_non_numeric( scalar $input->param('holds_pickup_period') );
308
    my $bookings_allowed_total        = strip_non_numeric( scalar $input->param('bookings_allowed_total') );
309
    my $bookings_per_item             = strip_non_numeric( scalar $input->param('bookings_per_item') );
310
    my $bookings_period_length        = $input->param('bookings_period_length') || 0;
305
311
306
    my $rules = {
312
    my $rules = {
307
        maxissueqty                      => $maxissueqty,
313
        maxissueqty                      => $maxissueqty,
Lines 344-349 elsif ( $op eq 'cud-add' ) { Link Here
344
        recall_overdue_fine              => $recall_overdue_fine,
350
        recall_overdue_fine              => $recall_overdue_fine,
345
        recall_shelf_time                => $recall_shelf_time,
351
        recall_shelf_time                => $recall_shelf_time,
346
        holds_pickup_period              => $holds_pickup_period,
352
        holds_pickup_period              => $holds_pickup_period,
353
        bookings_allowed_total           => $bookings_allowed_total,
354
        bookings_per_item                => $bookings_per_item,
355
        bookings_period_length           => $bookings_period_length,
347
    };
356
    };
348
357
349
    Koha::CirculationRules->set_rules(
358
    Koha::CirculationRules->set_rules(
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (-1 / +28 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 period length (day)</th>
154
                            [% IF Koha.Preference('ArticleRequests') %]
157
                            [% IF Koha.Preference('ArticleRequests') %]
155
                            <th>Article requests</th>
158
                            <th>Article requests</th>
156
                            [% END %]
159
                            [% END %]
Lines 212-219 Link Here
212
                                    [% SET recall_overdue_fine = all_rules.$c.$i.recall_overdue_fine %]
215
                                    [% SET recall_overdue_fine = all_rules.$c.$i.recall_overdue_fine %]
213
                                    [% SET recall_shelf_time = all_rules.$c.$i.recall_shelf_time %]
216
                                    [% SET recall_shelf_time = all_rules.$c.$i.recall_shelf_time %]
214
                                    [% SET holds_pickup_period = all_rules.$c.$i.holds_pickup_period %]
217
                                    [% SET holds_pickup_period = all_rules.$c.$i.holds_pickup_period %]
218
                                    [% SET bookings_allowed_total = all_rules.$c.$i.bookings_allowed_total %]
219
                                    [% SET bookings_per_item = all_rules.$c.$i.bookings_per_item %]
220
                                    [% SET bookings_period_length = all_rules.$c.$i.bookings_period_length %]
215
221
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 %]
222
                                    [% 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_period_length %]
217
                                    [% IF show_rule %]
223
                                    [% IF show_rule %]
218
                                        [% SET row_count = row_count + 1 %]
224
                                        [% SET row_count = row_count + 1 %]
219
                                        <tr row_countd="row_[% row_count | html %]">
225
                                        <tr row_countd="row_[% row_count | html %]">
Lines 380-385 Link Here
380
                                                        [% holds_pickup_period | html %]
386
                                                        [% holds_pickup_period | html %]
381
                                                    [% END %]
387
                                                    [% END %]
382
                                                </td>
388
                                                </td>
389
                                                <td>
390
                                                    [% IF bookings_allowed_total.defined && bookings_allowed_total != '' %]
391
                                                        [% bookings_allowed_total | html %]
392
                                                    [% ELSE %]
393
                                                        <span>Unlimited</span>
394
                                                    [% END %]
395
                                                </td>
396
                                                <td>
397
                                                    [% IF bookings_per_item.defined && bookings_per_item != '' %]
398
                                                        [% bookings_per_item | html %]
399
                                                    [% ELSE %]
400
                                                        <span>Unlimited</span>
401
                                                    [% END %]
402
                                                </td>
403
                                                <td>[% bookings_period_length | html %]</td>
383
                                                [% IF Koha.Preference('ArticleRequests') %]
404
                                                [% IF Koha.Preference('ArticleRequests') %]
384
                                                <td data-code="[% article_requests | html %]">
405
                                                <td data-code="[% article_requests | html %]">
385
                                                    [% IF article_requests == 'no' %]
406
                                                    [% IF article_requests == 'no' %]
Lines 530-535 Link Here
530
                                    </select>
551
                                    </select>
531
                                </td>
552
                                </td>
532
                                <td><input type="text" name="holds_pickup_period" id="holds_pickup_period" size="2" /></td>
553
                                <td><input type="text" name="holds_pickup_period" id="holds_pickup_period" size="2" /></td>
554
                                <td><input type="text" name="bookings_allowed_total" id="bookings_allowed_total" size="2" /></td>
555
                                <td><input type="text" name="bookings_per_item" id="bookings_per_item" size="2" /></td>
556
                                <td><input type="text" name="bookings_period_length" id="bookings_period_length" size="3" /></td>
533
                                [% IF Koha.Preference('ArticleRequests') %]
557
                                [% IF Koha.Preference('ArticleRequests') %]
534
                                <td>
558
                                <td>
535
                                    <select id="article_requests" name="article_requests">
559
                                    <select id="article_requests" name="article_requests">
Lines 601-606 Link Here
601
                                  <th>On shelf holds allowed</th>
625
                                  <th>On shelf holds allowed</th>
602
                                  <th>OPAC item level holds</th>
626
                                  <th>OPAC item level holds</th>
603
                                  <th>Holds pickup period (day)</th>
627
                                  <th>Holds pickup period (day)</th>
628
                                  <th>Bookings allowed (total)</th>
629
                                  <th>Bookings per item (total)</th>
630
                                  <th>Bookings period length (day)</th>
604
                                  [% IF Koha.Preference('ArticleRequests') %]
631
                                  [% IF Koha.Preference('ArticleRequests') %]
605
                                  <th>Article requests</th>
632
                                  <th>Article requests</th>
606
                                  [% END %]
633
                                  [% 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