[open-ils-commits] r15878 - in branches/rel_1_6/Open-ILS: examples src/extras src/perlmods/OpenILS/Application src/perlmods/OpenILS/Application/Circ src/perlmods/OpenILS/Application/Storage/Publisher web/css/skin/default web/js/dojo/openils/booking/nls web/js/ui/default/booking web/opac/locale/en-US web/opac/skin/default/js web/opac/skin/default/xml/rdetail web/templates/default/booking web/templates/default/conify/global/booking xul/staff_client/server/cat xul/staff_client/server/circ xul/staff_client/server/locale/en-US xul/staff_client/server/patron (senator)

svn at svn.open-ils.org svn at svn.open-ils.org
Wed Mar 17 15:10:25 EDT 2010


Author: senator
Date: 2010-03-17 15:10:20 -0400 (Wed, 17 Mar 2010)
New Revision: 15878

Modified:
   branches/rel_1_6/Open-ILS/examples/fm_IDL.xml
   branches/rel_1_6/Open-ILS/src/extras/ils_events.xml
   branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm
   branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
   branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
   branches/rel_1_6/Open-ILS/web/css/skin/default/booking.css
   branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/capture.js
   branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pickup_and_return.js
   branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pull_list.js
   branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/reservation.js
   branches/rel_1_6/Open-ILS/web/js/ui/default/booking/capture.js
   branches/rel_1_6/Open-ILS/web/js/ui/default/booking/common.js
   branches/rel_1_6/Open-ILS/web/js/ui/default/booking/populator.js
   branches/rel_1_6/Open-ILS/web/js/ui/default/booking/pull_list.js
   branches/rel_1_6/Open-ILS/web/js/ui/default/booking/reservation.js
   branches/rel_1_6/Open-ILS/web/opac/locale/en-US/lang.dtd
   branches/rel_1_6/Open-ILS/web/opac/locale/en-US/opac.dtd
   branches/rel_1_6/Open-ILS/web/opac/skin/default/js/copy_details.js
   branches/rel_1_6/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml
   branches/rel_1_6/Open-ILS/web/templates/default/booking/capture.tt2
   branches/rel_1_6/Open-ILS/web/templates/default/booking/pull_list.tt2
   branches/rel_1_6/Open-ILS/web/templates/default/conify/global/booking/resource_type.tt2
   branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/copy_browser.xul
   branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/util.js
   branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/checkout.js
   branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/util.js
   branches/rel_1_6/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties
   branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display.js
   branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul
   branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_overlay.xul
   branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.js
   branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.xul
Log:
Booking: a whole mess of bug fixes and improvements

One of the bigger features in here is that booking resources (items) can
be captured in the regular checkin interface now and not just in the
special booking capture interface.

There are many more subtle changes to both backend logic and UI access
points to booking. This same code will need to be merged neatly with trunk,
too.

Apologies for the giant commit.  This code has been accumulating for
entirely too long outside of the repo.


Modified: branches/rel_1_6/Open-ILS/examples/fm_IDL.xml
===================================================================
--- branches/rel_1_6/Open-ILS/examples/fm_IDL.xml	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/examples/fm_IDL.xml	2010-03-17 19:10:20 UTC (rev 15878)
@@ -2215,7 +2215,7 @@
 
 	<class id="brt" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="booking::resource_type" oils_persist:tablename="booking.resource_type" reporter:label="Resource Type">
 		<fields oils_persist:primary="id" oils_persist:sequence="booking.resource_type_id_seq">
-			<field reporter:label="Resource Type ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Resource Type ID" name="id" reporter:datatype="id" reporter:selector="name"/>
 			<field reporter:label="Resource Type Name" name="name" reporter:datatype="text"/>
 			<field reporter:label="Fine Interval" name="fine_interval" reporter:datatype="interval"/>
 			<field reporter:label="Fine Amount" name="fine_amount" reporter:datatype="money"/>
@@ -2248,7 +2248,7 @@
 
 	<class id="brsrc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="booking::resource" oils_persist:tablename="booking.resource" reporter:label="Resource">
 		<fields oils_persist:primary="id" oils_persist:sequence="booking.resource_id_seq">
-			<field reporter:label="Resource ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Resource ID" name="id" reporter:datatype="id" reporter:selector="barcode" />
 			<field reporter:label="Owning Library" name="owner" reporter:datatype="org_unit"/>
 			<field reporter:label="Resource Type" name="type" reporter:datatype="link"/>
 			<field reporter:label="Overbook" name="overbook" reporter:datatype="bool"/>
@@ -2280,7 +2280,7 @@
 	
 	<class id="bra" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="booking::resource_attr" oils_persist:tablename="booking.resource_attr" reporter:label="Resource Attribute">
 		<fields oils_persist:primary="id" oils_persist:sequence="booking.resource_attr_id_seq">
-			<field reporter:label="Resource Attribute ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Resource Attribute ID" name="id" reporter:datatype="id" reporter:selector="name" />
 			<field reporter:label="Owning Library" name="owner" reporter:datatype="org_unit"/>
 			<field reporter:label="Resource Attribute Name" name="name" reporter:datatype="text"/>
 			<field reporter:label="Resource Type" name="resource_type" reporter:datatype="link"/>
@@ -2306,7 +2306,7 @@
 	
 	<class id="brav" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="booking::resource_attr_value" oils_persist:tablename="booking.resource_attr_value" reporter:label="Resource Attribute Value">
 		<fields oils_persist:primary="id" oils_persist:sequence="booking.resource_attr_value_id_seq">
-			<field reporter:label="Resource Attribute Value ID" name="id" reporter:datatype="id"/>
+			<field reporter:label="Resource Attribute Value ID" name="id" reporter:datatype="id" reporter:selector="valid_value" />
 			<field reporter:label="Owning Library" name="owner" reporter:datatype="org_unit"/>
 			<field reporter:label="Resource Attribute" name="attr" reporter:datatype="link"/>
 			<field reporter:label="Valid Value" name="valid_value" reporter:datatype="text"/>
@@ -3861,7 +3861,6 @@
 			<field reporter:label="Send Date/Time" name="source_send_time" reporter:datatype="timestamp"/>
 			<field reporter:label="Transited Copy" name="target_copy" reporter:datatype="link"/>
 			<field reporter:label="Base Transit" name="transit_copy" oils_persist:virtual="true" reporter:datatype="link"/>
-			<field reporter:label="Prev Destination Library" name="prev_dest" reporter:datatype="org_unit"/>
 		</fields>
 		<links>
 			<link field="transit_copy" reltype="might_have" key="id" map="" class="atc"/>
@@ -3869,7 +3868,6 @@
 			<link field="source" reltype="has_a" key="id" map="" class="aou"/>
 			<link field="copy_status" reltype="has_a" key="id" map="" class="ccs"/>
 			<link field="dest" reltype="has_a" key="id" map="" class="aou"/>
-			<link field="prev_dest" reltype="has_a" key="id" map="" class="aou"/>
 			<link field="reservation" reltype="has_a" key="id" map="" class="bresv"/>
 		</links>
         <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">

Modified: branches/rel_1_6/Open-ILS/src/extras/ils_events.xml
===================================================================
--- branches/rel_1_6/Open-ILS/src/extras/ils_events.xml	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/src/extras/ils_events.xml	2010-03-17 19:10:20 UTC (rev 15878)
@@ -826,9 +826,10 @@
 	<event code='7023' textcode='RESERVATION_BAD_PARAMS'>
 		<desc xml:lang="en-US">Provided parameters describe unacceptable reservation.</desc>
 	</event>
+	<event code="7024" textcode="HOLD_RESERVATION_CONFLICT">
+		<desc xml:lang="en-US">Both a hold and a reservation exist for this item; staff intervention is required to resolve the conflict.</desc>
+	</event>
 
-
-
 	<!-- ================================================================ -->
 
 

Modified: branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm
===================================================================
--- branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Booking.pm	2010-03-17 19:10:20 UTC (rev 15878)
@@ -7,6 +7,7 @@
 use OpenILS::Application;
 use base qw/OpenILS::Application/;
 
+use OpenSRF::Utils qw/:datetime/;
 use OpenILS::Utils::CStoreEditor qw/:funcs/;
 use OpenILS::Utils::Fieldmapper;
 use OpenILS::Application::AppUtils;
@@ -319,7 +320,7 @@
     return undef unless ($filters->{type} || $filters->{attribute_values});
 
     my $query = {
-        "select"   => {brsrc => ["id"]},
+        "select"   => {brsrc => [qw/id owner/], brt => ["elbow_room"]},
         "from"     => {brsrc => {"brt" => {}}},
         "where"    => {},
         "distinct" => 1
@@ -373,6 +374,7 @@
                             "end_time"
                         ),
                         {"cancel_time" => undef},
+                        {"return_time" => undef},
                         {"current_resource" => {"=" => {"+brsrc" => "id"}}}
                     ]},
                 }}
@@ -408,21 +410,58 @@
     }
 
     my $cstore = OpenSRF::AppSession->connect('open-ils.cstore');
-    my $rows = $cstore->request( 'open-ils.cstore.json_query.atomic', $query )->gather(1);
+    my $rows = $cstore->request(
+        "open-ils.cstore.json_query.atomic", $query
+    )->gather(1);
     $cstore->disconnect;
 
-    return @$rows ? [map { $_->{id} } @$rows] : [];
+    return [] if not @$rows;
+
+    if ($filters->{"pickup_lib"} && $filters->{"available"}) {
+        my @new_rows = ();
+        my $general_elbow_room = $U->ou_ancestor_setting_value(
+            $filters->{"pickup_lib"},
+            "circ.booking_reservation.default_elbow_room"
+        ) || '0 seconds';
+        my $would_start = $filters->{"available"}->[0];
+        my $dt_parser = new DateTime::Format::ISO8601;
+
+        $logger->info(
+            "general_elbow_room: '$general_elbow_room', " .
+            "would_start: '$would_start'"
+        );
+
+        # Here, elbow_room will double as required transit time padding.
+        foreach (@$rows) {
+            my $elbow_room = $_->{"elbow_room"} || $general_elbow_room;
+            if ($_->{"owner"} != $filters->{"pickup_lib"}) {
+                (my $ws = $would_start) =~ s/ /T/;
+                push @new_rows, $_ if DateTime->compare(
+                    $dt_parser->parse_datetime($ws),
+                    DateTime->now(
+                        "time_zone" => DateTime::TimeZone->new(
+                            "name" => "local"
+                        )
+                    )->add(seconds => interval_to_seconds($elbow_room))
+                ) >= 0;
+            } else {
+                push @new_rows, $_;
+            }
+        }
+        return [map { $_->{id} } @new_rows];
+    } else {
+        return [map { $_->{id} } @$rows];
+    }
 }
 __PACKAGE__->register_method(
     method   => "resource_list_by_attrs",
     api_name => "open-ils.booking.resources.filtered_id_list",
-    argc     => 3,
+    argc     => 2,
     signature=> {
         params => [
             {type => 'string', desc => 'Authentication token (unused for now,' .
                ' but at least pass undef here)'},
             {type => 'object', desc => 'Filter object: see notes for details'},
-            {type => 'bool', desc => 'Return whole objects instead of IDs?'}
         ],
         return => { desc => "An array of brsrc ids matching the requested filters." },
     },
@@ -541,11 +580,12 @@
 __PACKAGE__->register_method(
     method   => "reservation_list_by_filters",
     api_name => "open-ils.booking.reservations.filtered_id_list",
-    argc     => 2,
+    argc     => 3,
     signature=> {
         params => [
             {type => 'string', desc => 'Authentication token'},
-            {type => 'object', desc => 'Filter object -- see notes for details'}
+            {type => "object", desc => "Filter object: see notes for details"},
+            {type => "bool", desc => "Return whole object instead of ID? (default false)"}
         ],
         return => { desc => "An array of bresv ids matching the requested filters." },
     },
@@ -572,7 +612,7 @@
 sub naive_ts_string {strftime("%F %T", localtime($_[0] || time));}
 sub naive_start_of_day {strftime("%F", localtime($_[0] || time))." 00:00:00";}
 
-# Return a list of bresv or an ilsevent on failure.
+# Return a map of bresv or an ilsevent on failure.
 sub get_uncaptured_bresv_for_brsrc {
     my ($e, $o) = @_; # o's keys (all optional): owning_lib, barcode, range
 
@@ -630,6 +670,10 @@
                 "-and" => [
                     {"current_resource" => "PLACEHOLDER"},
                     {"start_time" => "PLACEHOLDER"},
+                    {"capture_time" => undef},
+                    {"cancel_time" => undef},
+                    {"return_time" => undef},
+                    {"pickup_time" => undef}
                 ]
             }
         };
@@ -727,6 +771,49 @@
 );
 
 
