[open-ils-commits] r16974 - in branches/seials-integration/Open-ILS: src/perlmods/OpenILS/Application xul/staff_client/server/serial (dbwells)

svn at svn.open-ils.org svn at svn.open-ils.org
Sun Jul 18 17:55:43 EDT 2010


Author: dbwells
Date: 2010-07-18 17:55:37 -0400 (Sun, 18 Jul 2010)
New Revision: 16974

Added:
   branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js
   branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul
Modified:
   branches/seials-integration/Open-ILS/src/perlmods/OpenILS/Application/Serial.pm
   branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.js
   branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul
   branches/seials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul
Log:
- Initial draft commit of "Items" tab
- Basic working prediction support
- Enable multi-edit support for "Subscriptions" tab
- Simple SCAP and Issuance methods added


Modified: branches/seials-integration/Open-ILS/src/perlmods/OpenILS/Application/Serial.pm
===================================================================
--- branches/seials-integration/Open-ILS/src/perlmods/OpenILS/Application/Serial.pm	2010-07-18 14:44:18 UTC (rev 16973)
+++ branches/seials-integration/Open-ILS/src/perlmods/OpenILS/Application/Serial.pm	2010-07-18 21:55:37 UTC (rev 16974)
@@ -41,6 +41,7 @@
 use base qw/OpenILS::Application/;
 use OpenILS::Application::AppUtils;
 use OpenSRF::AppSession;
+use OpenSRF::Utils qw/:datetime/;;
 use OpenSRF::Utils::Logger qw($logger);
 use OpenILS::Utils::CStoreEditor q/:funcs/;
 use OpenILS::Utils::MFHD;
@@ -53,6 +54,9 @@
                         '864' => $MFHD_NAMES[1],
                         '855' => $MFHD_NAMES[2],
                         '865' => $MFHD_NAMES[2] );
+my %MFHD_TAGS_BY_NAME = (  $MFHD_NAMES[0] => '853',
+                        $MFHD_NAMES[1] => '854',
+                        $MFHD_NAMES[2] => '855');
 
 
 # helper method for conforming dates to ISO8601
@@ -83,7 +87,7 @@
                  type => 'string'
             },
             {
-                 name => 'issuances',
+                 name => 'items',
                  desc => 'Array of fleshed items',
                  type => 'array'
             }
@@ -119,7 +123,7 @@
         } elsif( $item->isnew ) {
             # TODO: reconsider this
             # if the item has a new issuance, create the issuance first
-            if ($item->issuance->isnew) {
+            if (ref $item->issuance eq 'Fieldmapper::serial::issuance' and $item->issuance->isnew) {
                 fleshed_issuance_alter($self, $conn, $auth, [$item->issuance]);
             }
             _cleanse_dates($item, ['date_expected','date_received']);
@@ -288,7 +292,25 @@
     return 0;
 }
 
+__PACKAGE__->register_method(
+    method  => "fleshed_serial_issuance_retrieve_batch",
+    authoritative => 1,
+    api_name    => "open-ils.serial.issuance.fleshed.batch.retrieve"
+);
 
+sub fleshed_serial_issuance_retrieve_batch {
+    my( $self, $client, $ids ) = @_;
+# FIXME: permissions?
+    $logger->info("Fetching fleshed serial issuances @$ids");
+    return $U->cstorereq(
+        "open-ils.cstore.direct.serial.issuance.search.atomic",
+        { id => $ids },
+        { flesh => 1,
+          flesh_fields => {siss => [ qw/creator editor subscription/ ]}
+        });
+}
+
+
 ##########################################################################
 # unit methods
 #
@@ -391,83 +413,101 @@
 # predict and receive methods
 #
 __PACKAGE__->register_method(
-    method    => 'generate_predictions',
-    api_name  => 'open-ils.serial.generate_predictions',
+    method    => 'make_predictions',
+    api_name  => 'open-ils.serial.make_predictions',
     api_level => 1,
     argc      => 1,
     signature => {
-        desc     => 'Receives an sre (serial record entry) id and returns an array ref of predicted issuances',
+        desc     => 'Receives an ssub id and populates the issuance and item tables',
         'params' => [ {
-                 name => 'sre_id',
-                 desc => 'Serial Record Entry ID',
-                 type => 'integer'
+                 name => 'ssub_id',
+                 desc => 'Serial Subscription ID',
+                 type => 'int'
             }
-        ],
-        'return' => {
-            desc => 'Returns predicted issuances',
-            type => 'array'
-        }
+        ]
     }
 );
 
