1 package OpenILS::Application::Booking;
6 use OpenILS::Application;
7 use base qw/OpenILS::Application/;
9 use OpenILS::Utils::CStoreEditor qw/:funcs/;
10 use OpenILS::Utils::Fieldmapper;
11 use OpenILS::Application::AppUtils;
12 my $U = "OpenILS::Application::AppUtils";
14 use OpenSRF::Utils::Logger qw/$logger/;
17 my ($record_id, $owning_lib, $mvr) = @_;
19 my $brt = new Fieldmapper::booking::resource_type;
21 $brt->name($mvr->title);
22 $brt->record($record_id);
23 $brt->catalog_item('t');
24 $brt->owner($owning_lib);
29 sub get_existing_brt {
30 my ($e, $record_id, $owning_lib, $mvr) = @_;
31 my $results = $e->search_booking_resource_type(
32 {name => $mvr->title, owner => $owning_lib, record => $record_id}
35 return $results->[0] if (length @$results > 0);
42 'open-ils.search.biblio.record.mods_slim.retrieve.authoritative',
47 sub get_unique_owning_libs {
49 $hash{$_->call_number->owning_lib} = 1 foreach (@_); # @_ are copies
53 sub fetch_copies_by_ids {
54 my ($e, $copy_ids) = @_;
55 my $results = $e->search_asset_copy([
57 {flesh => 1, flesh_fields => {acp => ['call_number']}}
59 return $results if ref($results) eq 'ARRAY';
63 sub get_single_record_id {
64 my $record_id = undef;
65 foreach (@_) { # @_ are copies
67 (defined $record_id && $record_id != $_->call_number->record);
68 $record_id = $_->call_number->record;
73 __PACKAGE__->register_method(
74 method => "create_brt_and_brsrc",
75 api_name => "open-ils.booking.create_brt_and_brsrc_from_copies",
78 {type => 'string', desc => 'Authentication token'},
79 {type => 'array', desc => 'Copy IDs'},
81 return => { desc => "A two-element hash. The 'brt' element " .
82 "is a list of created booking resource types described by " .
83 "id/copyid pairs. The 'brsrc' element is a similar " .
84 "list of created booking resources described by copy/recordid " .
89 sub create_brt_and_brsrc {
90 my ($self, $conn, $authtoken, $copy_ids) = @_;
91 my (@created_brt, @created_brsrc);
94 my $e = new_editor(xact => 1, authtoken => $authtoken);
95 return $e->die_event unless $e->checkauth;
97 my @copies = @{fetch_copies_by_ids($e, $copy_ids)};
98 my $record_id = get_single_record_id(@copies) or return $e->die_event;
99 my $mvr = get_mvr($record_id) or return $e->die_event;
101 foreach (get_unique_owning_libs(@copies)) {
102 $brt_table{$_} = get_existing_brt($e, $record_id, $_, $mvr) ||
103 prepare_new_brt($record_id, $_, $mvr);
106 while (my ($owning_lib, $brt) = each %brt_table) {
108 if ($e->allowed('CREATE_BOOKING_RESOURCE_TYPE', $owning_lib)) {
109 # We can/should abort if this creation fails, because the
110 # logic isn't going to be trying to create any redundnat
111 # brt's, therefore any error will be more serious than
112 # that. See the different take on creating brsrc's below.
113 return $e->die_event unless (
114 # v-- Important: assignment modifies original hash
115 $brt = $e->create_booking_resource_type($brt)
118 push @created_brt, [$brt->id, $brt->record];
124 $e->allowed('CREATE_BOOKING_RESOURCE', $_->call_number->owning_lib)
126 my $brsrc = new Fieldmapper::booking::resource;
128 $brsrc->type($brt_table{$_->call_number->owning_lib}->id);
129 $brsrc->owner($_->call_number->owning_lib);
130 $brsrc->barcode($_->barcode);
132 # We don't want to abort the transaction or do anything dramatic if
133 # this fails, because quite possibly a user selected many copies on
134 # which to perform this "create booking resource" operation, and
135 # among those copies there may be some that we still need to
136 # create, and some that we don't. So we just do what we can.
137 push @created_brsrc, [$brsrc->id, $_->id] if
138 ($brsrc = $e->create_booking_resource($brsrc));
139 # ^--- Important: assignment.
144 return {brt => \@created_brt, brsrc => \@created_brsrc} or
145 return $e->die_event;
148 sub res_list_by_attrs {
154 return undef unless ($filters->{type} || $filters->{attribute_values});
155 return undef unless ($filters->{type} || $filters->{attribute_values});
158 'select' => { brsrc => [ 'id' ] },
159 'from' => { brsrc => { bram => {} } },
163 if ($filters->{type}) {
164 $query->{where}->{type} = $filters->{type};
167 if ($filters->{attribute_values}) {
169 $filters->{attribute_values} = [$filters->{attribute_values}]
170 if (!ref($filters->{attribute_values}));
172 $query->{having}->{'+bram'}->{value}->{'@>'} = {
173 transform => 'array_accum',
174 value => '{'.join(',', @{ $filters->{attribute_values} } ).'}'
178 if ($filters->{available}) {
179 $query->{from}->{brsrc}->{bresv} = { field => 'current_resource' };
181 if (!ref($filters->{available})) { # just one time, start perhaps
182 $query->{where}->{'+bresv'} = {
186 start_time => { '>=' => $filters->{available} },
187 end_time => { '<=' => $filters->{available} },
191 } else { # start and end times
192 $query->{where}->{'+bresv'} = {
197 start_time => { '>=' => $filters->{available}->[0] },
198 end_time => { '<=' => $filters->{available}->[0] },
201 start_time => { '>=' => $filters->{available}->[1] },
202 end_time => { '<=' => $filters->{available}->[1] },
210 if ($filters->{booked}) {
211 $query->{from}->{brsrc}->{bresv} = { field => 'current_resource' };
213 if (!ref($filters->{booked})) { # just one time, start perhaps
214 $query->{where}->{'+bresv'} = {
215 start_time => { '<=' => $filters->{booked} },
216 end_time => { '>=' => $filters->{booked} },
218 } else { # start and end times
219 $query->{where}->{'+bresv'} = {
222 start_time => { '<=' => $filters->{booked}->[0] },
223 end_time => { '>=' => $filters->{booked}->[0] },
226 start_time => { '<=' => $filters->{booked}->[1] },
227 end_time => { '>=' => $filters->{booked}->[1] },
234 my $cstore = OpenSRF::AppSession->connect('open-ils.cstore');
235 my $ids = $cstore->request( 'open-ils.cstore.json_query.atomic', $query )->gather(1);
236 $ids = [ map { $_->{id} } @$ids ];
239 my $pcrud = OpenSRF::AppSession->connect('open-ils.pcrud');
240 my $allowed_ids = $pcrud->request(
241 'open-ils.pcrud.id_list.brsrc.atomic',
242 $auth => { id => $ids }
248 __PACKAGE__->register_method(
249 method => "res_list_by_attrs",
250 api_name => "open-ils.booking.resources.filtered_id_list",
254 {type => 'string', desc => 'Authentication token'},
255 {type => 'object', desc => 'Filter object -- see notes for details'}
257 return => { desc => "An array of brsrc ids matching the requested filters." },
261 The filter object parameter can contain the following keys:
262 * type => The id of a booking resource type (brt)
263 * attribute_values => The id of booking resource type attribute values that the resource must have assigned to it (brav)
264 * available => Either:
265 A timestamp during which the resources are not reserved. If the resource is overbookable, this is ignored.
266 A range of two timestamps which do not overlap any reservations for the resources. If the resource is overbookable, this is ignored.
268 A timestamp during which the resources are reserved.
269 A range of two timestamps which overlap a reservation of the resources.
271 Note that at least one of 'type' or 'attribute_values' is required.