+sub could_capture {
+    my ($self, $client, $auth, $barcode) = @_;
+
+    my $e = new_editor("authtoken" => $auth);
+    return $e->die_event unless $e->checkauth;
+    return $e->die_event unless $e->allowed("CAPTURE_RESERVATION");
+
+    my $res = get_uncaptured_bresv_for_brsrc($e, {"barcode" => $barcode});
+    if ($res and keys %$res) {
+        my $id;
+        while ((undef, $id) = each %$res) {
+            $client->respond(
+                $e->retrieve_booking_reservation([
+                    $id, {
+                        "flesh" => 1, "flesh_fields" => {
+                            "bresv" => [qw(
+                                usr target_resource_type
+                                target_resource current_resource
+                            )]
+                        }
+                    }
+                ])
+            );
+        }
+    }
+    $e->disconnect;
+    $client->respond_complete;
+}
+__PACKAGE__->register_method(
+    method   => "could_capture",
+    api_name => "open-ils.booking.reservations.could_capture",
+    argc     => 2,
+    streaming=> 1,
+    signature=> {
+        params => [
+            {type => "string", desc => "Authentication token"},
+            {type => "string", desc => "Resource barcode"}
+        ],
+        return => {desc => "One or zero reservations; event on error."}
+    }
+);
+
+
 sub get_copy_fleshed_just_right {
     my ($self, $client, $auth, $barcode) = @_;
 
@@ -771,7 +858,10 @@
     my ($e, $id_list) = @_;
 
     # This will almost always be the case.
-    return $id_list->[0] if @$id_list == 1;
+    if (@$id_list == 1) {
+        $logger->info("best_bresv_candidate (only) " . $id_list->[0]);
+        return $id_list->[0];
+    }
 
     my @here = ();
     my $this_ou = $e->requestor->ws_ou;
@@ -791,40 +881,48 @@
         push @here, $_->{"id"} if $_->{"pickup_lib"} == $this_ou;
     }
 
+    my $result;
     if (@here > 0) {
-        return pop @here if @here == 1;
-        return (sort @here)[0];
+        $result = @here == 1 ? pop @here : (sort @here)[0];
     } else {
-        return (sort @$id_list)[0];
+        $result = (sort @$id_list)[0];
     }
+    $logger->info(
+        "best_bresv_candidate from " . join(",", @$id_list) . ": $result"
+    );
+    return $result;
 }
 
 
 sub capture_resource_for_reservation {
-    my ($self, $client, $auth, $barcode) = @_;
+    my ($self, $client, $auth, $barcode, $no_update_copy) = @_;
 
-    my $e = new_editor(xact => 1, authtoken => $auth);
+    my $e = new_editor(authtoken => $auth);
     return $e->die_event unless $e->checkauth;
     return $e->die_event unless $e->allowed("CAPTURE_RESERVATION");
 
     my $uncaptured = get_uncaptured_bresv_for_brsrc(
         $e, {"barcode" => $barcode}
     );
-    $e->disconnect;
 
     if (keys %$uncaptured) {
         # Note this will only capture one reservation at a time, even in
         # cases with overbooking (multiple "soonest" bresv's on a resource).
-        my $key = (sort(keys %$uncaptured))[0];
+        my $bresv = best_bresv_candidate(
+            $e, $uncaptured->{
+                (sort(keys %$uncaptured))[0]
+            }
+        );
+        $e->disconnect;
         return capture_reservation(
-            $self, $client, $auth, best_bresv_candidate($e, $uncaptured->{$key})
+            $self, $client, $auth, $bresv, $no_update_copy
         );
     } else {
         return new OpenILS::Event(
             "RESERVATION_NOT_FOUND",
-            desc => "No capturable reservation found pertaining " .
+            "desc" => "No capturable reservation found pertaining " .
                 "to a resource with barcode $barcode",
-            payload => {fail_cause => 'no-reservation', captured => 0}
+            "payload" => {"fail_cause" => "no-reservation", "captured" => 0}
         );
     }
 }
@@ -836,7 +934,7 @@
         params => [
             {type => "string", desc => "Authentication token"},
             {type => "string", desc => "Barcode of booked & targeted resource"},
-            {type => "int", desc => "Pickup library (default to client ws_ou)"},
+            {type => "number", desc => "(optional) 1 to not update copy"}
         ],
         return => { desc => "An OpenILS event describing the capture outcome" }
     }
@@ -844,89 +942,123 @@
 
 
 sub capture_reservation {
-    my ($self, $client, $auth, $res_id) = @_;
+    my ($self, $client, $auth, $res_id, $no_update_copy) = @_;
 
-    my $e = new_editor(xact => 1, authtoken => $auth);
-    return $e->event unless $e->checkauth;
-    return $e->event unless $e->allowed('CAPTURE_RESERVATION');
+    my $e = new_editor("xact" => 1, "authtoken" => $auth);
+    return $e->die_event unless $e->checkauth;
+    return $e->die_event unless $e->allowed('CAPTURE_RESERVATION');
     my $here = $e->requestor->ws_ou;
 
     my $reservation = $e->retrieve_booking_reservation([
         $res_id, {
-            flesh => 2,
-            flesh_fields => {"bresv" => ["usr"], "au" => ["card"]}
+            "flesh" => 2, "flesh_fields" => {
+                "bresv" => [qw/usr current_resource type/],
+                "au" => ["card"],
+                "brsrc" => ["type"]
+            }
         }
     ]);
-    return OpenILS::Event->new('RESERVATION_NOT_FOUND') unless $reservation;
 
-    return OpenILS::Event->new('RESERVATION_CAPTURE_FAILED', payload => { captured => 0, fail_cause => 'no-resource' })
-        if (!$reservation->current_resource); # no resource
+    return new OpenILS::Event("RESERVATION_NOT_FOUND") unless $reservation;
+    return new OpenILS::Event(
+        "RESERVATION_CAPTURE_FAILED",
+        payload => {"captured" => 0, "fail_cause" => "no-resource"}
+    ) unless $reservation->current_resource;
 
-    return OpenILS::Event->new('RESERVATION_CAPTURE_FAILED', payload => { captured => 0, fail_cause => 'cancelled' })
-        if ($reservation->cancel_time); # canceled
+    return new OpenILS::Event(
+        "RESERVATION_CAPTURE_FAILED",
+        "payload" => {"captured" => 0, "fail_cause" => "cancelled"}
+    ) if $reservation->cancel_time;
 
-    my $resource = $e->retrieve_booking_resource( $reservation->current_resource );
-    my $type = $e->retrieve_booking_resource_type( $resource->type );
+    $reservation->capture_staff($e->requestor->id);
+    $reservation->capture_time("now");
 
-    $reservation->capture_staff( $e->requestor->id );
-    $reservation->capture_time( 'now' );
+    $e->update_booking_reservation($reservation) or return $e->die_event;
 
-    my $reservation_id = undef;
-    return $e->event unless ( $e->update_booking_reservation( $reservation ) and $reservation_id = $e->data );
+    my $ret = {"captured" => 1, "reservation" => $reservation};
 
-    $reservation->id($reservation_id);
+    my $search_acp_like_this = [
+        {
+            "barcode" => $reservation->current_resource->barcode,
+            "deleted" => "f"
+        },
+        {"flesh" => 1, "flesh_fields" => {"acp" => ["call_number"]}}
+    ];
 
-    my $ret = { captured => 1, reservation => $reservation };
-
     if ($here != $reservation->pickup_lib) {
-        return OpenILS::Event->new('RESERVATION_CAPTURE_FAILED', payload => { captured => 0, fail_cause => 'not-transferable' })
-            if (!$U->is_true($type->transferable)); # non-transferable resource
+        return new OpenILS::Event(
+            "RESERVATION_CAPTURE_FAILED",
+            "payload" => {"captured" => 0, "fail_cause" => "not-transferable"}
+        ) unless $U->is_true(
+            $reservation->current_resource->type->transferable
+        );
 
         # need to transit the item ... is it already in transit?
-        my $transit = $e->search_action_reservation_transit_copy( { reservation => $res_id, dest_recv_time => undef } )->[0];
+        my $transit = $e->search_action_reservation_transit_copy(
+            {"reservation" => $res_id, "dest_recv_time" => undef}
+        )->[0];
 
         if (!$transit) { # not yet in transit
             $transit = new Fieldmapper::action::reservation_transit_copy;
 
             $transit->reservation($reservation->id);
-            $transit->target_copy($resource->id);
+            $transit->target_copy($reservation->current_resource->id);
             $transit->copy_status(15);
-            $transit->source_send_time('now');
+            $transit->source_send_time("now");
             $transit->source($here);
             $transit->dest($reservation->pickup_lib);
 
-            $e->create_action_reservation_transit_copy( $transit );
+            $e->create_action_reservation_transit_copy($transit);
 
-            if ($U->is_true($type->catalog_item)) {
-                my $copy = $e->search_asset_copy( { barcode => $resource->barcode, deleted => 'f' } )->[0];
+            if ($U->is_true(
+                $reservation->current_resource->type->catalog_item
+            )) {
+                my $copy = $e->search_asset_copy($search_acp_like_this)->[0];
 
                 if ($copy) {
                     return new OpenILS::Event(
                         "OPEN_CIRCULATION_EXISTS",
-                        payload => { captured => 0, copy => $copy }
+                        "payload" => {"captured" => 0, "copy" => $copy}
                     ) if $copy->status == 1;
-                    $copy->status(6);
-                    $e->update_asset_copy( $copy );
-                    $$ret{catalog_item} = $copy; # $e->data is just id (int)
+
+                    $ret->{"mvr"} = get_mvr($copy->call_number->record);
+                    if ($no_update_copy) {
+                        $ret->{"new_copy_status"} = 6;
+                    } else {
+                        $copy->status(6);
+                        $e->update_asset_copy($copy) or return $e->die_event;
+                    }
                 }
             }
         }
 
-        $$ret{transit} = $transit;
-    } elsif ($U->is_true($type->catalog_item)) {
-        my $copy = $e->search_asset_copy( { barcode => $resource->barcode, deleted => 'f' } )->[0];
+        $ret->{"transit"} = $transit;
+    } elsif ($U->is_true($reservation->current_resource->type->catalog_item)) {
+        my $copy = $e->search_asset_copy($search_acp_like_this)->[0];
 
         if ($copy) {
-            return OpenILS::Event->new('OPEN_CIRCULATION_EXISTS', payload => { captured => 0, copy => $copy }) if ($copy->status == 1);
-            $copy->status(15);
-            $e->update_asset_copy( $copy );
-            $$ret{catalog_item} = $copy; # $e->data is just id (int)
+            return new OpenILS::Event(
+                "OPEN_CIRCULATION_EXISTS",
+                "payload" => {"captured" => 0, "copy" => $copy}
+            ) if $copy->status == 1;
+
+            $ret->{"mvr"} = get_mvr($copy->call_number->record);
+            if ($no_update_copy) {
+                $ret->{"new_copy_status"} = 15;
+            } else {
+                $copy->status(15);
+                $e->update_asset_copy($copy) or return $e->die_event;
+            }
         }
     }
 
-    $e->commit;
+    $e->commit or return $e->die_event;
 
-    return OpenILS::Event->new('SUCCESS', payload => $ret);
+    # XXX I'm not sure whether these last two elements of the payload
+    # actually get used anywhere.
+    $ret->{"resource"} = $reservation->current_resource;
+    $ret->{"type"} = $reservation->current_resource->type;
+    return new OpenILS::Event("SUCCESS", "payload" => $ret);
 }
 __PACKAGE__->register_method(
     method   => "capture_reservation",
@@ -960,10 +1092,18 @@
     ]);
     return $e->die_event if not $bresv_list;
 
+    my @results = ();
     my $circ = OpenSRF::AppSession->connect("open-ils.circ") or
         return $e->die_event;
-    my @results = ();
     foreach my $bresv (@$bresv_list) {
+        $bresv->cancel_time("now");
+        $e->update_booking_reservation($bresv) or do {
+            $circ->disconnect;
+            return $e->die_event;
+        };
+        $e->xact_commit;
+        $e->xact_begin;
+
         if (
             $bresv->target_resource_type->catalog_item == "t" &&
             $bresv->current_resource
@@ -975,16 +1115,10 @@
                         "noop" => 1}
                 )->gather(1)->{"textcode"});
         }