-sub generate_predictions {
+sub make_predictions {
     my ($self, $conn, $authtoken, $args) = @_;
 
     my $editor = OpenILS::Utils::CStoreEditor->new();
-    if (!exists($args->{sre_id})) { # lookup by sdist_id instead
-        my $sdist = $editor->retrieve_serial_distribution([$args->{sdist_id}]);
-        $args->{sre_id} = $sdist->record_entry;
-    }
-    #return $args->{sre_id};
-    my $sre = $editor->retrieve_serial_record_entry([$args->{sre_id}]);
+    my $ssub_id = $args->{ssub_id};
+    my $mfhd = MFHD->new(MARC::Record->new());
 
-    #return $sre->marc;
+    my $ssub = $editor->retrieve_serial_subscription([$ssub_id]);
+    my $scaps = $editor->search_serial_caption_and_pattern({ subscription => $ssub_id, active => 't'});
+    my $sdists = $editor->search_serial_distribution( [{ subscription => $ssub->id }, {  flesh => 1,
+              flesh_fields => {sdist => [ qw/ streams / ]}, limit => 1 }] ); #TODO: 'deleted' support?
 
-    #convert from marc_xml to marc
-    my $marc = MARC::Record->new_from_xml($sre->marc);
-
-    #turn into MFHD record object
-    my $mfhd = MFHD->new($marc);
-
     my @predictions;
-    # TODO: consider support for predicting supplements/indexes (854/855)
-    my $tag = '853';
-    my @active_captions = $mfhd->active_captions($tag);
-    foreach my $caption (@active_captions) {
+    my $link_id = 1;
+    foreach my $scap (@$scaps) {
+        my $caption_field = _revive_caption($scap);
+        $caption_field->update('8' => $link_id);
+        $mfhd->append_fields($caption_field);
         my $options = {
-                'caption' => $caption,
-                'num_to_predict' => $args->{num_to_predict},
-                'last_rec_date' => $args->{last_rec_date}
+                'caption' => $caption_field,
+                'scap_id' => $scap->id,
+                'num_to_predict' => $args->{num_to_predict}
+                #'last_rec_date' => $args->{last_rec_date}
                 };
-        if ($args->{from_last_received}) {
-            my $last_received = $editor->search_serial_issuance([
-                {   'holding_type' => $MFHD_NAMES_BY_TAG{$tag},
-                    'holding_link_id' => $caption->link_id,
-                    'distribution' => $args->{sdist_id}},
-                {limit => 1, order_by => { siss => "date_expected DESC" }}]
+        if ($args->{base_issuance}) { # predict from a given issuance
+            #$options->{last_rec_date} = $args->{base_issuance}->date_expected;
+            $options->{predict_from} = _revive_holding($args->{base_issuance}->holding_code, $caption_field, 1); # fresh MFHD Record, so we simply default to 1 for seqno
+        } else { # default to predicting from last published
+            my $last_published = $editor->search_serial_issuance([
+                    {'caption_and_pattern' => $scap->id,
+                    'subscription' => $ssub_id},
+                {limit => 1, order_by => { siss => "date_published DESC" }}]
                 );
-            if ($last_received->[0]) {
-                $options->{last_rec_date} = $last_received->[0]->date_expected;
-                $options->{predict_from} = _revive_holding($mfhd, $last_received->[0]->holding_code);
+            if ($last_published->[0]) {
+                my $last_siss = $last_published->[0];
+                #my $items_for_last_published = $editor->search_serial_item({'issuance' => $last_siss->id}, {limit => 1, order_by => { sitem => "date_expected ASC" }}); # assume later expected are exceptions, TODO: move this whole date offset idea to item creation portion, not issuance creation
+                #$options->{last_rec_date} = $items_for_last_published->[0]->date_expected;
+                $options->{predict_from} = _revive_holding($last_siss->holding_code, $caption_field, 1);
+            } else {
+                #TODO: throw event (can't predict from nothing!)
             }
         }
         push( @predictions, _generate_issuance_values($mfhd, $options) );
+        $link_id++;
     }
 
     my @issuances;
     foreach my $prediction (@predictions) {
         my $issuance = new Fieldmapper::serial::issuance;
         $issuance->isnew(1);
-        $issuance->holding_link_id($prediction->[0]);
-        $issuance->label($prediction->[1]);
-        $issuance->date_published($prediction->[2]);
-        $issuance->date_expected($prediction->[3]);
-        $issuance->holding_code(OpenSRF::Utils::JSON->perl2JSON($prediction->[4]));
-        $issuance->holding_type($prediction->[5]);
+        $issuance->label($prediction->{label});
+        $issuance->date_published($prediction->{date_published}->strftime('%F'));
+        $issuance->holding_code(OpenSRF::Utils::JSON->perl2JSON($prediction->{holding_code}));
+        $issuance->holding_type($prediction->{holding_type});
+        $issuance->caption_and_pattern($prediction->{caption_and_pattern});
+        $issuance->subscription($ssub->id);
         push (@issuances, $issuance);
     }
 
-    return \@issuances;
+    fleshed_issuance_alter($self, $conn, $authtoken, \@issuances); # FIXME: catch events
+
+    my @items;
+    for (my $i = 0; $i < @issuances; $i++) {
+        my $date_expected = $predictions[$i]->{date_published}->add(seconds => interval_to_seconds($ssub->expected_date_offset))->strftime('%F');
+        my $issuance = $issuances[$i];
+        #$issuance->label(interval_to_seconds($ssub->expected_date_offset));
+        foreach my $sdist (@$sdists) {
+            my $streams = $sdist->streams;
+            foreach my $stream (@$streams) {
+                my $item = new Fieldmapper::serial::item;
+                $item->isnew(1);
+                $item->stream($stream->id);
+                $item->date_expected($date_expected);
+                $item->issuance($issuance->id);
+                push (@items, $item);
+            }
+        }
+    }
+    fleshed_item_alter($self, $conn, $authtoken, \@items); # FIXME: catch events
+    return \@items;
 }
 
 #
@@ -482,105 +522,117 @@
 # The basic method is to first convert to a single holding if compressed, then
 # increment the holding and save the resulting values to @issuances.
 # 
-# returns @issuance_values, an array of array refs containing (link id, formatted
+# returns @issuance_values, an array of hashrefs containing (formatted
 # label, formatted chronology date, formatted estimated arrival date, and an
 # array ref of holding subfields as (key, value, key, value ...)) (not a hash
-# to protect order and possible duplicate keys).
+# to protect order and possible duplicate keys), and a holding type.
 #
 sub _generate_issuance_values {
     my ($mfhd, $options) = @_;
     my $caption = $options->{caption};
+    my $scap_id = $options->{scap_id};
     my $num_to_predict = $options->{num_to_predict};
-    my $last_rec_date = $options->{last_rec_date};   # expected or actual, according to preference
-    my $predict_from = $options->{predict_from};   # optional issuance to predict from
+    my $predict_from = $options->{predict_from};   # issuance to predict from
+    #my $last_rec_date = $options->{last_rec_date};   # expected or actual
 
     # TODO: add support for predicting serials with no chronology by passing in
     # a last_pub_date option?
 
-    my $strp = new DateTime::Format::Strptime(pattern => '%F');
 
-    my $receival_date = $strp->parse_datetime($last_rec_date);
+# Only needed for 'real' MFHD records, not our temp records
+#    my $link_id = $caption->link_id;
+#    if(!$predict_from) {
+#        my $htag = $caption->tag;
+#        $htag =~ s/^85/86/;
+#        my @holdings = $mfhd->holdings($htag, $link_id);
+#        my $last_holding = $holdings[-1];
+#
+#        #if ($last_holding->is_compressed) {
+#        #    $last_holding->compressed_to_last; # convert to last in range
+#        #}
+#        $predict_from = $last_holding;
+#    }
+#
 
-    my $htag    = $caption->tag;
-    $htag =~ s/^85/86/;
-    my $link_id = $caption->link_id;
-    if(!$predict_from) {
-        my @holdings = $mfhd->holdings($htag, $link_id);
-        my $last_holding = $holdings[-1];
-
-        if ($last_holding->is_compressed) {
-            $last_holding->compressed_to_last; # convert to last in range
-        }
-        $predict_from = $last_holding;
-    }
-
-    my $pub_date  = $strp->parse_datetime($predict_from->chron_to_date);
-    my $date_diff = $receival_date - $pub_date;
-
     $predict_from->notes('public',  []);
-# add a note marker for system use
+# add a note marker for system use (?)
     $predict_from->notes('private', ['AUTOGEN']);
 
+    my $strp = new DateTime::Format::Strptime(pattern => '%F');
+    my $pub_date;
     my @issuance_values;
     my @predictions = $mfhd->generate_predictions({'base_holding' => $predict_from, 'num_to_predict' => $num_to_predict});
     foreach my $prediction (@predictions) {
         $pub_date = $strp->parse_datetime($prediction->chron_to_date);
-        my $arrival_date = $pub_date + $date_diff;
         push(
                 @issuance_values,
-                [
-                    $link_id,
-                    $prediction->format,
-                    $pub_date->strftime('%F'),
-                    $arrival_date->strftime('%F'),
-                    [$htag,$prediction->indicator(1),$prediction->indicator(2),$prediction->subfields_list],
-                    $MFHD_NAMES_BY_TAG{$caption->tag}
-                ]
+                {
+                    #$link_id,
+                    label => $prediction->format,
+                    date_published => $pub_date,
+                    #date_expected => $date_expected->strftime('%F'),
+                    holding_code => [$prediction->indicator(1),$prediction->indicator(2),$prediction->subfields_list],
+                    holding_type => $MFHD_NAMES_BY_TAG{$caption->tag},
+                    caption_and_pattern => $scap_id
+                }
             );
     }
 
     return @issuance_values;
 }
 
+sub _revive_caption {
+    my $scap = shift;
+
+    my $pattern_code = $scap->pattern_code;
+
+    # build MARC::Field
+    my $pattern_parts = OpenSRF::Utils::JSON->JSON2perl($pattern_code);
+    unshift(@$pattern_parts, $MFHD_TAGS_BY_NAME{$scap->type});
+    my $pattern_field = new MARC::Field(@$pattern_parts);
+
+    # build MFHD::Caption
+    return new MFHD::Caption($pattern_field);
+}
+
 sub _revive_holding {
-    my $mfhd = shift;
     my $holding_code = shift;
+    my $caption_field = shift;
+    my $seqno = shift;
 
     # build MARC::Field
     my $holding_parts = OpenSRF::Utils::JSON->JSON2perl($holding_code);
-    my $issuance_holding = new MARC::Field(@$holding_parts);
-    # fetch matching captions
-    my $captag = $issuance_holding->tag;
-    $captag =~ s/^86/85/;
-    my $captions_ref = $mfhd->captions($captag, 'hashref');
+    my $captag = $caption_field->tag;
+    $captag =~ s/^85/86/;
+    unshift(@$holding_parts, $captag);
+    my $holding_field = new MARC::Field(@$holding_parts);
+
     # build MFHD::Holding
-    my $link_subfield = $issuance_holding->subfield('8');
-    my ($link_id, $seqno) = split(/\./, $link_subfield);
-    return new MFHD::Holding($seqno, $issuance_holding, $captions_ref->{$link_id});
+    return new MFHD::Holding($seqno, $holding_field, $caption_field);
 }
 
 __PACKAGE__->register_method(
-    method    => 'receive_issuances',
-    api_name  => 'open-ils.serial.receive_issuances',
+    method    => 'receive_items',
+    api_name  => 'open-ils.serial.receive_items',
     api_level => 1,
     argc      => 1,
     signature => {
-        desc     => 'Marks an issuance as received, updates the shelving unit (creating a new shelving unit if needed), and updates the underlying MFHD record',
+        desc     => 'Marks an item as received, updates the shelving unit (creating a new shelving unit if needed), and updates the summaries',
         'params' => [ {
-                 name => 'issuances',
-                 desc => 'array of Issuance objects',
+                 name => 'items',
+                 desc => 'array of serial items',
                  type => 'array'
             }
         ],
         'return' => {
-            desc => 'Returns number of received issuances',
+            desc => 'Returns number of received items',
             type => 'int'
         }
     }
 );
 
-sub receive_issuances {
-    my ($self, $conn, $auth, $issuances) = @_;
+sub receive_items {
+    my ($self, $conn, $auth, $items) = @_;
 
     my $last_distribution;
     my $last_mfhd;
@@ -589,29 +641,26 @@
     my( $reqr, $evt ) = $U->checkses($auth);
     return $evt if $evt;
     my $editor = new_editor(requestor => $reqr, xact => 1);
-    foreach my $issuance (@$issuances) {
-        # unflesh shelving unit if fleshed
-        $issuance->shelving_unit( $issuance->shelving_unit->id ) if ref($issuance->shelving_unit);
-        $issuance->distribution( $issuance->distribution->id ) if ref($issuance->distribution);
+    foreach my $item (@$items) {
+        # unflesh unit if fleshed
+        $item->unit( $item->unit->id ) if ref($item->unit);
 
-        $issuance->copies_received($issuance->copies_received + 1);
-        $issuance->copies_expected($issuance->copies_expected - 1);
-        $issuance->date_received('now');
+        $item->date_received('now');
 
-        # create shelving unit if needed
-        if ($issuance->shelving_unit == -1) { # create by "volume" (first issuance division)
+        # create unit if needed
+        if ($item->unit == -1) { # create by "volume" (first item division)
         #TODO
-        } elsif ($issuance->shelving_unit == -2) { # create by "issue" (second issuance division)
+        } elsif ($item->unit == -2) { # create by "issue" (second item division)
         #TODO
         }
 
         my $mfhd;
         my $sre;
-        if ($issuance->distribution == $last_distribution) {
+        if ($item->distribution == $last_distribution) {
             # use cached record
             $mfhd = $last_mfhd;
         } else { # get MFHD record
-            my $sdist = $editor->retrieve_serial_distribution([$issuance->distribution]);
+            my $sdist = $editor->retrieve_serial_distribution([$item->distribution]);
             $sre = $editor->retrieve_serial_record_entry([$sdist->record_entry]);
 
             #convert from marc_xml to marc
@@ -623,45 +672,33 @@
             $mfhds_to_save{$sre->id} = $mfhd;
         }
 
-#        # build MARC::Field
-#        my $holding_parts = OpenSRF::Utils::JSON->JSON2perl($issuance->holding_code);
-#        my $issuance_holding = new MARC::Field(@$holding_parts);
-#        # fetch matching captions
-#        my $captag = $issuance_holding->tag;
-#        $captag =~ s/^86/85/;
-#        my $captions_ref = $mfhd->captions($captag, 'hashref');
-#        # build MFHD::Holding
-#        my $link_subfield = $issuance_holding->subfield('8');
-#        my ($link_id, $seqno) = split(/\./, $link_subfield);
-#        $issuance_holding = new MFHD::Holding($seqno, $issuance_holding, $captions_ref->{$link_id});
-        my $issuance_holding = _revive_holding($mfhd, $issuance->holding_code);
+        my $item_holding = _revive_holding($mfhd, $item->holding_code);
         
         # get all current holdings for this linked caption
-#        my @curr_holdings = $mfhd->holdings($issuance_holding->tag, $link_id);
-        my @curr_holdings = $mfhd->holdings($issuance_holding->tag, $issuance_holding->caption->link_id);
+        my @curr_holdings = $mfhd->holdings($item_holding->tag, $item_holding->caption->link_id);
         # short-circuit logic : if holding is the next one, increment the last current holding
         my $next_holding_values = $curr_holdings[-1]->next;
-        if ($next_holding_values and $issuance_holding->matches($next_holding_values)) {
+        if ($next_holding_values and $item_holding->matches($next_holding_values)) {
             $curr_holdings[-1]->extend;
         } else { # not the next expected, do full replacement
-            $mfhd->append_fields($issuance_holding);
+            $mfhd->append_fields($item_holding);
 #            my @updated_holdings = $mfhd->get_compressed_holdings($captions_ref->{$link_id});
-            my @updated_holdings = $mfhd->get_compressed_holdings($issuance_holding->caption);
+            my @updated_holdings = $mfhd->get_compressed_holdings($item_holding->caption);
             # set reference point to top of current holdings
             my $marker_field = MARC::Field->new(500, '', '','a' => 'Temporary Marker'); 
             $mfhd->insert_fields_before($curr_holdings[0], $marker_field);
             foreach my $holding (@curr_holdings) {
                 $mfhd->delete_field($holding);
             }
-            $mfhd->delete_field($issuance_holding);
+            $mfhd->delete_field($item_holding);
             $mfhd->insert_fields_before($marker_field, @updated_holdings);
             # delete reference point
             $mfhd->delete_field($marker_field);
         }   
 
-        $last_distribution = $issuance->distribution;
+        $last_distribution = $item->distribution;
         $last_mfhd = $mfhd;
-        _update_issuance($editor, undef, $issuance);
+        _update_item($editor, undef, $item);
     }
 
     foreach my $sre_id (keys %sres_to_save) {
@@ -672,13 +709,10 @@
         $sre->marc($xml);
         $sre->ischanged(1);
         $editor->update_serial_record_entry($sre);
-        #return ($sre->record);
     }
 
-    #return OpenSRF::Utils::JSON->perl2JSON($last_mfhd);
-
     $editor->commit;
-    return scalar @$issuances;
+    return scalar @$items;
 }
 
 
@@ -711,6 +745,19 @@
     /
 );
 
+__PACKAGE__->register_method(
+    method      => 'fetch_notes',
+    api_name        => 'open-ils.serial.distribution_note.retrieve.all',
+    signature   => q/
+        Returns an array of copy note objects.  
+        @param args A named hash of parameters including:
+            authtoken       : Required if viewing non-public notes
+            distribution_id : The id of the item whose notes we want to retrieve
+            pub             : True if all the caller wants are public notes
+        @return An array of note objects
+    /
+);
+
 # TODO: revisit this method to consider replacing cstore direct calls
 sub fetch_notes {
     my( $self, $connection, $args ) = @_;
@@ -759,6 +806,17 @@
     /
 );
 
+__PACKAGE__->register_method(
+    method      => 'create_note',
+    api_name        => 'open-ils.serial.distribution_note.create',
+    signature   => q/
+        Creates a new distribution note
+        @param authtoken The login session key
+        @param note The note object to create
+        @return The id of the new note object
+    /
+);
+
 sub create_note {
     my( $self, $connection, $authtoken, $note ) = @_;
 
@@ -811,6 +869,17 @@
         /
 );
 
+__PACKAGE__->register_method(
+    method      => 'delete_note',
+    api_name        =>  'open-ils.serial.distribution_note.delete',
+    signature   => q/
+        Deletes an existing distribution note
+        @param authtoken The login session key
+        @param noteid The id of the note to delete
+        @return 1 on success - Event otherwise.
+        /
+);
+
 sub delete_note {
     my( $self, $conn, $authtoken, $noteid ) = @_;
 
@@ -820,7 +889,8 @@
     my $e = new_editor(xact=>1, authtoken=>$authtoken);
     return $e->die_event unless $e->checkauth;
 
-    my $note = $e->retrieve_serial_item_note([
+    my $method = "retrieve_serial_${type}_note";
+    my $note = $e->$method([
         $noteid,
     ]) or return $e->die_event;
 
@@ -830,7 +900,7 @@
 #            $e->allowed('DELETE_COPY_NOTE', $note->item->call_number->owning_lib);
 #    }
 
-    my $method = "delete_serial_${type}_note";
+    $method = "delete_serial_${type}_note";
     $e->$method($note) or return $e->die_event;
     $e->commit;
     return 1;
@@ -907,6 +977,15 @@
 sub _delete_ssub {
     my ($editor, $override, $ssub) = @_;
     $logger->info("subscription-alter: delete subscription ".OpenSRF::Utils::JSON->perl2JSON($ssub));
+    my $sdists = $editor->search_serial_distribution(
+            { subscription => $ssub->id }, { limit => 1 } ); #TODO: 'deleted' support?
+    my $cps = $editor->search_serial_caption_and_pattern(
+            { subscription => $ssub->id }, { limit => 1 } ); #TODO: 'deleted' support?
+    my $sisses = $editor->search_serial_issuance(
+            { subscription => $ssub->id }, { limit => 1 } ); #TODO: 'deleted' support?
+    return OpenILS::Event->new(
+            'SERIAL_SUBSCRIPTION_NOT_EMPTY', payload => $ssub->id ) if (@$sdists or @$cps or @$sisses);
+
     return $editor->event unless $editor->delete_serial_subscription($ssub);
     return 0;
 }
@@ -1175,8 +1254,119 @@
         "open-ils.cstore.direct.serial.distribution.search.atomic",
         { id => $ids },
         { flesh => 1,
-          flesh_fields => {sdist => [ qw/ holding_lib receive_call_number receive_unit_template bind_call_number bind_unit_template / ]}
+          flesh_fields => {sdist => [ qw/ holding_lib receive_call_number receive_unit_template bind_call_number bind_unit_template streams / ]}
         });
 }
 
+##########################################################################
+# caption and pattern methods
+#
+__PACKAGE__->register_method(
+    method    => 'scap_alter',
+    api_name  => 'open-ils.serial.caption_and_pattern.batch.update',
+    api_level => 1,
+    argc      => 2,
+    signature => {
+        desc     => 'Receives an array of one or more caption and patterns and updates the database as needed',
+        'params' => [ {
+                 name => 'authtoken',
+                 desc => 'Authtoken for current user session',
+                 type => 'string'
+            },
+            {
+                 name => 'scaps',
+                 desc => 'Array of caption and patterns',
+                 type => 'array'
+            }
+
+        ],
+        'return' => {
+            desc => 'Returns 1 if successful, event if failed',
+            type => 'mixed'
+        }
+    }
+);
+
+sub scap_alter {
+    my( $self, $conn, $auth, $scaps ) = @_;
+    return 1 unless ref $scaps;
+    my( $reqr, $evt ) = $U->checkses($auth);
+    return $evt if $evt;
+    my $editor = new_editor(requestor => $reqr, xact => 1);
+    my $override = $self->api_name =~ /override/;
+
+# TODO: permission check
+#        return $editor->event unless
+#            $editor->allowed('UPDATE_COPY', $class->copy_perm_org($vol, $copy));
+
+    for my $scap (@$scaps) {
+        my $scapid = $scap->id;
+
+        if( $scap->isdeleted ) {
+            $evt = _delete_scap( $editor, $override, $scap);
+        } elsif( $scap->isnew ) {
+            $evt = _create_scap( $editor, $scap );
+        } else {
+            $evt = _update_scap( $editor, $override, $scap );
+        }
+    }
+
+    if( $evt ) {
+        $logger->info("caption_and_pattern-alter failed with event: ".OpenSRF::Utils::JSON->perl2JSON($evt));
+        $editor->rollback;
+        return $evt;
+    }
+    $logger->debug("caption_and_pattern-alter: done updating caption_and_pattern batch");
+    $editor->commit;
+    $logger->info("caption_and_pattern-alter successfully updated ".scalar(@$scaps)." caption_and_patterns");
+    return 1;
+}
+
+sub _delete_scap {
+    my ($editor, $override, $scap) = @_;
+    $logger->info("caption_and_pattern-alter: delete caption_and_pattern ".OpenSRF::Utils::JSON->perl2JSON($scap));
+    my $sisses = $editor->search_serial_issuance(
+            { caption_and_pattern => $scap->id }, { limit => 1 } ); #TODO: 'deleted' support?
+    return OpenILS::Event->new(
+            'SERIAL_CAPTION_AND_PATTERN_HAS_ISSUANCES', payload => $scap->id ) if (@$sisses);
+
+    return $editor->event unless $editor->delete_serial_caption_and_pattern($scap);
+    return 0;
+}
+
+sub _create_scap {
+    my ($editor, $scap) = @_;
+
+    $logger->info("caption_and_pattern-alter: new caption_and_pattern ".OpenSRF::Utils::JSON->perl2JSON($scap));
+    return $editor->event unless $editor->create_serial_caption_and_pattern($scap);
+    return 0;
+}
+
+sub _update_scap {
+    my ($editor, $override, $scap) = @_;
+
+    $logger->info("caption_and_pattern-alter: retrieving caption_and_pattern ".$scap->id);
+    my $orig_scap = $editor->retrieve_serial_caption_and_pattern($scap->id);
+
+    $logger->info("caption_and_pattern-alter: original caption_and_pattern ".OpenSRF::Utils::JSON->perl2JSON($orig_scap));
+    $logger->info("caption_and_pattern-alter: updated caption_and_pattern ".OpenSRF::Utils::JSON->perl2JSON($scap));
+    return $editor->event unless $editor->update_serial_caption_and_pattern($scap);
+    return 0;
+}
+
+__PACKAGE__->register_method(
+    method  => "serial_caption_and_pattern_retrieve_batch",
+    authoritative => 1,
+    api_name    => "open-ils.serial.caption_and_pattern.batch.retrieve"
+);
+
+sub serial_caption_and_pattern_retrieve_batch {
+    my( $self, $client, $ids ) = @_;
+    $logger->info("Fetching caption_and_patterns @$ids");
+    return $U->cstorereq(
+        "open-ils.cstore.direct.serial.caption_and_pattern.search.atomic",
+        { id => $ids }
+    );
+}
+
 1;

Added: branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js
===================================================================
--- branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js	                        (rev 0)
+++ branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.js	2010-07-18 21:55:37 UTC (rev 16974)
@@ -0,0 +1,691 @@
+dump('entering manage_items.js\n');
+
+function $(id) { return document.getElementById(id); }
+
+if (typeof serial == 'undefined') serial = {};
+serial.manage_items = function (params) {
+
+	JSAN.use('util.error'); this.error = new util.error();
+	JSAN.use('util.network'); this.network = new util.network();
+	JSAN.use('OpenILS.data'); this.data = new OpenILS.data(); this.data.init({'via':'stash'});
+}
+
+serial.manage_items.prototype = {
+
+	'list_sitem_map' : {},
+
+    'set_sdist_ids' : function () {
+		var obj = this;
+
+        try {
+            var holding_lib = $('serial_item_lib_menu').value;
+            robj = obj.network.request(
+                'open-ils.pcrud',
+                'open-ils.pcrud.id_list.sdist',
+                [ ses(), {"holding_lib" : holding_lib, "+ssub":{"record_entry" : obj.docid}}, {"join":"ssub"} ]
+            );
+            if (robj != null) {
+                if (typeof robj.ilsevent != 'undefined') throw(robj);
+                obj.sdist_ids = robj.length ? robj : [robj];
+            } else {
+                obj.sdist_ids = [];
+            }
+        } catch(E) {
+            obj.error.standard_unexpected_error_alert('set_sdist_ids failed!',E);
+        }
+    },
+
+    'build_lib_menu' : function () {
+		var obj = this;
+
+        // draw library drop-down
+        obj.org_ids = obj.network.simple_request('FM_SSUB_AOU_IDS_RETRIEVE_VIA_RECORD_ID.authoritative',[ obj.docid ]);
+        if (typeof obj.org_ids.ilsevent != 'undefined') throw(obj.org_ids);
+        JSAN.use('util.functional');
+        obj.org_ids = util.functional.map_list( obj.org_ids, function (o) { return Number(o); });
+
+        var org = obj.data.hash.aou[ obj.data.list.au[0].ws_ou() ];
+
+        JSAN.use('util.file'); JSAN.use('util.widgets');
+
+        var file; var list_data; var ml;
+
+        file = new util.file('offline_ou_list');
+        if (file._file.exists()) {
+            list_data = file.get_object(); file.close();
+            ml = util.widgets.make_menulist( list_data[0], list_data[1] );
+            ml.setAttribute('id','serial_item_lib_menu'); document.getElementById('serial_item_lib_menu_box').appendChild(ml);
+            //TODO: class this menu properly
+            for (var i = 0; i < obj.org_ids.length; i++) {
+                ml.getElementsByAttribute('value',obj.org_ids[i])[0].setAttribute('class','has_distributions');
+            }
+            /*TODO: add/enable this legend?
+            ml.firstChild.addEventListener(
+                'popupshown',
+                function(ev) {
+                    document.getElementById('legend').setAttribute('hidden','false');
+                },
+                false
+            );
+            ml.firstChild.addEventListener(
+                'popuphidden',
+                function(ev) {
+                    document.getElementById('legend').setAttribute('hidden','true');
+                },
+                false
+            );*/
+            ml.addEventListener(
+                'command',
+                function(ev) {
+                    if (document.getElementById('serial_item_refresh_button')) document.getElementById('serial_item_refresh_button').focus();
+                    JSAN.use('util.file'); var file = new util.file('manage_items_prefs.'+obj.data.server_unadorned);
+                    util.widgets.save_attributes(file, { 'serial_item_lib_menu' : [ 'value' ], 'serial_manage_items_mode' : [ 'selectedIndex' ], 'serial_manage_items_show_all' : [ 'checked' ] }); //FIXME: do load_attributes somewhere and check if selectedIndex does what we want here
+                    // get latest sdist id list based on library drowdown
+                    obj.set_sdist_ids();
+                    obj.refresh_list('main');
+                    obj.refresh_list('sunit');
+                },
+                false
+            );
+        } else {
+            throw(document.getElementById('catStrings').getString('staff.cat.copy_browser.missing_library') + '\n');
+        }
+        file = new util.file('manage_items_prefs.'+obj.data.server_unadorned);
+        util.widgets.load_attributes(file);
+        ml.value = ml.getAttribute('value');
+        if (! ml.value) {
+            ml.value = org.id();
+            ml.setAttribute('value',ml.value);
+        }
+    },
+
+	'init' : function( params ) {
+		var obj = this;
+
+		obj.docid = params['docid'];
+
+        obj.build_lib_menu();
+        obj.set_sdist_ids();
+		obj.init_lists();
+
+		JSAN.use('util.controller'); obj.controller = new util.controller();
+		obj.controller.init(
+			{
+				'control_map' : {
+					'save_columns' : [ [ 'command' ], function() { obj.lists.main.save_columns(); } ],
+					'cmd_broken' : [ ['command'], function() { alert('Not Yet Implemented'); } ],
+					'sel_clip' : [ ['command'], function() { obj.lists.main.clipboard(); } ],
+                    'cmd_add_item' : [
+                        ['command'],
+                        function() {
+                            try {
+                                var new_item = new sitem();
+                                new_item.issuance(new siss());
+                                new_item.stream(1); //FIXME: hard-coded stream
+                                new_item.issuance().subscription(1); //FIXME: hard-coded subscription
+                                new_item.isnew(1);
+                                new_item.issuance().isnew(1);
+                                spawn_item_editor( {'items' : [new_item], 'edit' : 1 } );
+
+                                obj.refresh_list('main');
+
+                            } catch(E) {
+                                obj.error.standard_unexpected_error_alert(document.getElementById('catStrings').getString('staff.cat.copy_browser.edit_items.error'),E);
+                            }
+                        }
+                    ],
+                    'cmd_edit_items' : [
+                        ['command'],
+                        function() {
+                            try {
+                                if (!obj.retrieve_ids || obj.retrieve_ids.length == 0) return;
+
+                                JSAN.use('util.functional');
+                                var list = util.functional.map_list(
+                                        obj.retrieve_ids,
+                                        function (o) {
+                                            return o.sitem_id;
+                                        }
+                                    );
+
+                                spawn_item_editor( { 'item_ids' : list, 'edit' : 1 } );
+
+                                obj.refresh_list(obj.selected_list);
+
+                            } catch(E) {
+                                obj.error.standard_unexpected_error_alert(document.getElementById('catStrings').getString('staff.cat.copy_browser.edit_items.error'),E);
+                            }
+                        }
+                    ],
+                    'cmd_delete_items' : [
+                        ['command'],
+                        function() {
+                            try {
+                                JSAN.use('util.functional');
+                                var list = util.functional.map_list(
+                                        obj.retrieve_ids,
+                                        function (o) {
+                                            return obj.list_sitem_map[o.sitem_id];
+                                        }
+                                    );
+                                var delete_msg;
+                                if (list.length != 1) {
+                                    delete_msg = document.getElementById('catStrings').getFormattedString('staff.cat.copy_browser.delete_items.confirm.plural', [list.length]);
+                                } else {
+                                    delete_msg = document.getElementById('catStrings').getString('staff.cat.copy_browser.delete_items.confirm');
+                                }
+                                var r = obj.error.yns_alert(
+                                        delete_msg,
+                                        document.getElementById('catStrings').getString('staff.cat.copy_browser.delete_items.title'),
+                                        document.getElementById('catStrings').getString('staff.cat.copy_browser.delete_items.delete'),
+                                        document.getElementById('catStrings').getString('staff.cat.copy_browser.delete_items.cancel'),
+                                        null,
+                                        document.getElementById('commonStrings').getString('common.confirm')
+                                );
+
+                                if (r == 0) {
+                                    for (var i = 0; i < list.length; i++) {
+                                        list[i].isdeleted('1');
+                                    }
+                                    var robj = obj.network.request(
+                                            'open-ils.serial',
+                                            'open-ils.serial.item.fleshed.batch.update',
+                                        [ ses(), list, true ],
+                                        null,
+                                        {
+                                            'title' : document.getElementById('catStrings').getString('staff.cat.copy_browser.delete_items.override'),
+                                            'overridable_events' : [ // FIXME: replace or delete these events
+                                                1208 /* TITLE_LAST_COPY */,
+                                                1227 /* COPY_DELETE_WARNING */,
+                                            ]
+                                        }
+                                    );
+                                    if (robj == null) throw(robj);
+                                    if (typeof robj.ilsevent != 'undefined') {
+                                        if ( (robj.ilsevent != 0) && (robj.ilsevent != 1227 /* COPY_DELETE_WARNING */) && (robj.ilsevent != 1208 /* TITLE_LAST_COPY */) ) throw(robj);
+                                    }
+                                    obj.refresh_list(obj.selected_list);
+                                }
+
+
+                            } catch(E) {
+                                obj.error.standard_unexpected_error_alert('staff.serial.manage_items.delete_items.error',E);
+                                obj.refresh_list();
+                            }
+                        }
+                    ],
+                    'cmd_predict_items' : [
+                        ['command'],
+                        function() {
+                            alert('Subscription selection needed here'); //FIXME: make this prompt, or discard this feature
+                        }
+                    ],
+                    'cmd_receive_items' : [
+                        ['command'],
+                        function() {
+                            try {
+                                JSAN.use('util.functional');
+                                var list = util.functional.map_list(
+                                        obj.retrieve_ids,
+                                        function (o) {
+                                            return obj.list_sitem_map[o.sitem_id];
+                                        }
+                                    );
+                                for (var i = 0; i < list.length; i++) {
+                                        list[i].unit('1'); //FIXME: hard-coded unit
+                                }
+
+                                var robj = obj.network.request(
+                                            'open-ils.serial',
+                                            'open-ils.serial.receive_items',
+                                            [ ses(), list ]
+                                        );
+                                if (typeof robj.ilsevent != 'undefined') throw(robj); //TODO: catch for override
+
+                                alert('Successfully received '+robj+' item(s)');
+                                obj.refresh_list('main');
+                                obj.refresh_list('sunit');
+                                
+                            } catch(E) {
+                                obj.error.standard_unexpected_error_alert('cmd_receive_items failed!',E);
+                            }
+                        }
+                    ],
+                    'cmd_edit_sunit' : [
+                        ['command'],
+                        function() {
+                            try {
+                                /*if (!obj.retrieve_ids || obj.retrieve_ids.length == 0) return;
+
+                                JSAN.use('util.functional');
+                                var list = util.functional.map_list(
+                                        obj.retrieve_ids,
+                                        function (o) {
+                                            return o.sitem_id;
+                                        }
+                                    );
+*/
+                                spawn_sunit_editor( { 'sunit_ids' : [1], 'edit' : 1 } ); //FIXME: hard-coded sunit
+
+                            } catch(E) {
+                                obj.error.standard_unexpected_error_alert('cmd_edit_sunit failed!',E);
+                            }
+                        }
+                    ],
+
+                    'cmd_items_print' : [ ['command'], function() { obj.items_print(obj.selected_list); } ],
+					'cmd_items_export' : [ ['command'], function() { obj.items_export(obj.selected_list); } ],
+					'cmd_refresh_list' : [ ['command'], function() { obj.refresh_list('main'); obj.refresh_list('sunit'); } ]
+				}
+			}
+		);
+        
+		obj.retrieve('main'); // retrieve main list
+        obj.retrieve('sunit'); // retrieve shelving unit list
+
+		obj.controller.view.sel_clip.setAttribute('disabled','true');
+
+	},
+
+	'items_print' : function(which) {
+		var obj = this;
+		try {
+			var list = obj.lists[which];
+/* FIXME: serial items print template?			JSAN.use('patron.util');
+			var params = { 
+				'patron' : patron.util.retrieve_fleshed_au_via_id(ses(),obj.patron_id), 
+				'template' : 'items_out'
+			}; */
+			list.print( params );
+		} catch(E) {
+			obj.error.standard_unexpected_error_alert('manage_items printing',E);
+		}
+	},
+
+	'items_export' : function(which) {
+		var obj = this;
+		try {
+			var list = obj.lists[which];
+			list.dump_csv_to_clipboard();
+		} catch(E) {
+			obj.error.standard_unexpected_error_alert('manage_items export',E);
+		}
+	},
+
+	'init_lists' : function() {
+		var obj = this;
+
+		JSAN.use('circ.util');
+        var columns = item_columns({});
+        
+        function retrieve_row(params) {
+			try { 
+				var row = params.row;
+                obj.network.simple_request( //FIXME: pcrud fleshing won't work!!
+                    'FM_SITEM_RETRIEVE',
+                    //[ ses(), row.my.sitem_id, {"flesh":1, "flesh_fields":{"sitem": ["creator","editor","distribution","shelving_unit"]}}],
+                    [ ses(), row.my.sitem_id, {"flesh":2,"flesh_fields":{"sitem":["creator","editor","issuance","stream","unit","notes"], "sunit":["call_number"], "sstr":["distribution"]}}], // TODO: we really need note count only, not the actual notes, is there a smart way to do that?
+                    function(req) {
+                        try {
+                            var robj = req.getResultObject();
+                            if (typeof robj.ilsevent != 'undefined') throw(robj);
+                            if (typeof robj.ilsevent == 'null') throw('null result');
+                            obj.list_sitem_map[robj.id()] = robj;
+                            row.my.sitem = robj;
+                            //params.row_node.setAttribute( 'retrieve_id', js2JSON({'copy_id':copy_id,'circ_id':row.my.circ.id(),'barcode':row.my.acp.barcode(),'doc_id': ( row.my.record ? row.my.record.id() : null ) }) );
+                            params.row_node.setAttribute( 'retrieve_id', js2JSON({'sitem_id':robj.id()}) );
+                            dump('dumping... ' + js2JSON(obj.list_sitem_map[robj.id()]));
+                            if (typeof params.on_retrieve == 'function') {
+                                params.on_retrieve(row);
+                            }
+
+                        } catch(E) {
+                            obj.error.standard_unexpected_error_alert('staff.serial.manage_items.retrieve_row.callback_error', E);
+                        }
+                    }
+                );
+				return row;
+			} catch(E) {
+				obj.error.standard_unexpected_error_alert('staff.serial.manage_items.retrieve_row.error_in_retrieve_row',E);
+				return params.row;
+			}
+		}
+
+		JSAN.use('util.list');
+
+        obj.lists = {};
+        obj.lists.main = new util.list('item_tree');
+		obj.lists.main.init(
+			{
+				'columns' : columns,
+				'map_row_to_columns' : circ.util.std_map_row_to_columns(),
+				'retrieve_row' : retrieve_row,
+				'on_select' : function(ev) {
+                    obj.selected_list = 'main';
+					JSAN.use('util.functional');
+					var sel = obj.lists.main.retrieve_selection();
+					obj.controller.view.sel_clip.setAttribute('disabled',sel.length < 1);
+					var list = util.functional.map_list(
+						sel,
+						function(o) { return JSON2js( o.getAttribute('retrieve_id') ); }
+					);
+					if (typeof obj.on_select == 'function') {
+						obj.on_select(list);
+					}
+					if (typeof window.xulG == 'object' && typeof window.xulG.on_select == 'function') {
+						obj.error.sdump('D_CAT','manage_items: Calling external .on_select()\n');
+						window.xulG.on_select(list);
+					}
+				}
+			}
+		);
+        obj.lists.main.sitem_retrieve_params = {'date_received' : null };
+        obj.lists.main.sitem_sort_params ={'order_by' : {'sitem' : 'date_expected ASC'}};
+
+        obj.lists.sunit = new util.list('sunit_tree');
+		obj.lists.sunit.init(
+			{
+				'columns' : columns,
+				'map_row_to_columns' : circ.util.std_map_row_to_columns(),
+				'retrieve_row' : retrieve_row,
+				'on_select' : function(ev) {
+                    obj.selected_list = 'sunit';
+					JSAN.use('util.functional');
+					var sel = obj.lists.sunit.retrieve_selection();
+					obj.controller.view.sel_clip.setAttribute('disabled',sel.length < 1);
+					var list = util.functional.map_list(
+						sel,
+						function(o) { return JSON2js( o.getAttribute('retrieve_id') ); }
+					);
+					if (typeof obj.on_select == 'function') {
+						obj.on_select(list);
+					}
+					if (typeof window.xulG == 'object' && typeof window.xulG.on_select == 'function') {
+						obj.error.sdump('D_CAT','serctrl: Calling external .on_select()\n');
+						window.xulG.on_select(list);
+					} else {
+						obj.error.sdump('D_CAT','serctrl: No external .on_select()\n');
+					}
+				}
+			}
+		);
+        obj.lists.sunit.sitem_retrieve_params = {'unit' : 1, 'date_received' : {"!=" : null}}; //FIXME: hard-coded shelving unit
+        obj.lists.sunit.sitem_sort_params ={'order_by' : {'sitem' : 'date_received DESC'}};
+    },
+
+	'refresh_list' : function(list_name) {
+        var obj = this;
+
+        // TODO: make this change on the checkbox command event?
+        if (list_name == 'main') {
+            if (document.getElementById('serial_manage_items_show_all').checked) {
+                delete obj.lists.main.sitem_retrieve_params.date_received;
+            } else {
+                obj.lists.main.sitem_retrieve_params.date_received = null;
+            }
+        }
+        //TODO Optimize this?
+        obj.retrieve(list_name);
+    },
+
+	'retrieve' : function(list_name) {
+		var obj = this;
+        var list = obj.lists[list_name];
+		if (window.xulG && window.xulG.items) {
+			obj.items = window.xulG.items;
+		} else {
+			obj.items = []; //FIXME: not using this
+
+            var rparams = list.sitem_retrieve_params;
+            var robj;
+            if (obj.sdist_ids.length > 0) {
+                rparams['+sstr'] = { "distribution" : {"in" : obj.sdist_ids} };
+                var other_params = list.sitem_sort_params;
+                other_params.join = 'sstr';
+
+                robj = obj.network.simple_request(
+                    'FM_SITEM_ID_LIST',
+                    [ ses(), rparams, other_params ]
+                );
+            }
+/*			if (typeof robj.ilsevent!='undefined') {
+				obj.error.standard_unexpected_error_alert('FIXME: Catch edit control with no distribution',E);
+			}*/
+		}
+        if (!robj) {
+            robj = [];
+        }else if (!robj.length) {
+            robj = [robj];
+        }
+
+		list.clear();
+
+        for (i = 0; i < robj.length; i++) {
+            list.append( { 'row' : { 'my' : { 'sitem_id' : robj[i] } }, 'to_bottom' : true, 'no_auto_select' : true } );
+        }
+	},
+
+	'on_select' : function(list) {
+
+		dump('manage_items.on_select list = ' + js2JSON(list) + '\n');
+
+		var obj = this;
+
+		obj.retrieve_ids = list;
+	}
+}
+
+function item_columns(modify,params) {
+
+    JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
+    //JSAN.use('util.network'); var network = new util.network();
+
+    var c = [
+        {
+            'id' : 'sitem_id',
+            'label' : 'Item ID',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'render' : function(my) { return my.sitem.id(); },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'creator',
+            'label' : 'Creator',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : true,
+            'persist' : 'hidden width ordinal',
+            'render' : function(my) { return my.sitem.creator().usrname(); }
+        },
+        {
+            'id' : 'create_date',
+            'label' : document.getElementById('circStrings').getString('staff.circ.utils.create_date'),
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : true,
+            'persist' : 'hidden width ordinal',
+            'render' : function(my) { return my.sitem.create_date().substr(0,10); }
+        },
+        {
+            'id' : 'editor',
+            'label' : 'Editor',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'persist' : 'hidden width ordinal',
+            'render' : function(my) { return my.sitem.editor().usrname(); }
+        },
+        {
+            'id' : 'edit_date',
+            'label' : document.getElementById('circStrings').getString('staff.circ.utils.edit_date'),
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'persist' : 'hidden width ordinal',
+            'render' : function(my) { return my.sitem.edit_date().substr(0,10); }
+        },
+        {
+            'id' : 'distribution',
+            'label' : 'Distribution',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : true,
+            'persist' : 'hidden width ordinal',
+            'render' : function(my) { return my.sitem.stream().distribution().label(); }
+        },
+        {
+            'id' : 'unit',
+            'label' : 'Unit',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'persist' : 'hidden width ordinal',
+            'render' : function(my) { return my.sitem.unit().call_number().label(); }
+        },
+        {
+            'id' : 'label',
+            'label' : 'Issuance Label',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'render' : function(my) { return my.sitem.issuance().label(); },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'date_published',
+            'label' : 'Date Published',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'render' : function(my) { return my.sitem.issuance().date_published().substr(0,10); },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'date_expected',
+            'label' : 'Date Expected',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'render' : function(my) { return my.sitem.date_expected().substr(0,10); },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'date_received',
+            'label' : 'Date Received',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'render' : function(my) { return my.sitem.date_received().substr(0,10); },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'notes',
+            'label' : 'Notes',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : false,
+            'render' : function(my) { return my.sitem.notes().length; },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'holding_code',
+            'label' : 'Holding Code',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : true,
+            'render' : function(my) { return my.sitem.issuance().holding_code(); },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'holding_type',
+            'label' : 'Holding Type',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : true,
+            'render' : function(my) { return my.sitem.issuance().holding_type(); },
+            'persist' : 'hidden width ordinal'
+        },
+        {
+            'id' : 'holding_link_id',
+            'label' : 'Holding Link ID',
+            'flex' : 1,
+            'primary' : false,
+            'hidden' : true,
+            'render' : function(my) { return my.sitem.issuance().holding_link_id(); },
+            'persist' : 'hidden width ordinal'
+        }
+    ];
+    for (var i = 0; i < c.length; i++) {
+        if (modify[ c[i].id ]) {
+            for (var j in modify[ c[i].id ]) {
+                c[i][j] = modify[ c[i].id ][j];
+            }
+        }
+    }
+    if (params) {
+        if (params.just_these) {
+            JSAN.use('util.functional');
+            var new_c = [];
+            for (var i = 0; i < params.just_these.length; i++) {
+                var x = util.functional.find_list(c,function(d){return(d.id==params.just_these[i]);});
+                new_c.push( function(y){ return y; }( x ) );
+            }
+            c = new_c;
+        }
+        if (params.except_these) {
+            JSAN.use('util.functional');
+            var new_c = [];
+            for (var i = 0; i < c.length; i++) {
+                var x = util.functional.find_list(params.except_these,function(d){return(d==c[i].id);});
+                if (!x) new_c.push(c[i]);
+            }
+            c = new_c;
+        }
+    }
+    //return c.sort( function(a,b) { if (a.label < b.label) return -1; if (a.label > b.label) return 1; return 0; } );
+    return c;
+};
+
+spawn_item_editor = function(params) {
+    try {
+        if (!params.item_ids && !params.items) return;
+        if (params.item_ids && params.item_ids.length == 0) return;
+        if (params.items && params.items.length == 0) return;
+        if (params.item_ids) params.item_ids = js2JSON(params.item_ids); // legacy
+        if (!params.caller_handles_update) params.handle_update = 1; // legacy
+
+        var obj = {};
+        JSAN.use('util.network'); obj.network = new util.network();
+        JSAN.use('util.error'); obj.error = new util.error();
+
+        var title = '';
+        if (params.item_ids && params.item_ids.length > 1 && params.edit == 1)
+            title = 'Batch Edit Items';
+        else /* if(params.copies && params.copies.length > 1 && params.edit == 1)
+            title = 'Batch View Items';
+        else if(params.item_ids && params.item_ids.length == 1) */
+            title = 'Edit Item';/*
+        else
+            title = 'View Item';*/
+
+        JSAN.use('util.window'); var win = new util.window();
+        var my_xulG = win.open(
+            (urls.XUL_SERIAL_ITEM_EDITOR),
+            title,
+            'chrome,modal,resizable',
+            params
+        );
+        if (my_xulG.items && params.edit) {
+            return my_xulG.items;
+        } else {
+            return [];
+        }
+    } catch(E) {
+        JSAN.use('util.error'); var error = new util.error();
+        error.standard_unexpected_error_alert('error in spawn_item_editor',E);
+    }
+}
+
+dump('exiting manage_items.js\n');

Added: branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul
===================================================================
--- branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul	                        (rev 0)
+++ branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_items.xul	2010-07-18 21:55:37 UTC (rev 16974)
@@ -0,0 +1,135 @@
+<?xml version="1.0"?>
+<!-- Application: Evergreen Staff Client -->
+<!-- Screen: Manage Items Overlay -->
+<!--
+vim:noet:sw=4:ts=4:
+-->
+<!DOCTYPE overlay PUBLIC "" ""[
+    <!--#include virtual="/opac/locale/${locale}/lang.dtd"-->
+]>
+
+<overlay id="serial_manage_items_overlay" 
+	xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+    <script type="text/javascript" src="/xul/server/serial/manage_items.js"/>
+    <script>
+    <![CDATA[
+        function my_init() {
+            try {
+                netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                if (typeof JSAN == 'undefined') { 
+                    throw( document.getElementById("commonStrings").getString('common.jsan.missing') );
+                }
+                JSAN.errorLevel = "die"; // none, warn, or die
+                JSAN.addRepository('/xul/server/');
+                JSAN.use('util.error'); g.error = new util.error();
+                g.error.sdump('D_TRACE','my_init() for manage_items.xul');
+
+                JSAN.use('serial.manage_items'); g.manage_items = new serial.manage_items();
+
+                //g.manage_items.init( { 'sre_id' : xul_param('sre_id'), 'sdist_id' : xul_param('sdist_id') } );
+                g.manage_items.init( { 'docid' : xul_param('docid') } );
+
+            } catch(E) {
+                var err_msg = document.getElementById("commonStrings").getFormattedString('common.exception', ['serial/manage_items.xul', E]);
+                try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); }
+                alert(err_msg);
+            }
+        }
+
+    ]]>
+    </script>
+    <popupset>
+        <popup id="serial_manage_items_popup">
+            <menuitem command="cmd_edit_items" label="Edit Item Attributes" accesskey="&staff.cat.copy_browser.actions.cmd_edit_items.accesskey;"/>
+            <menuitem command="cmd_delete_items" label="Delete Item" accesskey="&staff.cat.copy_browser.actions.cmd_delete_items.accesskey;"/>
+        </popup>
+    </popupset>
+    <tabpanel id="serial_manage_items" orient="vertical" flex="1">
+        <hbox align="center">
+            <hbox id="serial_item_lib_menu_box"/>
+            <label value="Mode:" control="mode_receive"/><radiogroup id="serial_manage_items_mode" orient="horizontal"><radio id="mode_receive" label="Receive"/><radio id="mode_bind" label="Bind"/></radiogroup><checkbox id="serial_manage_items_show_all" label="Show All" />
+            <button id="refresh_button" label="&staff.cat.copy_browser.holdings_maintenance.refresh_button.label;" command="cmd_refresh_list" />
+            <spacer flex="1"/>
+            <menubar>
+                <menu label="Actions for this Serial Control" accesskey="C">
+                    <menupopup>
+                        <menuitem command="cmd_predict_items" label="Predict Items"/>
+                        <menuitem command="cmd_add_item" label="Add Custom Item"/>
+                        <menuitem command="cmd_edit_mfhd" label="Edit MFHD Record"/>
+                    </menupopup>
+                </menu>
+                <menu label="&staff.cat.copy_browser.holdings_maintenance.actions.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.actions.accesskey;">
+                    <menupopup>
+                        <menuitem command="cmd_edit_items" label="Edit Item Attributes" accesskey="&staff.cat.copy_browser.actions.cmd_edit_items.accesskey;"/>
+                        <menuitem command="cmd_delete_items" label="Delete Item" accesskey="&staff.cat.copy_browser.actions.cmd_delete_items.accesskey;"/>
+                        <menuseparator/>
+                        <menuitem command="cmd_refresh_list" label="&staff.cat.copy_browser.holdings_maintenance.cmd_refresh_list.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_refresh_list.accesskey;"/>
+                        <menuitem command="save_columns" label="&staff.cat.copy_browser.holdings_maintenance.save_columns.label;"/>
+                        <!-- <menuitem command="sel_clip" label="&staff.cat.copy_browser.holdings_maintenance.sel_clip.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.sel_clip.accesskey;"/>
+                        <menuitem command="cmd_transfer_items" label="&staff.cat.copy_browser.holdings_maintenance.cmd_transfer_items.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_transfer_items.accesskey;"/>
+                        <menuseparator/>
+                        <menuitem command="cmd_add_volumes" label="&staff.cat.copy_browser.holdings_maintenance.cmd_add_volumes.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_add_volumes.accesskey;"/>
+                        <menuitem command="cmd_edit_volumes" label="&staff.cat.copy_browser.holdings_maintenance.cmd_edit_volumes.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_edit_volumes.accesskey;"/>
+                        <menuitem command="cmd_mark_volume" label="&staff.cat.copy_browser.holdings_maintenance.cmd_mark_volume.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_mark_volume.accesskey;"/>
+                        <menuitem command="cmd_transfer_volume" label="&staff.cat.copy_browser.holdings_maintenance.cmd_transfer_volume.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_transfer_volume.accesskey;"/>
+                        <menuitem command="cmd_delete_volumes" label="&staff.cat.copy_browser.holdings_maintenance.cmd_delete_volumes.label;" accesskey=""/>
+                        <menuseparator/>
+                        <menuitem command="cmd_print_spine_labels" label="&staff.cat.copy_browser.holdings_maintenance.cmd_print_spine_labels.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_print_spine_labels.accesskey;"/>
+                        <menuitem command="cmd_replace_barcode" label="&staff.cat.copy_browser.holdings_maintenance.cmd_replace_barcode.label;" accesskey=""/> -->
+                    </menupopup>
+                </menu>
+            </menubar>
+        </hbox>
+        <tree id="item_tree" flex="2" enableColumnDrag="true" context="serial_manage_items_popup"/>
+        <splitter state="open" collapse="after" resizebefore="closest" resizeafter="farthest"/>
+        <hbox align="center">
+            <description value="Current Unit"/>
+            <spacer flex="1"/>
+            <!--<description value="Copy"/>
+            <menulist editable="true">
+                <menupopup>
+                    <menuitem label="Auto Copy" value="AUTO"/>
+                    <menuitem label="New Copy" value="NEW"/>
+                    <menuitem label="(list copies) ######"/>
+                </menupopup>
+            </menulist>-->
+            <button label="Receive/Move Selected &#8595;" command="cmd_receive_items"/>
+            <description value="into"/>
+            <description value="[V.135]"/>
+            <!--<menulist label="Auto - Top Level">
+                <menupopup>
+                    <menuitem label="Auto per Volume" selected="true"/>
+                    <menuitem label="Auto per Issue"/>
+                    <menuitem label="TEST1234"/>
+                </menupopup>
+            </menulist>-->
+            <menubar>
+                <menu label="Set Current Unit">
+                    <menupopup>
+                        <menu label="Recent">
+                            <menupopup>
+                                <menuitem command="cmd_broken" label="V.1"/>
+                                <menuitem command="cmd_broken" label="V.2"/>
+                            </menupopup>
+                        </menu>
+                        <menu label="Frequent">
+                            <menupopup>
+                                <menuitem command="cmd_broken" label="V.1"/>
+                                <menuitem command="cmd_broken" label="V.2"/>
+                            </menupopup>
+                        </menu>
+                        <menuitem command="cmd_broken" label="Other..."/>
+                    </menupopup>
+                </menu>
+                <menu label="Actions for Units">
+                    <menupopup>
+                        <menuitem command="cmd_edit_sunit" label="Edit Current Unit..."/>
+                        <menuitem command="cmd_broken" label="Manage Units..."/>
+                    </menupopup>
+                </menu>
+            </menubar>
+        </hbox>
+        <tree id="sunit_tree" flex="1" enableColumnDrag="true" context="serial_manage_items_popup"/>
+    </tabpanel>
+
+</overlay>

Modified: branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.js
===================================================================
--- branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.js	2010-07-18 14:44:18 UTC (rev 16973)
+++ branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.js	2010-07-18 21:55:37 UTC (rev 16974)
@@ -24,6 +24,8 @@
 
     'ids_from_sel_list' : function(type) {
         var obj = this;
+        JSAN.use('util.functional');
+
         var list = util.functional.map_list(
             util.functional.filter_list(
                 obj.sel_list,
@@ -185,7 +187,6 @@
                             ['command'],
                             function() {
                                 try {
-                                    JSAN.use('util.functional');
                                     var list = obj.ids_from_sel_list('ssub');
                                     if (list.length == 0) return;
 
@@ -223,7 +224,6 @@
                             ['command'],
                             function() {
                                 try {
-                                    JSAN.use('util.functional');
                                     var list = obj.ids_from_sel_list('ssub');
                                     if (list.length == 0) return;
 
@@ -261,7 +261,6 @@
                             ['command'],
                             function() {
                                 try {
-                                    JSAN.use('util.functional');
                                     var list = obj.ids_from_sel_list('ssub');
                                     if (list.length == 0) list = obj.ids_from_sel_list('sdist-group');
                                     if (list.length == 0) return;
@@ -454,7 +453,6 @@
                             ['command'],
                             function() {
                                 try {
-                                    JSAN.use('util.functional');
                                     var list = obj.ids_from_sel_list('aou');
                                     if (list.length == 0) return;
                                     //TODO: permission check?
@@ -500,12 +498,12 @@
                                         return;
                                     }
                                     
-                                    JSAN.use('util.functional');
-
                                     var list = obj.ids_from_sel_list('ssub');
 
                                     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserWrite');
 
+                                    JSAN.use('util.functional');
+
                                     var ssub_list = util.functional.map_list(
                                         list,
                                         function (o) {
@@ -609,6 +607,62 @@
                                 obj.refresh_list();
                             }
                         ],
+                        'cmd_make_predictions' : [
+                            ['command'],
+                            function() {
+                                try {
+                                    var list = obj.ids_from_sel_list('ssub');
+                                    if (list.length == 0) {
+                                        alert('You must select a subscription before predicting issuances.'); //TODO: better error
+                                        return;
+                                    }
+
+                                    var num_to_predict = prompt('How many items would you like to predict?',
+                                            '12',
+                                            'Number of Predicted Items');
+                                    num_to_predict = String( num_to_predict ).replace(/\D/g,'');
+                                    if (num_to_predict == '') {
+                                        alert('Invalid number entered!'); //TODO: better error
+                                        return;
+                                    }
+
+                                    for (i = 0; i < list.length; i++) {
+                                        var robj = obj.network.request(
+                                                'open-ils.serial',
+                                                'open-ils.serial.make_predictions',
+                                                [ ses(), {"ssub_id":list[i], "num_to_predict":num_to_predict, "last_rec_date":"2010-07-07"}]
+                                        );
+                                        util.functional.map_list(
+                                            robj,
+                                            function(o) {
+                                                alert('debug: ' + o.date_expected());
+                                            }
+                                        );
+                                    }
+                                    return;
+
+                                    /*JSAN.use('util.functional');
+                                    var list = util.functional.map_list(
+                                            robj,
+                                            function (o) {
+                                                o.distribution(obj.sdist_id);
+                                                return o;
+                                            }
+                                        );*/
+
+                                    var robj = obj.network.request(
+                                                'open-ils.serial',
+                                                'open-ils.serial.item.fleshed.batch.update',
+                                                [ ses(), list ]
+                                            );
+
+                                    //obj.refresh_list('main');
+
+                                } catch(E) {
+                                    obj.error.standard_unexpected_error_alert('cmd_make_predictions failed!',E);
+                                }
+                            }
+                        ],
 /*dbw2                      'sel_distribution_details' : [
                             ['command'],
                             function() {
@@ -1071,20 +1125,30 @@
 
     'on_select' : function(list,twisty) {
         var obj = this;
+        var sel_lists = {};
+
         for (var i = 0; i < list.length; i++) {
-            var node = obj.map_tree[ list[i] ];
             var row_type = list[i].split('_')[0];
             var id = list[i].split('_')[1];
 
-            switch(row_type) {
-                case 'aou' : obj.on_select_org(id,twisty); break;
-                case 'ssub' : obj.on_select_ssub(id,twisty); break;
-                case 'sdist' : obj.on_select_sdist(id,twisty); break;
-                case 'siss' : obj.on_select_siss(id,twisty); break;
-                case 'scap' : obj.on_select_scap(id,twisty); break;
-                default: break;
+            if (!sel_lists[row_type]) sel_lists[row_type] = [];
+            sel_lists[row_type].push(id);
+
+            if (twisty) {
+                switch(row_type) {
+                    case 'aou' : obj.on_select_org(id,twisty); break;
+                    case 'ssub' : obj.on_select_ssub(id,twisty); break;
+                    default: break;
+                }
             }
         }
+
+        var row_type = obj.focused_node_retrieve_id.split('_')[0];
+        var id = obj.focused_node_retrieve_id.split('_')[1];
+
+        if (sel_lists[row_type]) { // the type focused is in the selection (usually the case)
+            obj['on_click_' + row_type](sel_lists[row_type],twisty);
+        }
     },
 
     'on_select_ssub' : function(ssub_id,twisty) {
@@ -1135,11 +1199,18 @@
                 document.getElementById('cmd_show_libs_with_distributions').setAttribute('disabled','false'); 
                 document.getElementById('lib_menu').setAttribute('disabled','false'); 
             } );
+        } catch(E) {
+            alert(E);
+        }
+    },
 
-            // draw ssub editor
+    'on_click_ssub' : function(ssub_ids,twisty) {
+        var obj = this;
+        try {
+            // draw sdist editor
             if (typeof twisty == 'undefined') {
                 var params = {};
-                params.ssub_ids = [ssub_id];
+                params.ssub_ids = ssub_ids;
                 obj.editor_init('ssub', 'edit', params);
             }
         } catch(E) {
@@ -1147,13 +1218,13 @@
         }
     },
 
-    'on_select_sdist' : function(sdist_id,twisty) {
+    'on_click_sdist' : function(sdist_ids,twisty) {
         var obj = this;
         try {
             // draw sdist editor
             if (typeof twisty == 'undefined') {
                 var params = {};
-                params.sdist_ids = [sdist_id];
+                params.sdist_ids = sdist_ids;
                 obj.editor_init('sdist', 'edit', params);
             }
         } catch(E) {
@@ -1161,13 +1232,13 @@
         }
     },
 
-    'on_select_siss' : function(siss_id,twisty) {
+    'on_click_siss' : function(siss_ids,twisty) {
         var obj = this;
         try {
             // draw siss editor
             if (typeof twisty == 'undefined') {
                 var params = {};
-                params.siss_ids = [siss_id];
+                params.siss_ids = siss_ids;
                 obj.editor_init('siss', 'edit', params);
             }
         } catch(E) {
@@ -1175,13 +1246,13 @@
         }
     },
 
-    'on_select_scap' : function(scap_id,twisty) {
+    'on_click_scap' : function(scap_ids,twisty) {
         var obj = this;
         try {
             // draw scap editor
             if (typeof twisty == 'undefined') {
                 var params = {};
-                params.scap_ids = [scap_id];
+                params.scap_ids = scap_ids;
                 obj.editor_init('scap', 'edit', params);
             }
         } catch(E) {
@@ -1482,7 +1553,7 @@
                     'id' : 'tree_location',
                     'label' : document.getElementById('catStrings').getString('staff.cat.copy_browser.list_init.tree_location'),
                     'flex' : 1, 'primary' : true, 'hidden' : false, 
-                    'render' : function(my) { return my.sdist ? my.sdist.label() : my.siss ? my.siss.label() : my.scap ? 'C/P #:' + my.scap.id() : my.ssub ? 'Subscription #:' + my.ssub.id() : my.aou ? my.aou.shortname() + " : " + my.aou.name() : my.label ? my.label : "???"; },
+                    'render' : function(my) { return my.sdist ? my.sdist.label() : my.siss ? my.siss.label() : my.scap ? 'C/P : #' + my.scap.id() : my.ssub ? 'Subscription : #' + my.ssub.id() : my.aou ? my.aou.shortname() + " : " + my.aou.name() : my.label ? my.label : "???"; },
                 },
                 {
                     'id' : 'subscription_count',
@@ -1523,7 +1594,8 @@
                         netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect UniversalBrowserRead');
                         var row = {}; var col = {}; var nobj = {};
                         obj.list.node.treeBoxObject.getCellAt(ev.clientX,ev.clientY,row,col,nobj); 
-                        if ((row.value == -1)||(nobj.value != 'twisty')) { return; }
+                        if ((row.value == -1)||(nobj.value != 'twisty')) { return; } // on_click runs for twistys only
+
                         var node = obj.list.node.contentView.getItemAtIndex(row.value);
                         var list = [ node.getAttribute('retrieve_id') ];
                         if (typeof obj.on_select == 'function') {
@@ -1535,6 +1607,11 @@
                     },
                     'on_select' : function(ev) {
                         JSAN.use('util.functional');
+                        
+                        // get the actual node clicked to determine which editor to use
+                        var node = obj.list.node.contentView.getItemAtIndex(obj.list.node.view.selection.currentIndex);
+                        obj.focused_node_retrieve_id = node.getAttribute('retrieve_id');
+
                         var sel = obj.list.retrieve_selection();
                         obj.controller.view.sel_clip.disabled = sel.length < 1;
                         obj.sel_list = util.functional.map_list(
@@ -1585,6 +1662,7 @@
             obj.controller.view.cmd_add_sdist.setAttribute('disabled','true');
             obj.controller.view.cmd_add_siss.setAttribute('disabled','true');
             obj.controller.view.cmd_add_scap.setAttribute('disabled','true');
+            obj.controller.view.cmd_make_predictions.setAttribute('disabled','true');
             obj.controller.view.cmd_delete_sdist.setAttribute('disabled','true');
             obj.controller.view.cmd_delete_siss.setAttribute('disabled','true');
             obj.controller.view.cmd_delete_scap.setAttribute('disabled','true');
@@ -1605,6 +1683,7 @@
                 obj.controller.view.cmd_add_siss.setAttribute('disabled','false');
                 obj.controller.view.cmd_add_scap.setAttribute('disabled','false');
                 obj.controller.view.cmd_transfer_subscription.setAttribute('disabled','false');
+                obj.controller.view.cmd_make_predictions.setAttribute('disabled','false');
             }
             if (found_sdist_group) {
                 obj.controller.view.cmd_add_sdist.setAttribute('disabled','false');

Modified: branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul
===================================================================
--- branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul	2010-07-18 14:44:18 UTC (rev 16973)
+++ branches/seials-integration/Open-ILS/xul/staff_client/server/serial/manage_subs.xul	2010-07-18 21:55:37 UTC (rev 16974)
@@ -55,6 +55,7 @@
                         <menuitem command="cmd_add_sdist" label="Add Distribution"/>
                         <menuitem command="cmd_add_siss" label="Add Issuance"/>
                         <menuitem command="cmd_add_scap" label="Add Caption/Pattern"/>
+                        <menuitem command="cmd_make_predictions" label="Make Predictions"/>
                         <menuitem command="cmd_delete_ssub" label="Delete Subscription"/>
                         <menuitem command="cmd_delete_sdist" label="Delete Distribution"/>
                         <menuitem command="cmd_delete_siss" label="Delete Issuance"/>
@@ -77,6 +78,7 @@
                                         <menuitem command="cmd_add_sdist" label="Add Distribution"/>
                                         <menuitem command="cmd_add_siss" label="Add Issuance"/>
                                         <menuitem command="cmd_add_scap" label="Add Caption/Pattern"/>
+                                        <menuitem command="cmd_make_predictions" label="Make Predictions"/>
                                         <menuitem command="cmd_delete_ssub" label="Delete Subscription"/>
                                         <menuitem command="cmd_delete_sdist" label="Delete Distribution"/>
                                         <menuitem command="cmd_delete_siss" label="Delete Issuance"/>

Modified: branches/seials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul
===================================================================
--- branches/seials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul	2010-07-18 14:44:18 UTC (rev 16973)
+++ branches/seials-integration/Open-ILS/xul/staff_client/server/serial/serctrl_main.xul	2010-07-18 21:55:37 UTC (rev 16974)
@@ -59,9 +59,10 @@
 		<command id="cmd_delete_sdist"/>
 		<command id="cmd_delete_siss"/>
         <command id="cmd_delete_ssub"/>
+        <command id="cmd_make_predictions"/>
         <command id="cmd_mark_library"/>
         <command id="cmd_mark_subscription"/>
-		<command id="cmd_predict_items"/>
+		<!--<command id="cmd_predict_items"/>-->
 		<command id="cmd_print_spine_labels"/>
 		<command id="cmd_receive_items"/>
 		<command id="cmd_refresh_list"/>



More information about the open-ils-commits mailing list