add first version of the booking reservation targeter
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 10 Dec 2009 17:10:11 +0000 (17:10 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 10 Dec 2009 17:10:11 +0000 (17:10 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@15133 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/booking.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/dbi.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm

index c2b0f56..419179c 100644 (file)
@@ -557,6 +557,12 @@ sub modify_from_fieldmapper {
        action::circulation->has_a( usr => 'actor::user' );
        actor::user->has_many( circulations => 'action::circulation' => 'usr' );
 
+       booking::resource_attr_map->has_a( resource => 'booking::resource' );
+
+       booking::resource->has_a( owner => 'actor::org_unit' );
+       booking::resource->has_a( type => 'booking::resource_type' );
+       booking::resource_type->has_a( owner => 'actor::org_unit' );
+
        booking::reservation->has_a( usr => 'actor::user' );
        actor::user->has_many( reservations => 'booking::reservation' => 'usr' );
        
index 7a7bb36..811ad37 100644 (file)
@@ -6,6 +6,24 @@ package booking;
 use base qw/OpenILS::Application::Storage::CDBI/;
 #-------------------------------------------------------------------------------
 
+package booking::resource_type;
+use base qw/booking/;
+__PACKAGE__->table('booking_resource_type');
+__PACKAGE__->columns(Primary => 'id');
+__PACKAGE__->columns(Essential => qw/name fine_interval fine_amount
+                     max_fine owner catalog_item record transferable elbow_room/);
+
+#-------------------------------------------------------------------------------
+
+package booking::resource;
+use base qw/booking/;
+__PACKAGE__->table('booking_resource');
+__PACKAGE__->columns(Primary => 'id');
+__PACKAGE__->columns(Essential => qw/owner type overbook barcode deposit
+                     deposit_amount user_fee/);
+
+#-------------------------------------------------------------------------------
+
 package booking::reservation;
 use base qw/booking/;
 __PACKAGE__->table('booking_reservation');
@@ -19,5 +37,21 @@ __PACKAGE__->columns(Essential => qw/xact_start usr current_copy circ_lib
 
 #-------------------------------------------------------------------------------
 
+package booking::resource_attr_map;
+use base qw/booking/;
+__PACKAGE__->table('booking_resource_attr_map');
+__PACKAGE__->columns(Primary => 'id');
+__PACKAGE__->columns(Essential => qw/resource resource_attr value/);
+
+#-------------------------------------------------------------------------------
+
+package booking::reservation_attr_value_map;
+use base qw/booking/;
+__PACKAGE__->table('booking_reservation_attr_value_map');
+__PACKAGE__->columns(Primary => 'id');
+__PACKAGE__->columns(Essential => qw/reservation attr_value/);
+
+#-------------------------------------------------------------------------------
+
 1;
 
index 9939f5a..a3fd66e 100644 (file)
        action::circulation->sequence( 'money.billable_xact_id_seq' );
 
        #---------------------------------------------------------------------
+       package booking::resource_type;
+       
+       booking::resource_type->table( 'booking.resource_type' );
+       booking::resource_type->sequence( 'booking.resource_type_id_seq' );
+
+       #---------------------------------------------------------------------
+       package booking::resource;
+       
+       booking::resource->table( 'booking.resource' );
+       booking::resource->sequence( 'booking.resource_id_seq' );
+
+       #---------------------------------------------------------------------
        package booking::reservation;
        
        booking::reservation->table( 'booking.reservation' );
        booking::reservation->sequence( 'money.billable_xact_id_seq' );
 
        #---------------------------------------------------------------------
+       package booking::reservation_attr_value_map;
+       
+       booking::reservation_attr_value_map->table( 'booking.reservation_attr_value_map' );
+       booking::reservation_attr_value_map->sequence( 'booking.reservation_attr_value_map_id_seq' );
+
+       #---------------------------------------------------------------------
+       package booking::resource_attr_map;
+       
+       booking::resource_attr_map->table( 'booking.resource_attr_map' );
+       booking::resource_attr_map->sequence( 'booking.resource_attr_map_id_seq' );
+
+       #---------------------------------------------------------------------
        package action::non_cat_in_house_use;
        
        action::non_cat_in_house_use->table( 'action.non_cat_in_house_use' );
index 6785a75..375d598 100644 (file)
@@ -1320,6 +1320,205 @@ __PACKAGE__->register_method(
        method          => 'new_hold_copy_targeter',
 );
 
+sub reservation_targeter {
+       my $self = shift;
+       my $client = shift;
+       my $one_reservation = shift;
+
+       local $OpenILS::Application::Storage::WRITE = 1;
+
+       my $reservations;
+
+       try {
+               if ($one_reservation) {
+                       $self->method_lookup('open-ils.storage.transaction.begin')->run( $client );
+                       $reservations = booking::reservation->search_where( { id => $one_reservation, capture_time => undef, cancel_time => undef } );
+               } else {
+
+                       # find all the reservations needing targeting
+                       $reservations = [
+                booking::reservation->search_where(
+                                       { current_resource => undef,
+                                         cancel_time => undef,
+                                         start_time => { '>' => 'now' }
+                    },
+                    { order_by => 'start_time' }
+                )
+            ];
+               }
+       } catch Error with {
+               my $e = shift;
+               die "Could not retrieve reservation requests:\n\n$e\n";
+       };
+
+       for my $bresv (@$reservations) {
+               try {
+                       #start a transaction if needed
+                       if ($self->method_lookup('open-ils.storage.transaction.current')->run) {
+                               $log->debug("Cleaning up after previous transaction\n");
+                               $self->method_lookup('open-ils.storage.transaction.rollback')->run;
+                       }
+                       $self->method_lookup('open-ils.storage.transaction.begin')->run( $client );
+                       $log->info("Processing reservation ".$bresv->id."...\n");
+
+                       #first, re-fetch the hold, to make sure it's not captured already
+                       $bresv->remove_from_object_index();
+                       $bresv = booking::reservation->retrieve( $bresv->id );
+
+                       die "OK\n" if (!$bresv or $bresv->capture_time or $bresv->cancel_time);
+
+                       my $end_time = $parser->parse_datetime( clense_ISO8601( $bresv->end_time ) );
+                       if (DateTime->compare($end_time, DateTime->now) < 0) {
+
+                               # cancel cause = un-targeted expiration
+                               $bresv->update( { cancel_time => 'now' } ); 
+                               $self->method_lookup('open-ils.storage.transaction.commit')->run;
+
+                               # tell A/T the reservation was cancelled
+                               my $fm_bresv = $bresv->to_fieldmapper;
+                               my $ses = OpenSRF::AppSession->create('open-ils.trigger');
+                               $ses->request('open-ils.trigger.event.autocreate', 
+                                       'booking.reservation.cancel.expire_no_target', $fm_bresv, $fm_bresv->pickup_lib);
+
+                               die "OK\n";
+                       }
+
+            my $possible_resources;
+
+                       # find all the potential resources
+                       if (!$bresv->target_resource) {
+                my $filter = { type => $bresv->target_resource_type };
+                my $attr_maps = booking::reservations_attr_value_map->search( reservation => $bresv->id );
+
+                $filter->{attribute_values} = [ map { $_->attr_value } @$attr_maps ] if (@$attr_maps);
+
+                               my $ses = OpenSRF::AppSession->create('open-ils.booking');
+                $possible_resources = $ses->request('open-ils.booking.resources.filtered_id_list', $filter)->gather(1);
+
+                       } else {
+                               $possible_resources = $bresv->target_resource;
+                       }
+
+            my $all_resources booking::resource->search( id => $possible_resources );
+                       @$all_resources = grep { isTrue($_->type->transferable) || $_->owner.'' eq $bresv->pickup_lib.'' } @$all_resources;
+
+
+            my @good_resources;
+            for my $res (@$all_resources) {
+                unless (isTrue($res->type->catalog_item)) {
+                    push @good_resources, $res;
+                    next;
+                }
+
+                my $copy = asset::copy->search( deleted => f, barcode => $res->barcode )->[0];
+
+                unless ($copy) {
+                    push @good_resources, $res;
+                    next;
+                }
+
+                if ($copy->status->id == 0 || $copy->status->id == 7) {
+                    push @good_resources, $res;
+                    next;
+                }
+
+                if ($copy->status->id == 1) {
+                    my $circs = action::circulation->search_where(
+                        {target_copy => $copy->id, checkin_time => undef },
+                        { order_by => 'id DESC' }
+                    );
+
+                    if (@$circs) {
+                        my $due_date = $circs->[0]->due_date;
+                                   $due_date = $parser->parse_datetime( clense_ISO8601( $due_date ) );
+                                   my $start_time = $parser->parse_datetime( clense_ISO8601( $bresv->start_time ) );
+                        next if (DateTime->compare($start_time, $due_date) < 0);
+                        push @good_resources, $res;
+                    }
+
+                    next;
+                }
+
+                push @good_resources, $res if (isTrue($copy->status->holdable));
+            }
+
+                       # let 'em know we're still working
+                       $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+                       
+                       # if we have no copies ...
+                       if (!@good_resources) {
+                               $log->info("\tNo resources available for targeting at all!\n");
+                               push @successes, { reservation => $bresv->id, eligible_copies => 0, error => 'NO_COPIES' };
+
+                               $self->method_lookup('open-ils.storage.transaction.commit')->run;
+                               die "OK\n";
+                       }
+
+                       $log->debug("\t".scalar(@good_resources)." resources available for targeting...");
+
+                       my $prox_list = [];
+                       $$prox_list[0] =
+                       [
+                               grep {
+                                       $_->owner == $bresv->pickup_lib
+                               } @good_resources
+                       ];
+
+                       $all_resources = [grep {$_->owner != $bresv->pickup_lib } @good_resources];
+                       # $all_copies is now a list of copies not at the pickup library
+
+                       my $best = shift @good_resources;
+                       $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+
+                       if (!$best) {
+                               $log->debug("\tNothing at the pickup lib, looking elsewhere among ".scalar(@$all_resources)." resources");
+
+                               $prox_list =
+                    map  { $_->[1] }
+                    sort { $a->[0] <> $b->[0] }
+                    map  {
+                        [   actor::org_unit_proximity->search_where(
+                                { from_org => $bresv->pickup_lib.'', to_org => $_=>owner.'' }
+                            )->[0]->prox,
+                            $_
+                        ]
+                    } @$all_resources;
+
+                               $client->status( new OpenSRF::DomainObject::oilsContinueStatus );
+
+                               $best = shift @$prox_list
+                       }
+
+                       if ($best) {
+                               $bresv->update( { current_resource => ''.$best->id } );
+                               $log->debug("\tUpdating reservation [".$bresv->id."] with new 'current_resource' [".$best->id."] for reservation fulfillment.");
+                       }
+
+                       $self->method_lookup('open-ils.storage.transaction.commit')->run;
+                       $log->info("\tProcessing of hold ".$hold->id." complete.");
+
+                       push @successes,
+                               { reservation => $hold->id,
+                                 current_resource => ($best ? $best->id : undef) };
+
+               } otherwise {
+                       my $e = shift;
+                       if ($e !~ /^OK/o) {
+                               $log->error("Processing of hold failed:  $e");
+                               $self->method_lookup('open-ils.storage.transaction.rollback')->run;
+                               throw $e if ($e =~ /IS NOT CONNECTED TO THE NETWORK/o);
+                       }
+               };
+       }
+
+       return \@successes;
+}
+__PACKAGE__->register_method(
+       api_name        => 'open-ils.storage.booking.reservation.resource_targeter',
+       api_level       => 1,
+       method          => 'reservation_targeter',
+);
+
 my $locations;
 my $statuses;
 my %cache = (titles => {}, cns => {});