-        $bresv->cancel_time("now");
-        $e->update_booking_reservation($bresv) or do {
-            $circ->disconnect;
-            return $e->die_event;
-        };
-
         push @results, $bresv->id;
     }
 
-    $e->commit;
+    $e->disconnect;
     $circ->disconnect;
 
     return \@results;
@@ -1094,7 +1228,7 @@
     my $e = new_editor(xact => 1, authtoken => $auth);
     return $e->die_event unless $e->checkauth;
     return $e->die_event unless $e->allowed("VIEW_USER");
-    return $e->die_event unless $e->allowed("ADMIN_BOOKING_RESERVATION");
+#    return $e->die_event unless $e->allowed("ADMIN_BOOKING_RESERVATION");
 
     my $rows = $e->json_query({
         "select" => {"bresv" => ["id"]},

Modified: branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm
===================================================================
--- branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm	2010-03-17 19:10:20 UTC (rev 15878)
@@ -14,6 +14,11 @@
 my $script_libs;
 my $legacy_script_support = 0;
 
+my $MK_ENV_FLESH = { 
+    flesh => 2, 
+    flesh_fields => {acp => ['call_number'], acn => ['record']} 
+};
+
 sub initialize {
 
     my $self = shift;
@@ -187,7 +192,7 @@
             my $res_id_list = [ map { $_->id } @$resources ];
             my $transit = $circulator->editor->search_action_reservation_transit_copy(
                 [
-                    { target_copy => $res_id_list, dest => $circulator->circ_lib },
+                    { target_copy => $res_id_list, dest => $circulator->circ_lib, dest_recv_time => undef },
                     { order_by => { artc => 'source_send_time' }, limit => 1 }
                 ]
             )->[0]; # Any transit for this barcode?
@@ -197,12 +202,21 @@
                 my $reservation = $circulator->editor->retrieve_booking_reservation( $transit->reservation );
                 my $res_type = $circulator->editor->retrieve_booking_resource_type( $reservation->target_resource_type );
 
+                my $success_event = new OpenILS::Event(
+                    "SUCCESS", "payload" => {"reservation" => $reservation}
+                );
                 if ($U->is_true($res_type->catalog_item)) { # is there a copy to be had here?
-                    if (my $copy = $circulator->editor->search_asset_copy({ barcode => $bc, deleted => 'f' })->[0]) { # got a copy
+                    if (my $copy = $circulator->editor->search_asset_copy([
+                        { barcode => $bc, deleted => 'f' }, $MK_ENV_FLESH
+                    ])->[0]) { # got a copy
                         $copy->status( $transit->copy_status );
                         $copy->editor($circulator->editor->requestor->id);
                         $copy->edit_date('now');
-                        $circulator->editor->update_asset_copy( $copy );
+                        $circulator->editor->update_asset_copy($copy);
+                        $success_event->{"payload"}->{"record"} =
+                            $U->record_to_mvr($copy->call_number->record);
+                        $copy->call_number($copy->call_number->id);
+                        $success_event->{"payload"}->{"copy"} = $copy;
                     }
                 }
 
@@ -210,51 +224,9 @@
                 $circulator->editor->update_action_reservation_transit_copy( $transit );
 
                 $circulator->editor->commit;
-
-                #XXX need to return here, with info about the resource/copy and the "put it on the booking shelf" message
-
-            } else { # no transit, look for an upcoming reservation to capture for
-
-                my $reservation = $circulator->editor->search_booking_reservation(
-                    [
-                        { current_resource => $res_id_list,
-                          pickup_lib => $circulator->circ_lib,
-                          cancel_time => undef,
-                          capture_time => undef
-                        },
-                        { order_by => { bresv => 'start_time' }, limit => 1 }
-                    ]
-                )->[0];
-
-                if ($reservation) { # we have a reservation for which we could capture this resource.  wheee!
-                    my $res_type = $circulator->editor->retrieve_booking_resource_type( $reservation->target_resource_type );
-                    my $elbow_room = $res_type->elbow_room ||
-                        $U->ou_ancestor_setting_value( $circulator->circ_lib, 'circ.booking_reservation.default_elbow_room', $circulator->editor );
-                
-                    if ($elbow_room) {
-                        $reservation = $circulator->editor->search_booking_reservation(
-                            [
-                                { id => $reservation->id, start_time => { '<=' => DateTime->now->add( seconds => interval_to_seconds($elbow_room) )->strftime('%FT%T%z') } },
-                                { order_by => { bresv => 'start_time' }, limit => 1 }
-                            ]
-                        )->[0];
-                    }
-
-                    if ($reservation) { # no elbow room specified, or we still have a reservation within the elbow_room time
-                        my $b_ses = OpenSRF::AppSession->create('open-ils.booking');
-                        my $result = $b_ses->request(
-                            'open-ils.booking.reservations.capture',
-                            $auth => $reservation->id
-                        )->gather(1);
-
-                        if (ref($result) && $result->{ilsevent} == 0) { # captured!
-                            #XXX what to return here???
-                            return $result; # the booking capture success
-                        } else {
-                            #XXX how to fail???  Probably, just move on.
-                        }
-                    }
-                }
+                # Formerly this branch just stopped here. Argh!
+                $conn->respond_complete($success_event);
+                return;
             }
         }
     }
@@ -265,12 +237,13 @@
     # Go ahead and load the script runner to make sure we have all 
     # of the objects we need
     # --------------------------------------------------------------------------
-    $circulator->is_res_checkin($circulator->is_checkin(1)) if $api =~ /reservation.return/;
+    $circulator->is_res_checkin($circulator->is_checkin(1)) if $api =~ /reservation.return/ or $circulator->seems_like_reservation();
     $circulator->is_res_checkout(1) if $api =~ /reservation.pickup/;
 
     $circulator->is_renewal(1) if $api =~ /renew/;
     $circulator->is_checkin(1) if $api =~ /checkin/;
 
+    $circulator->mk_env();
     if($legacy_script_support and not $circulator->is_checkin) {
         $circulator->mk_script_runner();
         $circulator->legacy_script_support(1);
@@ -278,8 +251,6 @@
         $circulator->circ_permit_copy($scripts{circ_permit_copy});      
         $circulator->circ_duration($scripts{circ_duration});             
         $circulator->circ_permit_renew($scripts{circ_permit_renew});
-    } elsif (not $circulator->is_res_checkin) { # mk_env cannot work w/ reservation.return
-        $circulator->mk_env();
     }
     return circ_events($circulator) if $circulator->bail_out;
 
@@ -452,8 +423,8 @@
     is_renewal
     is_checkout
     is_res_checkout
+    is_precat
     is_noncat
-    is_precat
     request_precat
     is_checkin
     is_res_checkin
@@ -588,6 +559,9 @@
     my( $self, @evts ) = @_;
     for my $e (@evts) {
         next unless $e;
+        $e->{payload} = $self->copy if 
+              ($e->{textcode} eq 'COPY_NOT_AVAILABLE');
+
         $logger->info("circulator: pushing event ".$e->{textcode});
         push( @{$self->events}, $e ) unless
             grep { $_->{textcode} eq $e->{textcode} } @{$self->events};
@@ -611,6 +585,49 @@
     return ($one) ? 1 : 0;
 }
 
+sub seems_like_reservation {
+    my $self = shift;
+
+    # Some words about the following method:
+    # 1) It requires the VIEW_USER permission, but that's not an
+    # issue, right, since all staff should have that?
+    # 2) It returns only one reservation at a time, even if an item can be
+    # and is currently overbooked.  Hmmm....
+    my $booking_ses = create OpenSRF::AppSession("open-ils.booking");
+    my $result = $booking_ses->request(
+        "open-ils.booking.reservations.by_returnable_resource_barcode",
+        $self->editor->authtoken,
+        $self->copy_barcode
+    )->gather(1);
+    $booking_ses->disconnect;
+
+    return $self->bail_on_events($result) if defined $U->event_code($result);
+
+    if (@$result > 0) {
+        $self->reservation(shift @$result);
+        return 1;
+    } else {
+        return 0;
+    }
+
+}
+
+# save_trimmed_copy() used just to be a block in mk_env(), but was separated for re-use
+sub save_trimmed_copy {
+    my ($self, $copy) = @_;
+
+    $self->copy($copy);
+    $self->volume($copy->call_number);
+    $self->title($self->volume->record);
+    $self->copy->call_number($self->volume->id);
+    $self->volume->record($self->title->id);
+    $self->is_precat(1) if $self->volume->id == OILS_PRECAT_CALL_NUMBER;
+    if($self->copy->deposit_amount and $self->copy->deposit_amount > 0) {
+        $self->is_deposit(1) if $U->is_true($self->copy->deposit);
+        $self->is_rental(1) unless $U->is_true($self->copy->deposit);
+    }
+}
+
 sub mk_env {
     my $self = shift;
     my $e = $self->editor;
@@ -620,31 +637,49 @@
     # --------------------------------------------------------------------------
     unless($self->is_noncat) {
         my $copy;
-	    my $flesh = { 
-		    flesh => 2, 
-		    flesh_fields => {acp => ['location', 'status', 'circ_lib', 'age_protect', 'call_number'], acn => ['record']}
-	    };
 	    if($self->copy_id) {
 		    $copy = $e->retrieve_asset_copy(
-			    [$self->copy_id, $flesh ]) or return $e->event;
+			    [$self->copy_id, $MK_ENV_FLESH ]) or return $e->event;
     
 	    } elsif( $self->copy_barcode ) {
     
 		    $copy = $e->search_asset_copy(
-			    [{barcode => $self->copy_barcode, deleted => 'f'}, $flesh ])->[0];
-	    }
+			    [{barcode => $self->copy_barcode, deleted => 'f'}, $MK_ENV_FLESH ])->[0];
+	    } elsif( $self->reservation ) {
+            my $res = $e->json_query(
+                {
+                    "select" => {"acp" => ["id"]},
+                    "from" => {
+                        "acp" => {
+                            "brsrc" => {
+                                "fkey" => "barcode",
+                                "field" => "barcode",
+                                "join" => {
+                                    "bresv" => {
+                                        "fkey" => "id",
+                                        "field" => "current_resource"
+                                    }
+                                }
+                            }
+                        }
+                    },
+                    "where" => {
+                        "+bresv" => {
+                            "id" => (ref $self->reservation) ?
+                                $self->reservation->id : $self->reservation
+                        }
+                    }
+                }
+            );
+            if (ref $res eq "ARRAY" and scalar @$res) {
+                $logger->info("circulator: mapped reservation " .
+                    $self->reservation . " to copy " . $res->[0]->{"id"});
+                $copy = $e->retrieve_asset_copy([$res->[0]->{"id"}, $MK_ENV_FLESH]);
+            }
+        }
     
         if($copy) {
-            $self->copy($copy);
-            $self->volume($copy->call_number);
-            $self->title($self->volume->record);
-            $self->copy->call_number($self->volume->id);
-            $self->volume->record($self->title->id);
-            $self->is_precat(1) if $self->volume->id == OILS_PRECAT_CALL_NUMBER;
-            if($self->copy->deposit_amount and $self->copy->deposit_amount > 0) {
-                $self->is_deposit(1) if $U->is_true($self->copy->deposit);
-                $self->is_rental(1) unless $U->is_true($self->copy->deposit);
-            }
+            $self->save_trimmed_copy($copy);
         } else {
             # We can't renew if there is no copy
             return $self->bail_on_events(OpenILS::Event->new('ASSET_COPY_NOT_FOUND'))
@@ -1154,11 +1189,6 @@
    my %hash = map { ($_->{ilsevent} => $_) } @allevents;
    @allevents = values %hash;
 
-   for (@allevents) {
-      $_->{payload} = $copy if 
-            ($_->{textcode} eq 'COPY_NOT_AVAILABLE');
-   }
-
     $logger->info("circulator: permit_copy script returned events: @allevents") if @allevents;
 
     $self->push_events(@allevents);
@@ -1674,13 +1704,17 @@
 
     $self->log_me("do_reservation_return()");
 
-    my ($reservation, $evt) = $U->fetch_booking_reservation($self->reservation);
-    return $self->bail_on_events($evt) if $evt;
+    if (not ref $self->reservation) {
+        my ($reservation, $evt) =
+            $U->fetch_booking_reservation($self->reservation);
+        return $self->bail_on_events($evt) if $evt;
+        $self->reservation($reservation);
+    }
 
-    $self->reservation( $reservation );
     $self->generate_fines(1);
     $self->reservation->return_time('now');
     $self->update_reservation();
+    $self->reshelve_copy if $self->copy;
 
     if ( $self->reservation->current_resource && $self->reservation->current_resource->catalog_item ) {
         $self->copy( $self->reservation->current_resource->catalog_item );
@@ -1720,7 +1754,7 @@
         my $booking_ses = OpenSRF::AppSession->create( 'open-ils.booking' );
         my $bookings = $booking_ses->request(
             'open-ils.booking.reservations.filtered_id_list', $self->editor->authtoken,
-            { resource => $booking_item->id, search_start => 'now', search_end => $circ->due_date }
+            { resource => $booking_item->id, search_start => 'now', search_end => $circ->due_date, fields => { cancel_time => undef }}
         )->gather(1);
         $booking_ses->disconnect;
         
@@ -2029,10 +2063,30 @@
 
     if(!$self->noop) { # /not/ a no-op checkin, capture for hold or put item into transit
 
-        my $needed_for_hold = (!$self->remote_hold and $self->attempt_checkin_hold_capture());
+        my $needed_for_something = 0; # formerly "needed_for_hold"
+        if (!$self->remote_hold) {
+            my $potential_hold = $self->hold_capture_is_possible;
+            my $potential_reservation = $self->reservation_capture_is_possible;
+
+            if ($potential_hold and $potential_reservation) {
+                $logger->info("circulator: item could fulfill either hold or reservation");
+                $self->push_events(new OpenILS::Event(
+                    "HOLD_RESERVATION_CONFLICT",
+                    "hold" => $potential_hold,
+                    "reservation" => $potential_reservation
+                ));
+                return if $self->bail_out;
+            } elsif ($potential_hold) {
+                $needed_for_something =
+                    $self->attempt_checkin_hold_capture;
+            } elsif ($potential_reservation) {
+                $needed_for_something =
+                    $self->attempt_checkin_reservation_capture;
+            }
+        }
         return if $self->bail_out;
     
-        unless($needed_for_hold) {
+        unless($needed_for_something) {
             my $circ_lib = (ref $self->copy->circ_lib) ? 
                     $self->copy->circ_lib->id : $self->copy->circ_lib;
     
@@ -2057,10 +2111,10 @@
                 return if $self->bail_out;
                 $self->push_events(OpenILS::Event->new('ROUTE_ITEM', org => $circ_lib));
             }
+            $self->reshelve_copy;
         }
     }
 
-    $self->reshelve_copy;
     return if $self->bail_out;
 
     unless($self->checkin_changed) {
@@ -2201,6 +2255,42 @@
 }
 
 
+sub hold_capture_is_possible {
+    my $self = shift;
+    my $copy = $self->copy;
+
+    # we've been explicitly told not to capture any holds
+    return 0 if $self->capture eq 'nocapture';
+
+    # See if this copy can fulfill any holds
+    my $hold = $holdcode->find_nearest_permitted_hold(
+        $self->editor, $copy, $self->editor->requestor, 1 # check_only
+    );
+    return undef if ref $hold eq "HASH" and
+        $hold->{"textcode"} eq "ACTION_HOLD_REQUEST_NOT_FOUND";
+    return $hold;
+}
+
+sub reservation_capture_is_possible {
+    my $self = shift;
+    my $copy = $self->copy;
+
+    # we've been explicitly told not to capture any holds
+    return 0 if $self->capture eq 'nocapture';
+
+    my $booking_ses = OpenSRF::AppSession->connect("open-ils.booking");
+    my $resv = $booking_ses->request(
+        "open-ils.booking.reservations.could_capture",
+        $self->editor->authtoken, $copy->barcode
+    )->gather(1);
+    $booking_ses->disconnect;
+    if (ref($resv) eq "HASH" and exists $resv->{"textcode"}) {
+        $self->push_events($resv);
+    } else {
+        return $resv;
+    }
+}
+
 # returns true if the item was used (or may potentially be used 
 # in subsequent calls) to capture a hold.
 sub attempt_checkin_hold_capture {
@@ -2279,6 +2369,65 @@
     return 1;
 }
 
+sub attempt_checkin_reservation_capture {
+    my $self = shift;
+    my $copy = $self->copy;
+
+    # we've been explicitly told not to capture any holds
+    return 0 if $self->capture eq 'nocapture';
+
+    my $booking_ses = OpenSRF::AppSession->connect("open-ils.booking");
+    my $evt = $booking_ses->request(
+        "open-ils.booking.resources.capture_for_reservation",
+        $self->editor->authtoken,
+        $copy->barcode,
+        1 # don't update copy - we probably have it locked
+    )->gather(1);
+    $booking_ses->disconect;
+
+    if (ref($evt) ne "HASH" or not exists $evt->{"textcode"}) {
+        $logger->warn(
+            "open-ils.booking.resources.capture_for_reservation " .
+            "didn't return an event!"
+        );
+    } else {
+        if (
+            $evt->{"textcode"} eq "RESERVATION_NOT_FOUND" and
+            $evt->{"payload"}->{"fail_cause"} eq "not-transferable"
+        ) {
+            # not-transferable is an error event we'll pass on the user
+            $logger->warn("reservation capture attempted against non-transferable item");
+            $self->push_events($evt);
+            return 0;
+        } elsif ($evt->{"textcode"} eq "SUCCESS") {
+            # Re-retrieve copy as reservation capture may have changed
+            # its status and whatnot.
+            my $capmsg = "booking capture win on copy " . $self->copy->id;
+            if (my $new_copy_status = $evt->{"payload"}->{"new_copy_status"}) {
+                $capmsg .= sprintf "; changing status from %s to %d",
+                    $self->copy->status, $new_copy_status;
+                $self->copy->status($new_copy_status);
+                $self->update_copy;
+            }
+            $logger->info("circulator: $capmsg");
+            $self->reservation($evt->{"payload"}->{"reservation"});
+
+            if (exists $evt->{"payload"}->{"transit"}) {
+                $self->push_events(
+                    new OpenILS::Event(
+                        "ROUTE_ITEM",
+                        "org" => $evt->{"payload"}->{"transit"}->dest
+                    )
+                );
+            }
+            $self->checkin_changed(1);
+            return 1;
+        }
+    }
+    # other results are treated as "nothing to capture"
+    return 0;
+}
+
 sub do_hold_notify {
     my( $self, $holdid ) = @_;
 
@@ -2688,6 +2837,10 @@
         # valid hold after processing a cancelled hold
         $payload->{hold} = $self->hold unless (not $self->hold or $self->hold->cancel_time);
 
+        # $self->reservation may or may not have been replaced with a 
+        # valid reservation after processing a cancelled reservation
+        $payload->{reservation} = $self->reservation unless (not $self->reservation or $self->reservation->cancel_time);
+
         $evt->{payload} = $payload;
     }
 }

Modified: branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
===================================================================
--- branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm	2010-03-17 19:10:20 UTC (rev 15878)
@@ -701,7 +701,7 @@
 	for my $c (@circs) {
 
         my $ctype = ref($c);
-        $ctype =~ s/^.*([^:]+)$/$1/o;
+        $ctype =~ s/^.+::(\w+)$/$1/;
 	
         my $due_date_method = 'due_date';
         my $target_copy_method = 'target_copy';
@@ -723,9 +723,12 @@
 				$self->method_lookup('open-ils.storage.transaction.rollback')->run;
 			}
 			$self->method_lookup('open-ils.storage.transaction.begin')->run( $client );
-			$log->info("Processing circ ".$c->id."...\n");
+			$log->info(
+				sprintf("Processing %s %d...",
+					($is_reservation ? "reservation" : "circ"), $c->id
+				)
+			);
 
-
 			my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->$due_date_method ) );
 	
 			my $due = $due_dt->epoch;
