Bug 35285: Add non-html template support to html_content wrapping
[koha.git] / Koha / Booking.pm
1 package Koha::Booking;
2
3 # Copyright PTFS Europe 2021
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Koha::Exceptions::Booking;
23 use Koha::DateUtils qw( dt_from_string );
24
25 use base qw(Koha::Object);
26
27 =head1 NAME
28
29 Koha::Booking - Koha Booking object class
30
31 =head1 API
32
33 =head2 Class methods
34
35 =head3 biblio
36
37 Returns the related Koha::Biblio object for this booking
38
39 =cut
40
41 sub biblio {
42     my ($self) = @_;
43
44     my $biblio_rs = $self->_result->biblio;
45     return Koha::Biblio->_new_from_dbic($biblio_rs);
46 }
47
48 =head3 patron
49
50 Returns the related Koha::Patron object for this booking
51
52 =cut
53
54 sub patron {
55     my ($self) = @_;
56
57     my $patron_rs = $self->_result->patron;
58     return Koha::Patron->_new_from_dbic($patron_rs);
59 }
60
61 =head3 pickup_library
62
63 Returns the related Koha::Library object for this booking
64
65 =cut
66
67 sub pickup_library {
68     my ($self) = @_;
69
70     my $pickup_library_rs = $self->_result->pickup_library;
71     return Koha::Library->_new_from_dbic($pickup_library_rs);
72 }
73
74 =head3 item
75
76 Returns the related Koha::Item object for this Booking
77
78 =cut
79
80 sub item {
81     my ($self) = @_;
82
83     my $item_rs = $self->_result->item;
84     return unless $item_rs;
85     return Koha::Item->_new_from_dbic($item_rs);
86 }
87
88 =head3 store
89
90 Booking specific store method to catch booking clashes and ensure we have an item assigned
91
92 We assume that if an item is passed, it's bookability has already been checked. This is to allow
93 overrides in the future.
94
95 =cut
96
97 sub store {
98     my ($self) = @_;
99
100     $self->_result->result_source->schema->txn_do(
101         sub {
102             if ( $self->item_id ) {
103                 Koha::Exceptions::Object::FKConstraint->throw(
104                     broken_fk => 'item_id',
105                     value     => $self->item_id,
106                 ) unless ( $self->item );
107
108                 $self->biblio_id( $self->item->biblionumber )
109                     unless $self->biblio_id;
110
111                 Koha::Exceptions::Object::FKConstraint->throw()
112                     unless ( $self->biblio_id == $self->item->biblionumber );
113             }
114
115             Koha::Exceptions::Object::FKConstraint->throw(
116                 broken_fk => 'biblio_id',
117                 value     => $self->biblio_id,
118             ) unless ( $self->biblio );
119
120             # Throw exception for item level booking clash
121             Koha::Exceptions::Booking::Clash->throw()
122                 if $self->item_id && !$self->item->check_booking(
123                 {
124                     start_date => $self->start_date,
125                     end_date   => $self->end_date,
126                     booking_id => $self->in_storage ? $self->booking_id : undef
127                 }
128                 );
129
130             # Throw exception for biblio level booking clash
131             Koha::Exceptions::Booking::Clash->throw()
132                 if !$self->biblio->check_booking(
133                 {
134                     start_date => $self->start_date,
135                     end_date   => $self->end_date,
136                     booking_id => $self->in_storage ? $self->booking_id : undef
137                 }
138                 );
139
140             # FIXME: We should be able to combine the above two functions into one
141
142             # Assign item at booking time
143             if ( !$self->item_id ) {
144                 $self->_assign_item_for_booking;
145             }
146
147             $self = $self->SUPER::store;
148         }
149     );
150
151     return $self;
152 }
153
154 =head3 _assign_item_for_booking
155
156   $self->_assign_item_for_booking;
157
158 Used internally in Koha::Booking->store to ensure we have an item assigned for the booking.
159
160 =cut
161
162 sub _assign_item_for_booking {
163     my ($self) = @_;
164
165     my $biblio = $self->biblio;
166
167     my $start_date = dt_from_string( $self->start_date );
168     my $end_date   = dt_from_string( $self->end_date );
169
170     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
171
172     my $existing_bookings = $biblio->bookings(
173         [
174             start_date => {
175                 '-between' => [
176                     $dtf->format_datetime($start_date),
177                     $dtf->format_datetime($end_date)
178                 ]
179             },
180             end_date => {
181                 '-between' => [
182                     $dtf->format_datetime($start_date),
183                     $dtf->format_datetime($end_date)
184                 ]
185             },
186             {
187                 start_date => { '<' => $dtf->format_datetime($start_date) },
188                 end_date   => { '>' => $dtf->format_datetime($end_date) }
189             }
190         ]
191     );
192
193     my $checkouts =
194         $biblio->current_checkouts->search( { date_due => { '>=' => $dtf->format_datetime($start_date) } } );
195
196     my $bookable_items = $biblio->bookable_items->search(
197         {
198             itemnumber => [
199                 '-and' => { '-not_in' => $existing_bookings->_resultset->get_column('item_id')->as_query },
200                 { '-not_in' => $checkouts->_resultset->get_column('itemnumber')->as_query }
201             ]
202         },
203         { rows => 1 }
204     );
205
206     my $itemnumber = $bookable_items->single->itemnumber;
207     return $self->item_id($itemnumber);
208 }
209
210 =head3 get_items_that_can_fill
211
212     my $items = $bookings->get_items_that_can_fill();
213
214 Return the list of items that can fulfill this booking.
215
216 Items that are not:
217
218   in transit
219   lost
220   withdrawn
221   not for loan
222   not already booked
223
224 =cut
225
226 sub get_items_that_can_fill {
227     my ($self) = @_;
228     return;
229 }
230
231 =head3 to_api_mapping
232
233 This method returns the mapping for representing a Koha::Booking object
234 on the API.
235
236 =cut
237
238 sub to_api_mapping {
239     return {};
240 }
241
242 =head2 Internal methods
243
244 =head3 _type
245
246 =cut
247
248 sub _type {
249     return 'Booking';
250 }
251
252 =head1 AUTHORS
253
254 Martin Renvoize <martin.renvoize@ptfs-europe.com>
255
256 =cut
257
258 1;