Bug 35248: Refactor - Move assign_item_for_booking
[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 item
62
63 Returns the related Koha::Item object for this Booking
64
65 =cut
66
67 sub item {
68     my ($self) = @_;
69
70     my $item_rs = $self->_result->item;
71     return unless $item_rs;
72     return Koha::Item->_new_from_dbic($item_rs);
73 }
74
75 =head3 store
76
77 Booking specific store method to catch booking clashes and ensure we have an item assigned
78
79 We assume that if an item is passed, it's bookability has already been checked. This is to allow
80 overrides in the future.
81
82 =cut
83
84 sub store {
85     my ($self) = @_;
86
87     $self->_result->result_source->schema->txn_do(
88         sub {
89             if ( $self->item_id ) {
90                 Koha::Exceptions::Object::FKConstraint->throw(
91                     broken_fk => 'item_id',
92                     value     => $self->item_id,
93                 ) unless ( $self->item );
94
95                 $self->biblio_id( $self->item->biblionumber )
96                     unless $self->biblio_id;
97
98                 Koha::Exceptions::Object::FKConstraint->throw()
99                     unless ( $self->biblio_id == $self->item->biblionumber );
100             }
101
102             Koha::Exceptions::Object::FKConstraint->throw(
103                 broken_fk => 'biblio_id',
104                 value     => $self->biblio_id,
105             ) unless ( $self->biblio );
106
107             # Throw exception for item level booking clash
108             Koha::Exceptions::Booking::Clash->throw()
109                 if $self->item_id && !$self->item->check_booking(
110                 {
111                     start_date => $self->start_date,
112                     end_date   => $self->end_date,
113                     booking_id => $self->in_storage ? $self->booking_id : undef
114                 }
115                 );
116
117             # Throw exception for biblio level booking clash
118             Koha::Exceptions::Booking::Clash->throw()
119                 if !$self->biblio->check_booking(
120                 {
121                     start_date => $self->start_date,
122                     end_date   => $self->end_date,
123                     booking_id => $self->in_storage ? $self->booking_id : undef
124                 }
125                 );
126
127             # FIXME: We should be able to combine the above two functions into one
128
129             # Assign item at booking time
130             if ( !$self->item_id ) {
131                 $self->_assign_item_for_booking;
132             }
133
134             $self = $self->SUPER::store;
135         }
136     );
137
138     return $self;
139 }
140
141 =head3 _assign_item_for_booking
142
143   $self->_assign_item_for_booking;
144
145 Used internally in Koha::Booking->store to ensure we have an item assigned for the booking.
146
147 =cut
148
149 sub _assign_item_for_booking {
150     my ($self) = @_;
151
152     my $biblio = $self->biblio;
153
154     my $start_date = dt_from_string( $self->start_date );
155     my $end_date   = dt_from_string( $self->end_date );
156
157     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
158
159     my $existing_bookings = $biblio->bookings(
160         [
161             start_date => {
162                 '-between' => [
163                     $dtf->format_datetime($start_date),
164                     $dtf->format_datetime($end_date)
165                 ]
166             },
167             end_date => {
168                 '-between' => [
169                     $dtf->format_datetime($start_date),
170                     $dtf->format_datetime($end_date)
171                 ]
172             },
173             {
174                 start_date => { '<' => $dtf->format_datetime($start_date) },
175                 end_date   => { '>' => $dtf->format_datetime($end_date) }
176             }
177         ]
178     );
179
180     my $checkouts =
181         $biblio->current_checkouts->search( { date_due => { '>=' => $dtf->format_datetime($start_date) } } );
182
183     my $bookable_items = $biblio->bookable_items->search(
184         {
185             itemnumber => [
186                 '-and' => { '-not_in' => $existing_bookings->_resultset->get_column('item_id')->as_query },
187                 { '-not_in' => $checkouts->_resultset->get_column('itemnumber')->as_query }
188             ]
189         },
190         { rows => 1 }
191     );
192
193     my $itemnumber = $bookable_items->single->itemnumber;
194     return $self->item_id($itemnumber);
195 }
196
197 =head3 get_items_that_can_fill
198
199     my $items = $bookings->get_items_that_can_fill();
200
201 Return the list of items that can fulfill this booking.
202
203 Items that are not:
204
205   in transit
206   lost
207   withdrawn
208   not for loan
209   not already booked
210
211 =cut
212
213 sub get_items_that_can_fill {
214     my ($self) = @_;
215     return;
216 }
217
218 =head3 to_api_mapping
219
220 This method returns the mapping for representing a Koha::Booking object
221 on the API.
222
223 =cut
224
225 sub to_api_mapping {
226     return {};
227 }
228
229 =head2 Internal methods
230
231 =head3 _type
232
233 =cut
234
235 sub _type {
236     return 'Booking';
237 }
238
239 =head1 AUTHORS
240
241 Martin Renvoize <martin.renvoize@ptfs-europe.com>
242
243 =cut
244
245 1;