@@ -1334,6 +1337,7 @@
 
 
             my @good_resources = ();
+            my %conflicts = ();
             for my $res (@$all_resources) {
                 unless (isTrue($res->type->catalog_item)) {
                     push @good_resources, $res;
@@ -1353,16 +1357,19 @@
                 }
 
                 if ($copy->status->id == 1) {
-                    my $circs = action::circulation->search_where(
+                    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);
+                        if (DateTime->compare($start_time, $due_date) < 0) {
+                            $conflicts{$res->id} = $circs->[0]->to_fieldmapper;
+                            next;
+                        }
                         push @good_resources, $res;
                     }
 
@@ -1378,7 +1385,7 @@
 			# 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' };
+				push @successes, { reservation => $bresv->id, eligible_copies => 0, error => 'NO_COPIES', conflicts => \%conflicts };
 
 				$self->method_lookup('open-ils.storage.transaction.commit')->run;
 				die "OK\n";

Modified: branches/rel_1_6/Open-ILS/web/css/skin/default/booking.css
===================================================================
--- branches/rel_1_6/Open-ILS/web/css/skin/default/booking.css	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/css/skin/default/booking.css	2010-03-17 19:10:20 UTC (rev 15878)
@@ -87,3 +87,21 @@
 div#no_ready_bresv, div#no_out_bresv, div#no_in_bresv {
     font-style: italic;
 }
+.tundra .dojoxGridRowSelected {
+    background-color: #006699 !important;
+    color: #ffffff;
+}
+.pull_list_resv_detail {
+    border-bottom: 1px dashed #999999;
+    margin-bottom: 4px;
+}
+.pull_list_will_transit { font-weight: bold; color: #ff6666; }
+iframe#printing_iframe {
+    border-top: 1px dashed #333333;
+    border-left: 0;
+    border-right: 0;
+    border-bottom: 0;
+}
+button.print_slip {
+    margin-left: 24px;
+}

Modified: branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/capture.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/capture.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/capture.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -12,10 +12,23 @@
     'CAPTURE_BRESV_DATES': "Reservation time:",
     'CAPTURE_BRESV_BRSRC': "Resource barcode:",
     'CAPTURE_BRESV_PICKUP_LIB': "Pickup library:",
-    'CAPTURE_BRESV_PATRON_BARCODE': "Patron barcode:",
     'CAPTURE_CAUSES_TRANSIT': "This item is now in transit!",
     'CAPTURE_TRANSIT_SOURCE': "From:",
     'CAPTURE_TRANSIT_DEST': "To:",
+    'BARCODE': "Barcode",
+    'TITLE': "Title",
+    'AUTHOR': "Author",
+    'RESERVED': "Reserved for patron",
+    'REQUEST': "Request time",
+    'DURATION': "Reserved from",
+    'SLIP_DATE': "Slip date",
+    'PRINTED_BY': "Printed by",
+    'AT': "at",
+    'PRINT': "<u>P</u>rint",
+    'PRINT_ACCESSKEY': "P",
+    'TRANSIT': "*** TRANSIT ***",
+    'RESERVATION_SHELF': "RESERVATION SHELF",
+    'NEEDS_ROUTED_TO': "This item need to be routed to",
 
     'AUTO_capture_heading': "Capture Reserved Resources",
     'AUTO_resource_barcode': "Enter barcode:",

Modified: branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pickup_and_return.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pickup_and_return.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pickup_and_return.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -34,5 +34,6 @@
     'AUTO_ATTR_VALUE_go': "Go",
     'AUTO_ATTR_VALUE_reset': "Clear / New Patron",
     'AUTO_ATTR_VALUE_pickup': "Pick up",
-    'AUTO_ATTR_VALUE_return': "Return"
+    'AUTO_ATTR_VALUE_return': "Return",
+    'ADDRESS': "${0}\n${1}\n${2}, ${3} ${4}"
 }

Modified: branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pull_list.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pull_list.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/pull_list.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -4,8 +4,10 @@
     'COPY_LOOKUP_NO_RESPONSE': "No response looking up copies by barcode",
     'COPY_LOOKUP_ERROR': "Error looking up copies by barcode: ",
     'COPY_MISSING': "Unexpected error: No information for copy: ",
+    'AT': "at",
+    'FOR': "for",
 
-    'AUTO_no_results': "No results",
+    'AUTO_no_results': "No results.",
     'AUTO_owning_lib_selector': "See pull list for library:",
     'AUTO_pull_list_title': "Booking Pull List",
     'AUTO_interval_in_days': "Generate list for this many days hence: ",
@@ -14,7 +16,7 @@
     'AUTO_th_barcode': "Barcode",
     'AUTO_th_call_number': "Call number",
     'AUTO_th_copy_location': "Copy location",
-    'AUTO_th_copy_number': "Copy number",
+    'AUTO_th_pickup_lib': "Pickup library",
     'AUTO_th_resv_details': "Reservation details",
-    'AUTO_ATTR_VALUE_print': "Print",
+    'AUTO_ATTR_VALUE_print': "Print"
 }

