Merge branch 'master' of git.evergreen-ils.org:Evergreen into template-toolkit-opac
authorBill Erickson <berick@esilibrary.com>
Wed, 20 Jul 2011 18:19:41 +0000 (14:19 -0400)
committerBill Erickson <berick@esilibrary.com>
Wed, 20 Jul 2011 18:19:41 +0000 (14:19 -0400)
14 files changed:
Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD/Holding.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD/test/mfhd.t
Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD/test/mfhddata2.txt [new file with mode: 0644]
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/upgrade/0583.schema.aging_circ_view.sql [new file with mode: 0644]
Open-ILS/web/opac/locale/en-US/lang.dtd
Open-ILS/xul/staff_client/Makefile.am
Open-ILS/xul/staff_client/server/circ/checkout.js
Open-ILS/xul/staff_client/server/circ/copy_status.js
Open-ILS/xul/staff_client/server/circ/copy_status.xul
Open-ILS/xul/staff_client/server/circ/copy_status_overlay.xul
Open-ILS/xul/staff_client/server/patron/display.js

index c3dbb21..17389fa 100644 (file)
@@ -85,7 +85,7 @@ sub new {
 
         my $card = $e->search_actor_card([{barcode => $patron_id}, $usr_flesh])->[0];
 
-        if(!$card) {
+        if(!$card or !$U->is_true($card->active)) {
             syslog("LOG_WARNING", "No such patron barcode: $patron_id");
             return undef;
         }
@@ -96,11 +96,16 @@ sub new {
            $user = $e->retrieve_actor_user([$patron_id, $usr_flesh]);
     }
 
-    if(!$user) {
+    if(!$user or $U->is_true($user->deleted)) {
         syslog("LOG_WARNING", "OILS: Unable to find patron %s => %s", $key, $patron_id);
         return undef;
     }
 
+    if(!$U->is_true($user->active)) {
+        syslog("LOG_WARNING", "OILS: Patron is inactive %s => %s", $key, $patron_id);
+        return undef;
+    }
+
     # now grab the user's penalties
 
     $self->flesh_user_penalties($user, $e) unless $args{slim_user};
index 5b728c0..6964c56 100644 (file)
@@ -252,6 +252,16 @@ sub holdings {
       values %{$self->{_mfhd_HOLDINGS}->{$field}->{$capid}};
 }
 
+sub holdings_by_caption {
+    my $self  = shift;
+    my $caption = shift;
+
+    my $htag    = $caption->tag;
+    my $link_id = $caption->link_id;
+    $htag =~ s/^85/86/;
+    return $self->holdings($htag, $link_id);
+}
+
 sub _holding_date {
     my $self = shift;
     my $holding = shift;
@@ -348,32 +358,44 @@ sub get_compressed_holdings {
     my $opts = shift;
     my $skip_sort = $opts->{'skip_sort'};
 
-    # make sure none are compressed
+    # make sure none are compressed (except for open-ended)
     my @decomp_holdings;
     if ($skip_sort) {
-        @decomp_holdings = $self->get_decompressed_holdings($caption, {'skip_sort' => 1});
+        @decomp_holdings = $self->get_decompressed_holdings($caption, {'skip_sort' => 1, 'passthru_open_ended' => 1});
     } else {
         # sort for best algorithm
-        @decomp_holdings = $self->get_decompressed_holdings($caption, {'dedupe' => 1});
+        @decomp_holdings = $self->get_decompressed_holdings($caption, {'dedupe' => 1, 'passthru_open_ended' => 1});
     }
 
     return () if !@decomp_holdings;
 
+    # if first holding is open-ended, it 'includes' all the rest, so return
+    if ($decomp_holdings[0]->is_open_ended) {
+        return ($decomp_holdings[0]);
+    }
+
     my $runner = $decomp_holdings[0]->clone->increment;   
     my $curr_holding = shift(@decomp_holdings);
     $curr_holding = $curr_holding->clone;
     my $seqno = 1;
     $curr_holding->seqno($seqno);
     my @comp_holdings;
-#    my $last_holding;
     foreach my $holding (@decomp_holdings) {
         if ($runner eq $holding) {
             $curr_holding->extend;
             $runner->increment;
-#        } elsif ($holding eq $last_holding) {
-#            carp("Found duplicate holding in compression set, skipping");
         } elsif ($runner gt $holding) { # should not happen unless holding is not in series
             carp("Found unexpected holding, skipping");
+        } elsif ($holding->is_open_ended) { # special case, as it will always be the last
+            if ($runner eq $holding->clone->compressed_to_first) {
+                $curr_holding->compressed_end();
+            } else {
+                push(@comp_holdings, $curr_holding);
+                $curr_holding = $holding->clone;
+                $seqno++;
+                $curr_holding->seqno($seqno);
+            }
+            last;
         } else {
             push(@comp_holdings, $curr_holding);
             while ($runner le $holding) {
@@ -383,7 +405,6 @@ sub get_compressed_holdings {
             $seqno++;
             $curr_holding->seqno($seqno);
         }
-#        $last_holding = $holding;
     }
     push(@comp_holdings, $curr_holding);
 
@@ -394,10 +415,12 @@ sub get_compressed_holdings {
 # create an array of single holdings from all holdings for a given caption,
 # decompressing as needed
 #
-# resulting array is returned as they come in the record, unsorted
-#
-# optional argument will reorder and renumber the holdings before returning
-# 
+# optional arguments:
+#    skip_sort: do not sort the returned holdings
+#    dedupe: remove any duplicate holdings from the set
+#    passthru_open_ended: open-ended compressed holdings cannot be logically
+#    decompressed (they are infinite); if set to true these holdings are passed
+#    thru rather than skipped
 # TODO: some of this could be moved to the Caption (and/or Holding) object to
 # allow for decompression in the absense of an overarching MFHD object
 #
@@ -407,15 +430,13 @@ sub get_decompressed_holdings {
     my $opts = shift;
     my $skip_sort = $opts->{'skip_sort'};
     my $dedupe = $opts->{'dedupe'};
+    my $passthru_open_ended = $opts->{'passthru_open_ended'};
 
     if ($dedupe and $skip_sort) {
         carp("Attempted deduplication without sorting, failure likely");
     }
 
-    my $htag    = $caption->tag;
-    my $link_id = $caption->link_id;
-    $htag =~ s/^85/86/;
-    my @holdings = $self->holdings($htag, $link_id);
+    my @holdings = $self->holdings_by_caption($caption);
 
     return () if !@holdings;
 
@@ -424,6 +445,12 @@ sub get_decompressed_holdings {
     foreach my $holding (@holdings) {
         if (!$holding->is_compressed) {
             push(@decomp_holdings, $holding->clone);
+        } elsif ($holding->is_open_ended) {
+            if ($passthru_open_ended) {
+                push(@decomp_holdings, $holding->clone);
+            } else {
+                carp("Open-ended holdings cannot be decompressed, skipping");
+            }
         } else {
             my $base_holding = $holding->clone->compressed_to_first;
             my @new_holdings = $self->generate_predictions(
@@ -454,6 +481,54 @@ sub get_decompressed_holdings {
     return @return_holdings;
 }
 
+##
+## close any open-ended holdings which are followed by another holding by
+## combining them
+##
+## This needs more thought about concerning usability (e.g. should it be a
+## mutator?), commenting out for now
+#sub _get_truncated_holdings {
+#    my $self = shift;
+#    my $caption = shift;
+#
+#    my @holdings = $self->holdings_by_caption($caption);
+#
+#    return () if !@holdings;
+#
+#    @holdings = sort {$a cmp $b} @holdings;
+#    
+#    my $current_open_holding;
+#    my @truncated_holdings;
+#    foreach my $holding (@holdings) {
+#        if ($current_open_holding) {
+#            if ($holding->is_open_ended) {
+#                next; # consecutive open holdings are meaningless, as they are contained by the previous
+#            } elsif ($holding->is_compressed) {
+#                $current_open_holding->compressed_end($holding->compressed_to_last);
+#            } else {
+#                $current_open_holding->compressed_end($holding);
+#            }
+#            push(@truncated_holdings, $current_open_holding);
+#            $current_open_holding = undef;
+#        } elsif ($holding->is_open_ended) {
+#            $current_open_holding = $holding;
+#        } else {
+#            push(@truncated_holdings, $holding);
+#        }
+#    }
+#    
+#    # catch possible open holding at end
+#    push(@truncated_holdings, $current_open_holding) if $current_open_holding;
+#
+#    my $seqno = 1;
+#    foreach my $holding (@truncated_holdings) { # renumber sequence
+#        $holding->seqno($seqno);
+#        $seqno++;
+#    }
+#
+#    return @truncated_holdings;
+#}
+
 #
 # format_holdings(): Generate textual display of all holdings in record
 # for given type of caption (853--855) taking into account all the
index ce08d4f..9b673c2 100644 (file)
@@ -44,7 +44,10 @@ sub new {
                 next;
             }
             if ($self->{_mfhdh_COMPRESSED}) {
-                $self->{_mfhdh_FIELDS}->{$key}{HOLDINGS} = [split(/\-/, $val)];
+                $self->{_mfhdh_FIELDS}->{$key}{HOLDINGS} = [split(/\-/, $val, -1)];
+                if (!defined($self->{_mfhdh_FIELDS}->{$key}{HOLDINGS}[1])) {
+                    $self->{_mfhdh_FIELDS}->{$key}{HOLDINGS}[1] = $self->{_mfhdh_FIELDS}->{$key}{HOLDINGS}[0];
+                }
             } else {
                 $self->{_mfhdh_FIELDS}->{$key}{HOLDINGS} = [$val];
             }
@@ -561,7 +564,7 @@ sub compressed_to_last {
         return $self;
     } elsif ($self->is_open_ended) {
         carp "Holding is open-ended, cannot convert to last member";
-        return $self;
+        return undef;
     }
 
     my %changes;
@@ -578,6 +581,40 @@ sub compressed_to_last {
 }
 
 #
+# Creates or replaces an end of a compressed holding
+#
+sub compressed_end {
+    my $self = shift;
+    my $end_holding = shift;
+
+    my %changes;
+    if ($end_holding) {
+        foreach my $key (keys %{$self->fields}) {
+            my @values = @{$self->field_values($key)};
+            my @end_values = @{$end_holding->field_values($key)};
+            $values[1] = $end_values[0];
+            $self->fields->{$key}{HOLDINGS} = \@values;
+            $changes{$key} = join('-', @values);
+        }
+    } elsif (!$self->is_open_ended) { # make open-ended if no $end_holding
+        foreach my $key (keys %{$self->fields}) {
+            my @values = @{$self->field_values($key)};
+            $self->fields->{$key}{HOLDINGS} = [$values[0]];
+            $changes{$key} = $values[0] . '-';
+        }
+        $self->{_mfhdh_OPEN_ENDED} = 1; #TODO: setter for this value
+    }
+
+    $self->update(%changes);    # update underlying subfields
+
+    if (!$self->is_compressed) {
+        $self->is_compressed(1);  # add compressed state
+    }
+
+    return $self;
+}
+
+#
 # Basic, working, unoptimized clone operation
 #
 sub clone {
@@ -707,17 +744,23 @@ sub _uncombine {
 # Please note that this comparison is based on what the holding represents,
 # not whether it is strictly identical (e.g. the seqno and link may vary)
 #
+# XXX: sorting using this operator is currently not deterministic for
+# nonsensical holdings (e.g. V.10-V.5), and may require further consideration
 use overload ('cmp' => \&_compare,
               'fallback' => 1);
 sub _compare {
-    my ($holding_1, $holding_2) = @_;
+    my ($holding_1, $holding_2, $swap) = @_;
 
     # TODO: this needs some more consideration
     # fall back to 'built-in' comparison
     if (!UNIVERSAL::isa($holding_2, ref $holding_1)) {
         if (defined $holding_2) {
-            carp("Use of non-holding in holding comparison operation");
-            return ( "$holding_1" cmp "$holding_2" );
+            carp("Use of non-holding in holding comparison operation") if $holding_2 ne '~~~';
+            if ($swap) {
+                return ( "$holding_2" cmp "$holding_1" );
+            } else {
+                return ( "$holding_1" cmp "$holding_2" );
+            }
         } else {
             carp("Use of undefined value in holding comparison operation");
             return 1; # similar to built-in, something is "greater than" nothing
@@ -729,7 +772,11 @@ sub _compare {
     # 0 for no compressed, 1 for first compressed, 2 for second compressed, 3 for both compressed
     $found_compressed = 0; 
     if ($holding_1->is_compressed) {
-        $holding_1_last = $holding_1->clone->compressed_to_last;
+        if (!$holding_1->is_open_ended) {
+            $holding_1_last = $holding_1->clone->compressed_to_last;
+        } else {
+            $holding_1_last = '~~~'; # take advantage of string sort fallback
+        }
         $found_compressed += 1;
     } else {
         $holding_1_first = $holding_1;
@@ -753,7 +800,11 @@ sub _compare {
         } else { # check the opposite, 2 ends before 1 starts
             # clone is expensive, wait until we need it (here)
             if (!defined($holding_2_last)) {
-                $holding_2_last = $holding_2->clone->compressed_to_last;
+                if (!$holding_2->is_open_ended) {
+                    $holding_2_last = $holding_2->clone->compressed_to_last;
+                } else {
+                    $holding_2_last = '~~~'; # take advantage of string sort fallback
+                }
             }
             if (!defined($holding_1_first)) {
                 $holding_1_first = $holding_1->clone->compressed_to_first;
@@ -766,7 +817,7 @@ sub _compare {
                 return 1;
             } else {
                 $cmp = ($holding_1_first cmp $holding_2_first);
-                if (!$cmp) { # they are not equal
+                if ($cmp) { # they are not equal
                     carp("Overlapping holdings in comparison, lt and gt based on start value only");
                     return $cmp;
                 } elsif ($found_compressed == 1) {
@@ -777,7 +828,7 @@ sub _compare {
                     return -1; # compressed (second holding) is 'greater than' non-compressed
                 } else { # both holdings compressed, check for full equality
                     $cmp = ($holding_1_last cmp $holding_2_last);
-                    if (!$cmp) { # they are not equal
+                    if ($cmp) { # they are not equal
                         carp("Compressed holdings in comparison have equal starts, lt and gt based on end value only");
                         return $cmp;
                     } else {
index a478b32..d9ad34b 100644 (file)
@@ -63,4 +63,67 @@ while ($rec = testlib::load_MARC_rec($testdata, $testno++)) {
     }
 }
 
+close $testdata;
+
+# test: passthru_open_ended
+open($testdata, "<mfhddata2.txt") or die("Cannot open 'mfhddata2.txt': $!");
+
+$rec = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+my $rec2 = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+
+my @holdings_a = $rec->get_decompressed_holdings(($rec->captions('853'))[0], {'passthru_open_ended' => 1});
+my @holdings_b = $rec2->holdings_by_caption(($rec2->captions('853'))[0]);
+
+is_deeply(\@holdings_a, \@holdings_b, 'passthru open ended');
+
+# test: compressed to last
+$testno++;
+
+$rec = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+$rec2 = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+
+@holdings_a = $rec->holdings_by_caption(($rec->captions('853'))[0]);
+@holdings_b = $rec2->holdings_by_caption(($rec2->captions('853'))[0]);
+
+is_deeply($holdings_a[0]->compressed_to_last, $holdings_b[0], 'compressed to last, normal');
+is($holdings_a[1]->compressed_to_last, undef, 'compressed to last, open ended');
+
+# test: get compressed holdings, open ended member
+$testno++;
+
+$rec = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+$rec2 = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+
+@holdings_a = $rec->get_compressed_holdings(($rec->captions('853'))[0]);
+@holdings_b = $rec2->holdings_by_caption(($rec2->captions('853'))[0]);
+
+is_deeply(\@holdings_a, \@holdings_b, 'get compressed holdings, open ended member');
+
+# test comparisons, for all operands, for all types of holdings
+$testno++;
+
+$rec = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+$rec2 = MFHD->new(testlib::load_MARC_rec($testdata, $testno));
+
+@holdings_a = $rec->holdings_by_caption(($rec->captions('853'))[0]);
+@holdings_b = $rec2->holdings_by_caption(($rec2->captions('853'))[0]);
+
+unshift(@holdings_a, "zzz I am NOT a holding");
+push(@holdings_b, "zzz I am NOT a holding");
+
+push(@holdings_a, undef);
+unshift(@holdings_b, undef);
+
+@holdings_a = sort { $a cmp $b } @holdings_a;
+my $seqno = 1;
+foreach my $holding (@holdings_a) {
+    if (ref $holding) {
+        $holding->seqno($seqno);
+        $seqno++;
+    }
+}
+
+is_deeply(\@holdings_a, \@holdings_b, 'comparison testing via sort');
+
+close $testdata;
 1;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD/test/mfhddata2.txt b/Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD/test/mfhddata2.txt
new file mode 100644 (file)
index 0000000..a450bb4
--- /dev/null
@@ -0,0 +1,57 @@
+245 00 $aPassthru Open Ended, test data
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 40 $81.1$a1$b6-8$i1990$j06-08
+863 40 $81.2$a1-$b11-$i1990-$j11-
+
+245 00 $aPassthru Open Ended, test results
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 41 $81.1$a1$b6$i1990$j06
+863 41 $81.2$a1$b7$i1990$j07
+863 41 $81.3$a1$b8$i1990$j08
+863 40 $81.4$a1-$b11-$i1990-$j11-
+
+245 00 $aCompressed to last, part 1
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 40 $81.1$a1$b6-8$i1990$j06-08
+863 40 $81.2$a1-$b11-$i1990-$j11-
+
+245 00 $aCompressed to last, part 2
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 41 $81.1$a1$b8$i1990$j08
+
+245 00 $aGet compressed holdings, open ended member
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 41 $81.1$a1$b4$i1990$j04
+863 41 $81.2$a1$b5$i1990$j05
+863 40 $81.3$a1-$b6-$i1990-$j06-
+863 41 $81.4$a1$b8$i1990$j08
+
+245 00 $aGet compressed holdings, open ended member, result
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 40 $81.1$a1-$b4-$i1990-$j04-
+
+245 00 $aComparison test, unsorted
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 40 $81.1$a1$b4-7$i1990$j04-07
+863 40 $81.2$a1$b4-7$i1990$j04-07
+863 41 $81.3$a1$b4$i1990$j04
+863 41 $81.4$a1$b4$i1990$j04
+863 40 $81.5$a1-$b3-$i1990-$j03-
+863 40 $81.6$a1$b3-6$i1990$j03-06
+863 40 $81.7$a1$b3-5$i1990$j03-05
+863 41 $81.8$a1$b3$i1990$j03
+863 41 $81.9$a1$b2$i1990$j02
+
+245 00 $aComparison test, sorted
+853 20 $81$av.$bno.$u12$vr$i(year)$j(month)$wm$x01
+863 41 $81.1$a1$b2$i1990$j02
+863 41 $81.2$a1$b3$i1990$j03
+863 40 $81.3$a1$b3-5$i1990$j03-05
+863 40 $81.4$a1$b3-6$i1990$j03-06
+863 40 $81.5$a1-$b3-$i1990-$j03-
+863 41 $81.6$a1$b4$i1990$j04
+863 41 $81.7$a1$b4$i1990$j04
+863 40 $81.8$a1$b4-7$i1990$j04-07
+863 40 $81.9$a1$b4-7$i1990$j04-07
+
+
index 00ab333..175a63c 100644 (file)
@@ -86,7 +86,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0582', :eg_version); -- miker
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0583', :eg_version); -- miker/gmcharlt
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/0583.schema.aging_circ_view.sql b/Open-ILS/src/sql/Pg/upgrade/0583.schema.aging_circ_view.sql
new file mode 100644 (file)
index 0000000..3a1e03f
--- /dev/null
@@ -0,0 +1,38 @@
+-- Evergreen DB patch 0583.schema.aging_circ_view.sql
+--
+-- This implements the same update as 0582.schema.aging_circ_view.sql,
+-- and exists purely the the sake of avoid a potential upgrade
+-- glitch for somebody who's upgrading from 2.0.8+.
+--
+--
+BEGIN;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0583', :eg_version);
+
+CREATE OR REPLACE VIEW action.all_circulation AS
+    SELECT  id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
+        copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
+        circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, grace_period, due_date,
+        stop_fines_time, checkin_time, create_time, duration, fine_interval, recurring_fine,
+        max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recurring_fine_rule,
+        max_fine_rule, stop_fines, workstation, checkin_workstation, checkin_scan_time, parent_circ
+      FROM  action.aged_circulation
+            UNION ALL
+    SELECT  DISTINCT circ.id,COALESCE(a.post_code,b.post_code) AS usr_post_code, p.home_ou AS usr_home_ou, p.profile AS usr_profile, EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
+        cp.call_number AS copy_call_number, cp.location AS copy_location, cn.owning_lib AS copy_owning_lib, cp.circ_lib AS copy_circ_lib,
+        cn.record AS copy_bib_record, circ.xact_start, circ.xact_finish, circ.target_copy, circ.circ_lib, circ.circ_staff, circ.checkin_staff,
+        circ.checkin_lib, circ.renewal_remaining, circ.grace_period, circ.due_date, circ.stop_fines_time, circ.checkin_time, circ.create_time, circ.duration,
+        circ.fine_interval, circ.recurring_fine, circ.max_fine, circ.phone_renewal, circ.desk_renewal, circ.opac_renewal, circ.duration_rule,
+        circ.recurring_fine_rule, circ.max_fine_rule, circ.stop_fines, circ.workstation, circ.checkin_workstation, circ.checkin_scan_time,
+        circ.parent_circ
+      FROM  action.circulation circ
+        JOIN asset.copy cp ON (circ.target_copy = cp.id)
+        JOIN asset.call_number cn ON (cp.call_number = cn.id)
+        JOIN actor.usr p ON (circ.usr = p.id)
+        LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
+        LEFT JOIN actor.usr_address b ON (p.billing_address = b.id);
+
+
+
+COMMIT;
index 710abd0..ce4252e 100644 (file)
 <!ENTITY staff.circ.copy_status_overlay.cmd_find_acq_po.accesskey "F">
 <!ENTITY staff.circ.copy_status_overlay.sel_edit.label "Edit Item Attributes">
 <!ENTITY staff.circ.copy_status_overlay.sel_edit.accesskey "E">
+<!ENTITY staff.circ.copy_status_overlay.sel_vol_copy_edit.label "Edit Items/Volumes Per Bib">
+<!ENTITY staff.circ.copy_status_overlay.sel_vol_copy_edit.accesskey "V">
 <!ENTITY staff.circ.copy_status_overlay.sel_mark_items_damaged.label "Mark Item Damaged">
 <!ENTITY staff.circ.copy_status_overlay.sel_mark_items_damaged.accesskey "D">
 <!ENTITY staff.circ.copy_status_overlay.sel_mark_items_missing.label "Mark Item Missing">
index 51a61c0..dab3b2f 100644 (file)
@@ -9,7 +9,7 @@ export STAFF_CLIENT_STAMP_ID = $$(/bin/cat build/STAMP_ID)
 
 # from http://closure-compiler.googlecode.com/files/compiler-latest.zip  FIXME: Autotools this?
 export CLOSURE_COMPILER_JAR = ~/closure-compiler/compiler.jar
-XULRUNNER_VERSION=1.9.2.18
+XULRUNNER_VERSION=1.9.2.19
 XULRUNNER_WINFILE=xulrunner-$(XULRUNNER_VERSION).en-US.win32.zip
 XULRUNNER_LINUXFILE=xulrunner-$(XULRUNNER_VERSION).en-US.linux-i686.tar.bz2
 XULRUNNER_URL=http://releases.mozilla.org/pub/mozilla.org/xulrunner/releases/$(XULRUNNER_VERSION)/runtimes/
index c7d3813..11f5373 100644 (file)
@@ -762,7 +762,7 @@ circ.checkout.prototype = {
 
                 for (var i = 0; i < test_permit.length; i++) {
                     dump('found [' + test_permit[i].ilsevent + ']\n');
-                    switch(test_permit[i].ilsevent == null ? null : Number(test_permit[i].ilsevent)) {
+                    switch(test_permit[i].ilsevent == null || test_permit[i].ilsevent == '' ? null : Number(test_permit[i].ilsevent)) {
                         case null /* custom event */ :
                             found_handled = true;
                         break;
index bfd7b79..f7ac6dd 100644 (file)
@@ -10,6 +10,8 @@ circ.copy_status = function (params) {
     JSAN.use('util.date');
     JSAN.use('OpenILS.data'); this.data = new OpenILS.data(); this.data.init({'via':'stash'});
     JSAN.use('util.sound'); this.sound = new util.sound();
+    JSAN.use('cat.util');
+
 };
 
 circ.copy_status.prototype = {
@@ -57,6 +59,7 @@ circ.copy_status.prototype = {
                             obj.controller.view.sel_checkin.setAttribute('disabled','true');
                             obj.controller.view.cmd_replace_barcode.setAttribute('disabled','true');
                             obj.controller.view.sel_edit.setAttribute('disabled','true');
+                            obj.controller.view.sel_vol_copy_edit.setAttribute('disabled','true');
                             obj.controller.view.sel_opac.setAttribute('disabled','true');
                             obj.controller.view.sel_bucket.setAttribute('disabled','true');
                             obj.controller.view.sel_record_bucket.setAttribute('disabled','true');
@@ -86,6 +89,7 @@ circ.copy_status.prototype = {
                             obj.controller.view.sel_checkin.setAttribute('disabled','false');
                             obj.controller.view.cmd_replace_barcode.setAttribute('disabled','false');
                             obj.controller.view.sel_edit.setAttribute('disabled','false');
+                            obj.controller.view.sel_vol_copy_edit.setAttribute('disabled','false');
                             obj.controller.view.sel_opac.setAttribute('disabled','false');
                             obj.controller.view.sel_patron.setAttribute('disabled','false');
                             obj.controller.view.cmd_triggered_events.setAttribute('disabled','false');
@@ -741,6 +745,58 @@ circ.copy_status.prototype = {
                         }
 
                     ],
+
+                    'sel_vol_copy_edit' : [
+                        ['command'],
+                        function() {
+                            try {
+                                JSAN.use('util.functional');
+
+                                var list = util.functional.map_list( obj.selection_list, function(o) { return o.copy_id; } );
+
+                                var copies = obj.network.simple_request('FM_ACP_FLESHED_BATCH_RETRIEVE',[list]);
+
+                                if (list.length == 0) { return; }
+
+                                var map_acn = {};
+                                var rec_copy_map = {};
+
+                                for (var i = 0; i < copies.length; i++) {
+                                    var volume_id = copies[i].call_number();
+                                    if (! map_acn[volume_id]) {
+                                        map_acn[ volume_id ] = obj.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ volume_id ]);
+                                    }
+                                    copies[i].call_number( map_acn[ volume_id ] );
+                                    var record_id = map_acn[ volume_id ].record();
+                                    if (!rec_copy_map[record_id]) {
+                                        rec_copy_map[record_id] = [];
+                                    }
+                                    rec_copy_map[record_id].push( copies[i] );
+                                }
+
+                                var timeout = 0; // FIXME: stagger invocation of each tab or they'll break for someone unknown reason
+                                var vol_item_creator = function(items) {
+                                    setTimeout(
+                                        function() {
+                                            xulG.volume_item_creator({ 'existing_copies' : items });
+                                        }, timeout
+                                    );
+                                    timeout += 1000;
+                                }
+                                for (var r in rec_copy_map) {
+                                    if (r == -1) { /* no unified interface for pre-cats */ 
+                                        cat.util.spawn_copy_editor( { 'copy_ids' : rec_copy_map[r], 'edit' : 1 } );
+                                    } else {
+                                        vol_item_creator( rec_copy_map[r] );
+                                    }
+                                }
+
+                            } catch(E) {
+                                obj.error.standard_unexpected_error_alert('copy status -> edit items/volumes per bib',E);
+                            }
+                        }
+                    ],
+
                     'cmd_edit_volumes' : [
                         ['command'],
                         function() {
index 6a1411a..351b01b 100644 (file)
         <command id="sel_checkin" disabled="true"/>
         <command id="sel_renew" disabled="true"/>
         <command id="sel_edit" disabled="true"/>
+        <command id="sel_vol_copy_edit" disabled="true"/>
         <command id="sel_opac" disabled="true"/>
         <command id="sel_bucket" disabled="true"/>
         <command id="sel_record_bucket" disabled="true" label="&staff.circ.copy_status_overlay.sel_record_bucket.label;" accesskey="&staff.circ.copy_status_overlay.sel_record_bucket.accesskey;" />
index d8ffd77..909b547 100644 (file)
@@ -25,6 +25,7 @@
         <menuitem command="cmd_find_acq_po" label="&staff.circ.copy_status_overlay.cmd_find_acq_po.label;" accesskey="&staff.circ.copy_status_overlay.cmd_find_acq_po.accesskey;"/>
         <menuseparator/>
         <menuitem command="sel_edit" label="&staff.circ.copy_status_overlay.sel_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_edit.accesskey;" />
+        <menuitem command="sel_vol_copy_edit" label="&staff.circ.copy_status_overlay.sel_vol_copy_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_vol_copy_edit.accesskey;" />
         <menuseparator/>
         <menuitem command="sel_mark_items_damaged" label="&staff.circ.copy_status_overlay.sel_mark_items_damaged.label;" accesskey="&staff.circ.copy_status_overlay.sel_mark_items_damaged.accesskey;"/>
         <menuseparator/>
@@ -52,6 +53,7 @@
         <menuitem command="sel_patron" label="&staff.circ.copy_status_overlay.sel_patron.label;" accesskey="&staff.circ.copy_status_overlay.sel_patron.accesskey;"/>
         <menuseparator/>
         <menuitem command="sel_edit" label="&staff.circ.copy_status_overlay.sel_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_edit.accesskey;" />
+        <menuitem command="sel_vol_copy_edit" label="&staff.circ.copy_status_overlay.sel_vol_copy_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_vol_copy_edit.accesskey;" />
         <menuitem command="cmd_transfer_items" label="&staff.circ.copy_status_overlay.cmd_transfer_items.label;" accesskey="&staff.circ.copy_status_overlay.cmd_transfer_items.accesskey;"/>
         <menuseparator/>
         <menuitem command="cmd_add_volumes" label="&staff.circ.copy_status_overlay.cmd_add_volumes.label;" accesskey="&staff.circ.copy_status_overlay.cmd_add_volumes.accesskey;"/>
             <menuitem command="cmd_triggered_events" label="&staff.circ.copy_status_overlay.cmd_triggered_events.label;" accesskey="&staff.circ.copy_status_overlay.cmd_triggered_events.accesskey;"/>
             <menuseparator/>
             <menuitem command="sel_edit" label="&staff.circ.copy_status_overlay.sel_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_edit.accesskey;" />
+            <menuitem command="sel_vol_copy_edit" label="&staff.circ.copy_status_overlay.sel_vol_copy_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_vol_copy_edit.accesskey;" />
             <menuitem command="cmd_transfer_items" label="&staff.circ.copy_status_overlay.cmd_transfer_items.label;" accesskey="&staff.circ.copy_status_overlay.cmd_transfer_items.accesskey;"/>
             <menuseparator/>
             <menuitem command="cmd_add_volumes" label="&staff.circ.copy_status_overlay.cmd_add_volumes.label;" accesskey="&staff.circ.copy_status_overlay.cmd_add_volumes.accesskey;"/>
             <menuitem command="cmd_find_acq_po" label="&staff.circ.copy_status_overlay.cmd_find_acq_po.label;" accesskey="&staff.circ.copy_status_overlay.cmd_find_acq_po.accesskey;"/>
             <menuseparator/>
             <menuitem command="sel_edit" label="&staff.circ.copy_status_overlay.sel_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_edit.accesskey;" />
+            <menuitem command="sel_vol_copy_edit" label="&staff.circ.copy_status_overlay.sel_vol_copy_edit.label;" accesskey="&staff.circ.copy_status_overlay.sel_vol_copy_edit.accesskey;" />
             <menuseparator />
             <menuitem command="sel_mark_items_damaged" label="&staff.circ.copy_status_overlay.sel_mark_items_damaged.label;" accesskey="&staff.circ.copy_status_overlay.sel_mark_items_damaged.accesskey;"/>
             <menuseparator />
index 7840825..9e4ebfa 100644 (file)
@@ -954,7 +954,7 @@ patron.display.prototype = {
                         msg += '<dt>';
                         msg += obj.OpenILS.data.hash.aou[ penalties[i].org_unit() ].shortname() + ' : ' + penalties[i].standing_penalty().label() + '<br/>';
                         msg += '</dt><dd>';
-                        msg += penalties[i].note();
+                        msg += (penalties[i].note())?penalties[i].note():'';
                         msg += '</dd>';
                     }
                 }