Modified: branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/reservation.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/reservation.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/dojo/openils/booking/nls/reservation.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -2,7 +2,12 @@
     'NO_BRT_RESULTS': "There are no bookable resource types registered.",
     'NO_TARG_DIV': "Could not find target div",
     'NO_BRA_RESULTS': "Couldn't retrieve booking resource attributes.",
-    'SELECT_A_BRSRC_THEN': "Select a resource from the big list above.",
+    'SELECT_A_BRSRC_THEN':
+        "You have clicked 'Reserve Selected', but nothing is selected!\n\n" +
+        "You must select a resource from the large box above.\n\n" +
+        "***  If resources that you would select are highlighted in RED, ***\n"+
+        "these items are not available during the requested time; if\n" +
+        "possible, choose another resource or change the reservation time.",
     'CREATE_BRESV_LOCAL_ERROR': "Exception trying to create reservation: ",
     'CREATE_BRESV_SERVER_ERROR': "Server error trying to create reservation: ",
     'CREATE_BRESV_SERVER_NO_RESPONSE':
@@ -10,11 +15,27 @@
     /* FIXME: Users aren't likely to be able to do anything with the following
      * message.  Figure out a way to do something more helpful.
      */
-    'CREATE_BRESV_OK_MISSING_TARGET': function(n, m) {
-        return "Created " + n + " reservation(s), but " + m + " of these " +
-            "couldn't target any resources.\n\n" +
+    'CREATE_BRESV_OK_MISSING_TARGET': function(n, m, d, c) {
+        var s = "Created " + n + " reservation(s), but " + m + " of these " +
+            "could not target any resources.\n\n";
+        var t;
+        if (d && d.length) {
+            var gram = d.length == 1 ?
+                ["A circulation blocks", ""] : ["Circulations block", "s"];
+            t = s + gram[0] + " the desired reservation(s), " +
+                "with due date" + gram[1] + ":\n";
+
+            for (var k in d) { t += "\t" + d[k] + "\n"; }
+        } else {
+            t = s +
             "This means that it won't be possible to fulfill some of these\n" +
             "reservations until a suitable resource becomes available.";
+        }
+        if (c) {
+            t +="\n\nSince the requested resource could not be targeted, " +
+                "this\nreservation will now be canceled.";
+        }
+        return t;
     },
     'CREATE_BRESV_OK': function(n) {
         return "Created " + n + " reservation" + (n == 1 ? "" : "s") + ".";
@@ -53,6 +74,10 @@
     'ON_FLY_ERROR':
         "Error attempting to make item a bookable resource:",
     'ANY': "ANY",
+    'ERROR_FETCHING_AOUS':
+        "Could not retrieve organizational unit settings.\n" +
+        "This is a non-fatal error, but you may wish to\n" +
+        "contact your system administrator.",
 
     'AUTO_choose_a_brt': "Choose a Bookable Resource Type",
     'AUTO_i_need_this_resource': "I need this resource...",
@@ -64,7 +89,7 @@
     'AUTO_ATTR_VALUE_reserve_brsrc': "Reserve Selected",
     'AUTO_ATTR_VALUE_reserve_brt': "Reserve Any",
     'AUTO_ATTR_VALUE_button_edit_existing': "Edit selected",
-    'AUTO_ATTR_VALUE_button_cancel_existing': "Cancel selcted",
+    'AUTO_ATTR_VALUE_button_cancel_existing': "Cancel selected",
     'AUTO_bresv_grid_type': "Type",
     'AUTO_bresv_grid_resource': "Resource",
     'AUTO_bresv_grid_start_time': "Start time",

Modified: branches/rel_1_6/Open-ILS/web/js/ui/default/booking/capture.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/ui/default/booking/capture.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/ui/default/booking/capture.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -8,14 +8,19 @@
 
 var localeStrings = dojo.i18n.getLocalization("openils.booking", "capture");
 
-function CaptureDisplay(element) { this.element = element; }
+function CaptureDisplay(control_holder, data_holder) {
+    this.control_holder = control_holder;
+    this.data_holder = data_holder;
+}
 CaptureDisplay.prototype.no_payload = function() {
-    this.element.appendChild(document.createTextNode(localeStrings.NO_PAYLOAD));
+    this.data_holder.appendChild(
+        document.createTextNode(localeStrings.NO_PAYLOAD)
+    );
 };
 CaptureDisplay.prototype.dump = function(payload) {
     var div = document.createElement("div");
     div.appendChild(document.createTextNode(localeStrings.HERES_WHAT_WE_KNOW));
-    this.element.appendChild(div);
+    this.data_holder.appendChild(div);
 
     var ul = document.createElement("ul");
     for (var k in payload) {
@@ -23,90 +28,149 @@
         li.appendChild(document.createTextNode(k + ": " + payload[k]));
         ul.appendChild(li);
     }
-    this.element.appendChild(ul);
+    this.data_holder.appendChild(ul);
 };
-CaptureDisplay.prototype.generate_transit_display = function(payload) {
-    var super_div = document.createElement("div");
-    var div;
-
-    div = document.createElement("div");
+CaptureDisplay.prototype._generate_barcode_line = function(payload) {
+    var div = document.createElement("div");
     div.appendChild(document.createTextNode(
-        localeStrings.CAPTURE_CAUSES_TRANSIT
+        localeStrings.BARCODE + ": " + payload.resource.barcode()
     ));
-    div.setAttribute("class", "transit_notice");
-    super_div.appendChild(div);
-
-    div = document.createElement("div");
+    return div;
+};
+CaptureDisplay.prototype._generate_title_line = function(payload) {
+    var div = document.createElement("div");
     div.appendChild(document.createTextNode(
-        localeStrings.CAPTURE_TRANSIT_SOURCE + " " +
-        fieldmapper.aou.findOrgUnit(payload.transit.source()).shortname()
+        localeStrings.TITLE + ": " +
+        (payload.mvr ? payload.mvr.title() : payload.type.name())
     ));
-    super_div.appendChild(div);
-
-    div = document.createElement("div");
-    div.appendChild(document.createTextNode(
-        localeStrings.CAPTURE_TRANSIT_DEST + " " +
-        fieldmapper.aou.findOrgUnit(payload.transit.dest()).shortname()
-    ));
-    super_div.appendChild(div);
-
-    return super_div;
+    return div;
 };
-CaptureDisplay.prototype.display_with_transit_info = function(payload) {
-    var div;
-
-    div = document.createElement("div");
-    div.appendChild(document.createTextNode(localeStrings.CAPTURE_INFO));
-    div.setAttribute("class", "capture_info");
-    this.element.appendChild(div);
-
-    if (payload.catalog_item) {
-        div = document.createElement("div");
+CaptureDisplay.prototype._generate_author_line = function(payload) {
+    var div = document.createElement("div");
+    if (payload.mvr) {
         div.appendChild(document.createTextNode(
-            localeStrings.CAPTURE_BRESV_BRSRC + " " +
-            payload.catalog_item.barcode()
+            localeStrings.AUTHOR + ": " + payload.mvr.author()
         ));
-        this.element.appendChild(div);
     }
-
-    div = document.createElement("div");
-    div.appendChild(document.createTextNode(
-        localeStrings.CAPTURE_BRESV_DATES + " " +
-        humanize_timestamp_string(payload.reservation.start_time()) + " - " +
-        humanize_timestamp_string(payload.reservation.end_time())
+    return div;
+};
+CaptureDisplay.prototype._generate_transit_notice = function(payload) {
+    var div = document.createElement("div");
+    if (payload.transit) {
+        div.setAttribute("class", "transit_notice");
+        div.appendChild(document.createTextNode(localeStrings.TRANSIT));
+    }
+    return div;
+};
+CaptureDisplay.prototype._generate_route_line = function(payload) {
+    var div = document.createElement("div");
+    var strong = document.createElement("strong");
+    strong.appendChild(document.createTextNode(
+        (payload.transit ?
+            fieldmapper.aou.findOrgUnit(payload.transit.dest()).shortname() :
+            localeStrings.RESERVATION_SHELF) + ":"
     ));
-    this.element.appendChild(div);
-
-    div = document.createElement("div");
     div.appendChild(document.createTextNode(
-        localeStrings.CAPTURE_BRESV_PICKUP_LIB + " " +
-        fieldmapper.aou.findOrgUnit(
-            payload.reservation.pickup_lib()
-        ).shortname()
+        localeStrings.NEEDS_ROUTED_TO + " "
     ));
-    this.element.appendChild(div);
+    div.appendChild(strong);
+    return div;
+};
+CaptureDisplay.prototype._generate_patron_info = function(payload) {
+    var p = document.createElement("p");
+    p.innerHTML = "<strong>" + localeStrings.RESERVED + "</strong> " +
+        formal_name(payload.reservation.usr()) + "<br />" +
+        localeStrings.BARCODE + ": " +
+        payload.reservation.usr().card().barcode();
+    return p;
+};
+CaptureDisplay.prototype._generate_resv_info = function(payload) {
+    var p = document.createElement("p");
+    p.innerHTML = localeStrings.REQUEST + ": " +
+        humanize_timestamp_string(payload.reservation.request_time()) +
+        "<br />" + 
+        localeStrings.DURATION + ": " +
+        humanize_timestamp_string(payload.reservation.start_time()) +
+        " - " + 
+        humanize_timestamp_string(payload.reservation.end_time());
+    return p;
+};
+CaptureDisplay.prototype._generate_meta_info = function(result) {
+    var p = document.createElement("p");
+    p.innerHTML = localeStrings.SLIP_DATE + ": " + result.servertime +
+        "<br />" + localeStrings.PRINTED_BY + " " +
+        formal_name(openils.User.user) + " " + localeStrings.AT + " " +
+        fieldmapper.aou.findOrgUnit(openils.User.user.ws_ou()).shortname()
+    return p;
+};
+CaptureDisplay.prototype.display_with_transit_info = function(result) {
+    var div = document.createElement("div");
+    var span = document.createElement("span");
+    span.appendChild(document.createTextNode(localeStrings.CAPTURE_INFO));
+    span.setAttribute("class", "capture_info");
+    this.control_holder.appendChild(span);
 
-    div = document.createElement("div");
-    div.appendChild(document.createTextNode(
-        localeStrings.CAPTURE_BRESV_PATRON_BARCODE + " " +
-        payload.reservation.usr().card().barcode()
-    ));
-    this.element.appendChild(div);
+    var button = document.createElement("button");
+    button.setAttribute("class", "print_slip");
+    button.setAttribute("type", "button");
+    button.setAttribute("accesskey", localeStrings.PRINT_ACCESSKEY);
+    button.innerHTML = localeStrings.PRINT;
+    button.onclick = function() {
+        try { dojo.byId("printing_iframe").contentWindow.print(); }
+        catch (E) { alert(E); } /* XXX */
+        return false;
+    };
+    this.control_holder.appendChild(button);
 
-    if (payload.transit) {
-        this.element.appendChild(this.generate_transit_display(payload));
-    }
+    div.appendChild(this._generate_transit_notice(result.payload));
+
+    var p = document.createElement("p");
+    p.appendChild(this._generate_route_line(result.payload));
+    p.appendChild(this._generate_barcode_line(result.payload));
+    p.appendChild(this._generate_title_line(result.payload));
+    p.appendChild(this._generate_author_line(result.payload));
+    div.appendChild(p);
+
+    div.appendChild(this._generate_patron_info(result.payload));
+    div.appendChild(this._generate_resv_info(result.payload));
+    div.appendChild(this._generate_meta_info(result));
+
+    this._create_iframe(div);
 };
-CaptureDisplay.prototype.clear = function() { this.element.innerHTML = ""; };
-CaptureDisplay.prototype.load = function(payload) {
+CaptureDisplay.prototype._create_iframe = function(contents) {
+    var iframe = document.createElement("iframe");
+    iframe.setAttribute("name", "printing_iframe");
+    iframe.setAttribute("id", "printing_iframe");
+    iframe.setAttribute("src", "");
+    iframe.setAttribute("width", "100%");
+    iframe.setAttribute("height", "400"); /* hardcode 400px? really? */
+
+    this.data_holder.appendChild(iframe);
+
+    var w = dojo.byId("printing_iframe").contentWindow;
+    w.document.open();
+    w.document.write(
+        "<html><head><link rel='stylesheet' type='text/css' href='" +
+        dojo.byId("booking_stylesheet_link").href +
+        "' /><body></body></html>"
+    );
+    w.document.close();
+    w.document.body.appendChild(contents);
+    /* FIXME if (determine_autoprint_setting_somehow()) w.print(); */
+};
+CaptureDisplay.prototype.clear = function() {
+    this.control_holder.innerHTML = "";
+    this.data_holder.innerHTML = "";
+};
+CaptureDisplay.prototype.load = function(result) {
     try {
-        this.element.appendChild(document.createElement("hr"));
-        if (!payload) {
+        this.control_holder.appendChild(document.createElement("hr"));
+        if (!result.payload) {
             this.no_payload();
-        } else if (!payload.fail_cause && payload.captured) {
-            this.display_with_transit_info(payload);
+        } else if (!result.payload.fail_cause && result.payload.captured) {
+            this.display_with_transit_info(result);
         } else {
-            this.dump(payload);
+            this.dump(result.payload);
         }
     } catch (E) {
         alert(E); /* XXX */
@@ -136,10 +200,10 @@
 
     if (result && result.ilsevent !== undefined) {
         if (result.payload && result.payload.captured > 0) {
-            capture_display.load(result.payload);
+            capture_display.load(result);
             return CAPTURE_SUCCESS;
         } else {
-            capture_display.load(result.payload);
+            capture_display.load(result);
             alert(my_ils_error(localeStrings.CAPTURED_NOTHING, result));
             return CAPTURE_FAILURE;
         }
@@ -167,8 +231,9 @@
 }
 
 function my_init() {
-    init_auto_l10n(document.getElementById("auto_l10n_start_here"));
+    init_auto_l10n(dojo.byId("auto_l10n_start_here"));
     capture_display = new CaptureDisplay(
-        document.getElementById("capture_display")
+        dojo.byId("capture_info_top"),
+        dojo.byId("capture_info_bottom")
     );
 }

Modified: branches/rel_1_6/Open-ILS/web/js/ui/default/booking/common.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/ui/default/booking/common.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/ui/default/booking/common.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -48,6 +48,13 @@
     var timeparts = parts[1].split("-")[0].split(":");
     return parts[0] + " " + timeparts[0] + ":" + timeparts[1];
 }
+function humanize_timestamp_string2(ts) {
+    /* For now, this discards time zones, too. */
+    var parts = ts.split(" ");
+    parts[1] = parts[1].replace(/[\-\+]\d+$/, "");
+    var timeparts = parts[1].split("-")[0].split(":");
+    return parts[0] + " " + timeparts[0] + ":" + timeparts[1];
+}
 function is_ils_event(e) { return (e.ilsevent != undefined); }
 function is_ils_actor_card_error(e) {
     return (e.textcode == "ACTOR_CARD_NOT_FOUND");

Modified: branches/rel_1_6/Open-ILS/web/js/ui/default/booking/populator.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/ui/default/booking/populator.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/ui/default/booking/populator.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -2,6 +2,8 @@
  * localization (Dojo/nls) for pickup and return . */
 
 dojo.require("dojo.data.ItemFileReadStore");
+dojo.require("dojo.date.locale");
+dojo.require("openils.PermaCrud");
 
 function Populator(widgets, primary_input) {
     this.widgets = widgets;
@@ -137,7 +139,7 @@
     var r = fieldmapper.standardRequest(
         ["open-ils.booking",
         "open-ils.booking.reservations.by_returnable_resource_barcode"],
-        [xulG.auth.session.key, barcode]
+        [openils.User.authtoken, barcode]
     );
     if (!r || r.length < 1) {
         alert(localeStrings.NO_SUCH_RETURNABLE_RESOURCE);
@@ -159,7 +161,10 @@
         if (!ret) {
             alert(localeStrings.RETURN_NO_RESPONSE);
         } else if (is_ils_event(ret) && ret.textcode != "SUCCESS") {
-            alert(my_ils_error(localeStrings.RETURN_ERROR, ret));
+            if (ret.textcode == "ROUTE_ITEM")
+                display_transit_slip(ret);
+            else
+                alert(my_ils_error(localeStrings.RETURN_ERROR, ret));
         } else {
             /* XXX speedbump should go, but something has to happen else
              * there's no indication to staff that anything happened when
@@ -189,7 +194,7 @@
 
     var result = fieldmapper.standardRequest(
         ["open-ils.booking", "open-ils.booking.reservations.get_captured"],
-        [xulG.auth.session.key, this.patron_barcode, which]
+        [openils.User.authtoken, this.patron_barcode, which]
     );
 
     if (!result) {
@@ -220,7 +225,7 @@
 Populator.prototype.pickup = function(reservation) {
     return fieldmapper.standardRequest(
         ["open-ils.circ", "open-ils.circ.reservation.pickup"],
-        [xulG.auth.session.key, {
+        [openils.User.authtoken, {
             "patron_barcode": this.patron_barcode,
             "reservation": reservation
         }]
@@ -229,7 +234,7 @@
 Populator.prototype.return = function(reservation) {
     return fieldmapper.standardRequest(
         ["open-ils.circ", "open-ils.circ.reservation.return"],
-        [xulG.auth.session.key, {
+        [openils.User.authtoken, {
             "patron_barcode": this.patron_barcode,
             "reservation": reservation.id()
             /* yeah just id here ------^; lack of parallelism */
@@ -258,7 +263,10 @@
         if (!result) {
             alert(no_response_msg);
         } else if (is_ils_event(result) && result.textcode != "SUCCESS") {
-            alert(my_ils_error(error_msg, result));
+            if (result.textcode == "ROUTE_ITEM")
+                display_transit_slip(result);
+            else
+                alert(my_ils_error(error_msg, result));
         } else {
             continue;
         }
@@ -281,3 +289,50 @@
         this.primary_input.focus();
     }
 };
+
+/* XXX needs to be combined with the code that shows transit slips in the
+ * booking capture interface. */
+function display_transit_slip(e) {
+    var ou = fieldmapper.aou.findOrgUnit(e.org, /* slim_ok */false);
+    var ma = (new openils.PermaCrud()).retrieve("aoa", ou.mailing_address());
+    var mas = ma ?
+        dojo.string.substitute(
+            localeStrings.ADDRESS,
+            [ma.street1(),ma.street2(),ma.city(),ma.state(),ma.post_code()].map(
+                function(o) { return o ? o : ""; }
+            )
+        ).replace("\n\n", "\n").replace("\n", "<br />") : "[Unknown address]";
+    /* XXX i18n and/or template */
+    try {
+        var win = window.open(
+            "","","resizeable,width=600,height=400,scrollbars=1"
+        );
+        win.document.body.innerHTML =
+            "<h1>Transit Slip</h1>\n" +
+            //"<img src='/xul/server/skin/media/images/turtle.gif' />\n" +
+            "<p>Destination: <strong>" + ou.name() + "</strong></p>\n" +
+            "<p>" + mas + "</p>\n" +
+            "<p>Barcode: " + e.payload.copy.barcode() + "<br />\n" +
+            "Title: <span id='title'></span><br />\n" +
+            "Author: <span id='author'></span><br />\n" +
+            "Slip Date: " +
+                dojo.date.locale.format(new Date(), {"formatLength": "short"}) +
+            "</p>";
+        fieldmapper.standardRequest(
+            ["open-ils.search", "open-ils.search.biblio.mods_from_copy"], {
+                "params": [e.payload.copy.id()],
+                "async": true,
+                "onresponse": function(r) {
+                    var mvr = openils.Util.readResponse(r);
+                    dojo.byId("title", win.document).innerHTML = mvr.title();
+                    dojo.byId("author", win.document).innerHTML = mvr.author();
+                },
+                "oncomplete": function() {
+                    win[confirm("Print transit slip?") ? "print" : "close"]();
+                }
+            }
+        );
+    } catch (E) {
+        alert("exception rendering transit slip: " + E); // XXX
+    }
+}

Modified: branches/rel_1_6/Open-ILS/web/js/ui/default/booking/pull_list.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/ui/default/booking/pull_list.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/ui/default/booking/pull_list.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -47,18 +47,33 @@
         return td;
     }
 
+    function render_pickup_lib(pickup_lib) {
+        var span = document.createElement("span");
+        if (pickup_lib != owning_lib_selected)
+            span.setAttribute("class", "pull_list_will_transit");
+        span.innerHTML = localeStrings.AT + " " +
+            fieldmapper.aou.findOrgUnit(pickup_lib).shortname();
+        return span;
+    }
+
     function reservation_info_cell(one) {
         var td = document.createElement("td");
         for (var i in one.reservations) {
             var one_resv = one.reservations[i];
             var div = document.createElement("div");
-            var s = humanize_timestamp_string(one_resv.start_time()) + " - " +
-                humanize_timestamp_string(one_resv.end_time()) + " " +
-                formal_name(one_resv.usr());
-            /* FIXME: The above need patron barcode instead of name, but
-             * that requires a fix in the middle layer to flesh on the
-             * right stuff. */
-            div.appendChild(document.createTextNode(s));
+            div.setAttribute("class", "pull_list_resv_detail");
+            var content = [
+                document.createTextNode(
+                    humanize_timestamp_string(one_resv.start_time()) +
+                    " - " + humanize_timestamp_string(one_resv.end_time())
+                ),
+                document.createElement("br"),
+                render_pickup_lib(one_resv.pickup_lib()),
+                document.createTextNode(
+                    " " + localeStrings.FOR + " " + formal_name(one_resv.usr())
+                )
+            ];
+            for (var k in content) { div.appendChild(content[k]); }
             td.appendChild(div);
         }
         return td;
@@ -71,7 +86,6 @@
     cells.push(cell(undefined, one.current_resource.barcode()));
     cells.push(cell(baseid + "_call_number", "-"));
     cells.push(cell(baseid + "_copy_location", "-"));
-    cells.push(cell(baseid + "_copy_number", "-"));
     cells.push(reservation_info_cell(one));
 
     var row = document.createElement("tr");
@@ -120,6 +134,7 @@
             return results;
         }
     }
+    return null;
 }
 
 function fill_in_pull_list_details(list, acp_cache) {
@@ -133,10 +148,6 @@
             var copy_location_el = document.getElementById(
                 dom_table_rowid(one.current_resource.id()) + "_copy_location"
             );
-            var copy_number_el = document.getElementById(
-                dom_table_rowid(one.current_resource.id()) + "_copy_number"
-            );
-
             var bc = one.current_resource.barcode();
 
             if (acp_cache[bc]) {
@@ -148,10 +159,6 @@
                     var value = acp_cache[bc].location().name();
                     if (value) copy_location_el.innerHTML = value;
                 }
-                if (copy_number_el) {
-                    var value = acp_cache[bc].copy_number();
-                    if (value) copy_number_el.innerHTML = value;
-                }
             } else {
                 alert(localeStrings.COPY_MISSING + bc);
             }

Modified: branches/rel_1_6/Open-ILS/web/js/ui/default/booking/reservation.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/js/ui/default/booking/reservation.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/js/ui/default/booking/reservation.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -3,6 +3,7 @@
  */
 dojo.require("fieldmapper.OrgUtils");
 dojo.require("openils.PermaCrud");
+dojo.require("openils.User");
 dojo.require("openils.widget.OrgUnitFilteringSelect");
 dojo.require("dojo.data.ItemFileReadStore");
 dojo.require("dijit.form.DateTextBox");
@@ -21,6 +22,7 @@
 var brsrc_index = {};
 var bresv_index = {};
 var just_reserved_now = {};
+var aous_cache = {};
 
 function AttrValueTable() { this.t = {}; }
 AttrValueTable.prototype.set = function(attr, value) { this.t[attr] = value; };
@@ -262,14 +264,25 @@
 
 function check_bresv_targeting(results) {
     var missing = 0;
+    var due_dates = [];
     for (var i in results) {
-        if (!(results[i].targeting && results[i].targeting.current_resource)) {
+        var targ = results[i].targeting;
+        if (!(targ && targ.current_resource)) {
             missing++;
+            if (targ) {
+                if (targ.error == "NO_COPIES" && targ.conflicts) {
+                    for (var k in targ.conflicts) {
+                        /* Could potentially get more circ information from
+                         * targ.conflicts for display in the future. */
+                        due_dates.push(humanize_timestamp_string2(targ.conflicts[k].due_date()));
+                    }
+                }
+            }
         } else {
             just_reserved_now[results[i].targeting.current_resource] = true;
         }
     }
-    return missing;
+    return {"missing": missing, "due_dates": due_dates};
 }
 
 function create_bresv(resource_list) {
@@ -308,13 +321,27 @@
                 ));
             }
         } else {
-            var missing;
-            alert((missing = check_bresv_targeting(results)) ?
-                localeStrings.CREATE_BRESV_OK_MISSING_TARGET(
-                    results.length, missing
-                ) :
-                localeStrings.CREATE_BRESV_OK(results.length)
-            );
+            var targeting = check_bresv_targeting(results);
+            if (targeting.missing) {
+                if (aous_cache["booking.require_successful_targeting"]) {
+                    alert(localeStrings.CREATE_BRESV_OK_MISSING_TARGET(
+                        results.length, targeting.missing, targeting.due_dates,
+                        true /* will cancel */
+                    ));
+                    cancel_reservations(
+                        results.map(
+                            function(o) { return o.bresv; },
+                            true /* skip_update */
+                        )
+                    );
+                } else {
+                    alert(localeStrings.CREATE_BRESV_OK_MISSING_TARGET(
+                        results.length, targeting.missing, targeting.due_dates
+                    ));
+                }
+            } else {
+                alert(localeStrings.CREATE_BRESV_OK(results.length));
+            }
             update_brsrc_list();
             update_bresv_grid();
         }
@@ -332,7 +359,7 @@
                 "id": o.id(),
                 "type": o.target_resource_type().name(),
                 "start_time": humanize_timestamp_string(o.start_time()),
-                "end_time": humanize_timestamp_string(o.end_time()),
+                "end_time": humanize_timestamp_string(o.end_time())
             };
 
             if (o.current_resource())
@@ -432,7 +459,7 @@
     }
 }
 
-function cancel_reservations(bresv_id_list) {
+function cancel_reservations(bresv_id_list, skip_update) {
     try {
         var result = fieldmapper.standardRequest(
             ["open-ils.booking", "open-ils.booking.reservations.cancel"],
@@ -442,7 +469,7 @@
         alert(localeStrings.CXL_BRESV_FAILURE2 + E);
         return;
     }
-    setTimeout(update_bresv_grid, 0);
+    if (!skip_update) setTimeout(update_bresv_grid, 0);
     if (!result) {
         alert(localeStrings.CXL_BRESV_FAILURE);
     } else if (is_ils_event(result)) {
@@ -687,7 +714,7 @@
                     timePattern: "HH:mm",
                     clickableIncrement: "T00:15:00",
                     visibleIncrement: "T00:15:00",
-                    visibleRange: "T01:30:00",
+                    visibleRange: "T01:30:00"
                 },
                 onChange: function() {
                     reserve_timestamp_range.update_from_widget(this);
@@ -718,7 +745,7 @@
         /* After some delay to allow the cancellations a chance to get
          * committed, refresh the brsrc list as it might reflect newly
          * available resources now. */
-        setTimeout(update_brsrc_list, 2000);
+        if (our_brt) setTimeout(update_brsrc_list, 2000);
     } else {
         alert(localeStrings.CXL_BRESV_SELECT_SOMETHING);
     }
@@ -752,6 +779,24 @@
     return true;
 }
 
+function init_aous_cache() {
+    /* The following method call could be given a longer
+     * list of OU settings to fetch in the future if needed. */
+    var results = fieldmapper.aou.fetchOrgSettingBatch(
+        openils.User.user.ws_ou(), ["booking.require_successful_targeting"]
+    );
+    if (results && !is_ils_event(results)) {
+        for (var k in results) {
+            if (results[k] != undefined)
+                aous_cache[k] = results[k].value;
+        }
+    } else if (results) {
+        alert(my_ils_error(localeStrings.ERROR_FETCHING_AOUS, results));
+    } else {
+        alert(localeStrings.ERROR_FETCHING_AOUS);
+    }
+}
+
 /*
  * my_init
  */
@@ -760,6 +805,7 @@
     reveal_dom_element(document.getElementById("brt_search_block"));
     hide_dom_element(document.getElementById("reserve_under"));
     init_auto_l10n(document.getElementById("auto_l10n_start_here"));
+    init_aous_cache();
     init_timestamp_widgets();
 
     if (!(opts = xulG.bresv_interface_opts)) opts = {};

Modified: branches/rel_1_6/Open-ILS/web/opac/locale/en-US/lang.dtd
===================================================================
--- branches/rel_1_6/Open-ILS/web/opac/locale/en-US/lang.dtd	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/opac/locale/en-US/lang.dtd	2010-03-17 19:10:20 UTC (rev 15878)
@@ -732,7 +732,8 @@
 
 <!ENTITY staff.main.menu.booking.label "Booking">
 <!ENTITY staff.main.menu.booking.accesskey "B">
-<!ENTITY staff.main.menu.booking.reservation.label "Create or Edit Reservations">
+<!ENTITY staff.main.menu.booking.reservation.label_alt "Create or Cancel Reservations">
+<!ENTITY staff.main.menu.booking.reservation.label "Create Reservations">
 <!ENTITY staff.main.menu.booking.reservation.accesskey "C">
 <!ENTITY staff.main.menu.booking.pull_list.label "Pull List">
 <!ENTITY staff.main.menu.booking.pull_list.accesskey "L">

Modified: branches/rel_1_6/Open-ILS/web/opac/locale/en-US/opac.dtd
===================================================================
--- branches/rel_1_6/Open-ILS/web/opac/locale/en-US/opac.dtd	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/opac/locale/en-US/opac.dtd	2010-03-17 19:10:20 UTC (rev 15878)
@@ -489,6 +489,7 @@
 <!ENTITY rdetail.cn.more "more info...">
 <!ENTITY rdetail.cn.less "less info">
 <!ENTITY rdetail.cn.hold "place hold">
+<!ENTITY rdetail.cn.reserve "book now">
 <!ENTITY rdetail.cn.disabled "- Disabled -">
 <!ENTITY rdetail.cn.note "Copy Note">
 <!ENTITY rdetail.cn.category "Copy Category">

Modified: branches/rel_1_6/Open-ILS/web/opac/skin/default/js/copy_details.js
===================================================================
--- branches/rel_1_6/Open-ILS/web/opac/skin/default/js/copy_details.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/opac/skin/default/js/copy_details.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -232,6 +232,29 @@
 			);
 		}
 
+		/* show the book now link */
+		l = $n(row, 'copy_reserve_link');
+		unHideMe(l);
+		l.onclick = function() {
+			// XXX FIXME this duplicates things in cat/util.js
+			// Also needs i18n
+			dojo.require("fieldmapper.Fieldmapper");
+			var r = fieldmapper.standardRequest(
+				["open-ils.booking",
+					"open-ils.booking.resources.create_from_copies"],
+				[G.user.session, [copy.id()]]
+			);
+			if (!r) {
+				alert("No response from server!");
+			} else if (r.ilsevent != undefined) {
+				alert("Error from server:\n" + js2JSON(r));
+			} else {
+				xulG.auth = {"session": {"key": G.user.session}};
+				xulG.bresv_interface_opts = {"booking_results": r};
+				location.href = "/eg/booking/reservation";
+			}
+		}
+
 		if( copy.age_protect() ) 
 			appendClear($n(row, 'age_protect_value'), text(copy.age_protect().name()));
 

Modified: branches/rel_1_6/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml
===================================================================
--- branches/rel_1_6/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml	2010-03-17 19:10:20 UTC (rev 15878)
@@ -29,6 +29,8 @@
 									<a class='hide_me classic_link copy_more_info' name='less_details_link'>&rdetail.cn.less;</a>
 									<a class='hide_me classic_link copy_more_info' name='copy_hold_link' 
 										href='javascript:void(0);'>&rdetail.cn.hold;</a>
+									<a class='hide_me classic_link copy_more_info' name='copy_reserve_link' 
+										href='javascript:void(0);'>&rdetail.cn.reserve;</a>
 								</td>
 
 								<td name='status'> </td>

Modified: branches/rel_1_6/Open-ILS/web/templates/default/booking/capture.tt2
===================================================================
--- branches/rel_1_6/Open-ILS/web/templates/default/booking/capture.tt2	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/templates/default/booking/capture.tt2	2010-03-17 19:10:20 UTC (rev 15878)
@@ -1,7 +1,7 @@
 [% WRAPPER "default/base.tt2" %]
 <script src="[% ctx.media_prefix %]/js/ui/default/booking/common.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/booking/capture.js"></script>
-<link rel="stylesheet" type="text/css" href="[% ctx.media_prefix %]/css/skin/[% ctx.skin %]/booking.css" />
+<link rel="stylesheet" type="text/css" href="[% ctx.media_prefix %]/css/skin/[% ctx.skin %]/booking.css" id="booking_stylesheet_link" />
 <script type="text/javascript">openils.Util.addOnLoad(my_init);</script>
 <div id="auto_l10n_start_here">
 <!-- XXX This interface will probably go away soon in favor of merging its
@@ -15,7 +15,7 @@
             onclick="attempt_capture();" />
         <span id="result_display"></span>
     </form>
-    <div class="nice_vertical_padding" id="capture_display">
-    </div>
+    <div class="nice_vertical_padding" id="capture_info_top"></div>
+    <div class="nice_vertical_padding" id="capture_info_bottom"></div>
 </div>
 [% END %]

Modified: branches/rel_1_6/Open-ILS/web/templates/default/booking/pull_list.tt2
===================================================================
--- branches/rel_1_6/Open-ILS/web/templates/default/booking/pull_list.tt2	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/templates/default/booking/pull_list.tt2	2010-03-17 19:10:20 UTC (rev 15878)
@@ -28,12 +28,11 @@
         <table id="the_table" width="100%">
             <thead>
                 <tr>
-                    <th width="30%" class="AUTO_th_title_or_name"></th>
+                    <th width="25%" class="AUTO_th_title_or_name"></th>
                     <th width="10%" class="AUTO_th_barcode"></th>
-                    <th width="10%" class="AUTO_th_call_number"></th>
+                    <th width="15%" class="AUTO_th_call_number"></th>
                     <th width="10%" class="AUTO_th_copy_location"></th>
-                    <th width="10%" class="AUTO_th_copy_number"></th>
-                    <th width="30%" class="AUTO_th_resv_details"></th>
+                    <th width="40%" class="AUTO_th_resv_details"></th>
                 </tr>
             </thead>
             <tbody id="the_table_body">

Modified: branches/rel_1_6/Open-ILS/web/templates/default/conify/global/booking/resource_type.tt2
===================================================================
--- branches/rel_1_6/Open-ILS/web/templates/default/conify/global/booking/resource_type.tt2	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/web/templates/default/conify/global/booking/resource_type.tt2	2010-03-17 19:10:20 UTC (rev 15878)
@@ -27,7 +27,8 @@
             autoHeight='true'
             dojoType="openils.widget.AutoGrid"
             fieldOrder="['name', 'fine_interval', 'fine_amount',
-                'owner', 'catalog_item', 'transferable', 'record']"
+                'owner', 'catalog_item', 'transferable']"
+            suppressFields="['record']"
             query="{name: '*'}"
             defaultCellWidth='"auto"'
             fmClass='brt'

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/copy_browser.xul
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/copy_browser.xul	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/copy_browser.xul	2010-03-17 19:10:20 UTC (rev 15878)
@@ -33,6 +33,12 @@
     </script>
     <scripts id="openils_util_scripts"/>
 
+    <script>var djConfig = { 'baseUrl' : '/js/dojo/dojo/',parseOnLoad: true,isDebug:false };</script>
+    <script type="text/javascript" src="/js/dojo/dojo/dojo.js" />
+    <script>try { dojo.require('fieldmapper.AutoIDL'); } catch(E) { alert(E); }</script>
+    <script>try { dojo.require('dojo.date.locale'); } catch(E) { alert(E); }</script>
+    <script>try { dojo.require('dojo.date.stamp'); } catch(E) { alert(E); }</script>
+
     <script type="text/javascript" src="/xul/server/main/JSAN.js"/>
     <script type="text/javascript" src="/xul/server/cat/copy_browser.js"/>
     <script>
@@ -66,6 +72,7 @@
     ]]>
     </script>
 
+    <messagecatalog id="offlineStrings" src="/xul/server/locale/<!--#echo var='locale'-->/offline.properties" />
     <messagecatalog id="catStrings" src="/xul/server/locale/<!--#echo var='locale'-->/cat.properties" />
     <messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties" />
 

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/util.js
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/util.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/cat/util.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -503,6 +503,7 @@
 }
 
 cat.util.make_bookable = function(copy_ids) {
+    dojo.require("fieldmapper.Fieldmapper");
     var results = fieldmapper.standardRequest(
         ["open-ils.booking", "open-ils.booking.resources.create_from_copies"],
         [ses(), copy_ids]
@@ -529,7 +530,7 @@
             urls.XUL_BROWSER + "?url=" + window.escape(
                 xulG.url_prefix("/eg/conify/global/booking/resource")
             ), {
-                "tab_name": offlineStrings.getString(
+                "tab_name": $("offlineStrings").getString(
                     "menu.cmd_booking_resource.tab"
                  ),
                 "browser" : true
@@ -559,7 +560,7 @@
         xulG.new_tab(
             xulG.url_prefix("/eg/booking/reservation"),
             {
-                "tab_name": offlineStrings.getString(
+                "tab_name": $("offlineStrings").getString(
                     "menu.cmd_booking_reservation.tab"
                  ),
                 "browser" : false

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/checkout.js
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/checkout.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/checkout.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -571,7 +571,11 @@
                             return document.getElementById('circStrings').getString('staff.circ.checkout.override.item_rental_fee_required.warning');
                         },
                         '7004' : function(r) {
-                            return r.payload.status().name();
+                            try {
+                                return r.payload.status().name();
+                            } catch (E) {
+                                return "copy not available: (Unexpected error: payload not available)";  // XXX
+                            }
                         },
                         '7010' : function(r) {
                             return r.payload;

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/util.js
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/util.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/circ/util.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -2260,6 +2260,86 @@
                         document.getElementById('no_change_label').setAttribute('hidden','false');
                     }
                 break;
+                case 15: // ON_RESERVATION_SHELF
+                    check.route_to = 'RESERVATION SHELF';
+                    if (check.payload.reservation) {
+                        if (check.payload.reservation.pickup_lib() != data.list.au[0].ws_ou()) {
+                            msg += document.getElementById('commonStrings').getString('common.error');
+                            msg += '\nFIXME: ';
+                            msg += document.getElementById('circStrings').getString('staff.circ.utils.route_item_error');
+                            msg += '\n';
+                        } else {
+                            msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.route_to.msg', [check.route_to]);
+                            msg += '.\n';
+                        }
+                    } else {
+                        msg += document.getElementById('commonStrings').getString('common.error');
+                        msg += '\nFIXME: ';
+                        msg += document.getElementById('circStrings').getString('staff.circ.utils.reservation_status_error');
+                        msg += '\n';
+                    }
+                    JSAN.use('util.date');
+                    if (check.payload.reservation) {
+                        JSAN.use('patron.util');
+                        msg += '\n';
+                        msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.barcode', [check.payload.copy.barcode()]);
+                        msg += '\n';
+                        var payload_title  = (check.payload.record ? check.payload.record.title() : check.payload.copy.dummy_title() );
+                        msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.title', [payload_title]);
+                        msg += '\n';
+                        var au_obj =
+                            typeof(check.payload.reservation.usr().card) == "function" ?
+                                check.payload.reservation.usr() :
+                                patron.util.retrieve_fleshed_au_via_id(session, check.payload.reservation.usr());
+                        msg += '\n';
+                        if (au_obj.alias()) {
+                            msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.patron_alias',  [au_obj.alias()]);
+                        } else {
+                            msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.patron',  [au_obj.family_name() || "", au_obj.first_given_name() || "", au_obj.second_given_name() || ""]);
+                        }
+                        msg += '\n';
+                        msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.barcode', [au_obj.card().barcode()]);
+                        msg += '\n';
+                        msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.request_date', [util.date.formatted_date(check.payload.reservation.request_time(),'%F %H:%M')]);
+                        msg += '\n';
+
+                        msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.start_date', [util.date.formatted_date(check.payload.reservation.start_time(),'%F %H:%M')]);
+                        msg += '\n';
+                    }
+                    var rv = 0;
+                    msg += document.getElementById('circStrings').getFormattedString('staff.circ.utils.payload.reservation.slip_date', [util.date.formatted_date(new Date(),'%F')]);
+                    msg += '\n';
+                    if (!auto_print) {
+                        rv = error.yns_alert_formatted(
+                            msg,
+                            document.getElementById('circStrings').getString('staff.circ.utils.reservation_slip'),
+                            document.getElementById('circStrings').getString('staff.circ.utils.reservation_slip.print.yes'),
+                            document.getElementById('circStrings').getString('staff.circ.utils.reservation_slip.print.no'),
+                            null,
+                            document.getElementById('circStrings').getString('staff.circ.confirm.msg'),
+                            '/xul/server/skin/media/images/turtle.gif'
+                        );
+                    }
+                    if (rv == 0) {
+                        try {
+                            JSAN.use('util.print'); var print = new util.print();
+                            msg = msg.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g,'<br/>');
+                            print.simple( msg , { 'no_prompt' : true, 'content_type' : 'text/html' } );
+                        } catch(E) {
+                            var err_msg = document.getElementById('commonStrings').getString('common.error');
+                            err_msg += '\nFIXME: ' + E + '\n';
+                            dump(err_msg);
+                            alert(err_msg);
+                        }
+                    }
+                    msg = '';
+                    if (document.getElementById('no_change_label')) {
+                        var m = document.getElementById('no_change_label').getAttribute('value');
+                        m += document.getElementById('circStrings').getFormattedString('staff.circ.utils.reservation_capture', [params.barcode]);
+                        document.getElementById('no_change_label').setAttribute('value', m);
+                        document.getElementById('no_change_label').setAttribute('hidden','false');
+                    }
+                break;
                 default:
                     msg += document.getElementById('commonStrings').getString('common.error');
                     var copy_status = data.hash.ccs[check.copy.status()] ? data.hash.ccs[check.copy.status()].name() : check.copy.status().name();

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties	2010-03-17 19:10:20 UTC (rev 15878)
@@ -286,6 +286,7 @@
 staff.circ.utils.route_to.msg=This item needs to be routed to %1$s
 staff.circ.utils.route_item_error=We should have received a ROUTE_ITEM
 staff.circ.utils.route_item_status_error=status of Holds Shelf, but no actual hold found.
+staff.circ.utils.reservation_status_error=status of Reservation Shelf, but no actual hold found.
 staff.circ.utils.payload.hold.barcode=Barcode: %1$s
 staff.circ.utils.payload.hold.title=Title: %1$s
 # Hold for patron familyName, firstName secondName
@@ -298,6 +299,17 @@
 staff.circ.utils.hold_slip=Hold Slip
 staff.circ.utils.hold_slip.print.yes=Print
 staff.circ.utils.hold_slip.print.no=Do Not Print
+staff.circ.utils.payload.reservation.barcode=Barcode: %1$s
+staff.circ.utils.payload.reservation.title=Title: %1$s
+# Hold for patron familyName, firstName secondName
+staff.circ.utils.payload.reservation.patron=Reservation for patron %1$s, %2$s %3$s
+staff.circ.utils.payload.reservation.patron_alias=Reservation for patron %1$s
+staff.circ.utils.payload.reservation.request_date=Request Date: %1$s
+staff.circ.utils.payload.reservation.start_date=Start Date: %1$s
+staff.circ.utils.payload.reservation.slip_date=Slip Date: %1$s
+staff.circ.utils.reservation_slip=Reservation Slip
+staff.circ.utils.reservation_slip.print.yes=Print
+staff.circ.utils.reservation_slip.print.no=Do Not Print
 staff.circ.utils.transit_slip=Transit Slip
 staff.circ.utils.transit_slip.print.yes=Print
 staff.circ.utils.transit_slip.print.no=Do Not Print
@@ -305,6 +317,7 @@
 staff.circ.utils.hold_capture_delayed.titlebar=Hold Capture Delayed
 staff.circ.utils.hold_capture_delayed.prompt_for_nocapture=Do Not Capture 
 staff.circ.utils.hold_capture_delayed.prompt_for_capture=Capture
+staff.circ.utils.reservation_capture=%1$s has been captured for a reservation.
 staff.circ.utils.capture=%1$s has been captured for a hold.
 staff.circ.utils.needs_cataloging=%1$s needs to be cataloged.
 staff.circ.utils.copy_status.error=FIXME -- this case "%1$s" is unhandled.

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display.js
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -365,7 +365,7 @@
                             xulG.new_tab(
                                 "/eg/booking/reservation",
                                 {
-                                    "tab_name": offlineStrings.getString(
+                                    "tab_name": $("offlineStrings").getString(
                                         "menu.cmd_booking_reservation.tab"
                                     ),
                                     "browser": false
@@ -386,7 +386,7 @@
                             xulG.new_tab(
                                 "/eg/booking/pickup",
                                 {
-                                    "tab_name": offlineStrings.getString(
+                                    "tab_name": $("offlineStrings").getString(
                                         "menu.cmd_booking_reservation_pickup.tab"
                                     ),
                                     "browser": false
@@ -407,7 +407,7 @@
                             xulG.new_tab(
                                 "/eg/booking/return",
                                 {
-                                    "tab_name": offlineStrings.getString(
+                                    "tab_name": $("offlineStrings").getString(
                                         "menu.cmd_booking_reservation_return.tab"
                                     ),
                                     "browser": false

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_horiz_overlay.xul	2010-03-17 19:10:20 UTC (rev 15878)
@@ -93,7 +93,7 @@
                                     <menuitem label="&staff.patron.info.stat_cats.label;" accesskey="&staff.patron.info.stat_cats.accesskey;" command="cmd_patron_info_stats"/>
                                     <menu id="PatronNavBar_other_booking" label="&staff.main.menu.booking.label;" accesskey="&staff.main.menu.booking.accesskey;">
                                         <menupopup id="PatronNavBar_other_booking_popup">
-                                            <menuitem label="&staff.main.menu.booking.reservation.label;" accesskey="&staff.main.menu.booking.reservation.accesskey;" command="cmd_patron_reservation" />
+                                            <menuitem label="&staff.main.menu.booking.reservation.label_alt;" accesskey="&staff.main.menu.booking.reservation.accesskey;" command="cmd_patron_reservation" />
                                             <menuitem label="&staff.main.menu.booking.reservation_pickup.label;" accesskey="&staff.main.menu.booking.reservation_pickup.accesskey;" command="cmd_patron_reservation_pickup" />
                                             <menuitem label="&staff.main.menu.booking.reservation_return.label;" accesskey="&staff.main.menu.booking.reservation_return.accesskey;" command="cmd_patron_reservation_return" />
                                         </menupopup>

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_overlay.xul
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_overlay.xul	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/display_overlay.xul	2010-03-17 19:10:20 UTC (rev 15878)
@@ -93,7 +93,7 @@
                                     <menuitem label="&staff.patron.info.stat_cats.label;" accesskey="&staff.patron.info.stat_cats.accesskey;" command="cmd_patron_info_stats"/>
                                     <menu id="PatronNavBar_other_booking" label="&staff.main.menu.booking.label;" accesskey="&staff.main.menu.booking.accesskey;">
                                         <menupopup id="PatronNavBar_other_booking_popup">
-                                            <menuitem label="&staff.main.menu.booking.reservation.label;" accesskey="&staff.main.menu.booking.reservation.accesskey;" command="cmd_patron_reservation" />
+                                            <menuitem label="&staff.main.menu.booking.reservation.label_alt;" accesskey="&staff.main.menu.booking.reservation.accesskey;" command="cmd_patron_reservation" />
                                             <menuitem label="&staff.main.menu.booking.reservation_pickup.label;" accesskey="&staff.main.menu.booking.reservation_pickup.accesskey;" command="cmd_patron_reservation_pickup" />
                                             <menuitem label="&staff.main.menu.booking.reservation_return.label;" accesskey="&staff.main.menu.booking.reservation_return.accesskey;" command="cmd_patron_reservation_return" />
                                         </menupopup>

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.js
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.js	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.js	2010-03-17 19:10:20 UTC (rev 15878)
@@ -2,6 +2,7 @@
 
 function $(id) { return document.getElementById(id); }
 var patronStrings = $('patronStrings');
+var offlineStrings = $('offlineStrings');
 
 if (typeof patron == 'undefined') patron = {};
 patron.summary = function (params) {

Modified: branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.xul
===================================================================
--- branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.xul	2010-03-17 13:42:42 UTC (rev 15877)
+++ branches/rel_1_6/Open-ILS/xul/staff_client/server/patron/summary.xul	2010-03-17 19:10:20 UTC (rev 15878)
@@ -103,6 +103,7 @@
     </script>
     
     <messagecatalog id="patronStrings" src="/xul/server/locale/<!--#echo var='locale'-->/patron.properties"/>
+    <messagecatalog id="offlineStrings" src="/xul/server/locale/<!--#echo var='locale'-->/offline.properties"/>
 
     <commandset id="patron_summary_cmds">
     </commandset>



More information about the open-ils-commits mailing list