Merge branch 'master' of git+ssh://yeti.esilibrary.com/home/evergreen/evergreen-equin...
authorMike Rylander <mrylander@gmail.com>
Fri, 13 May 2011 17:20:12 +0000 (13:20 -0400)
committerMike Rylander <mrylander@gmail.com>
Fri, 13 May 2011 17:20:12 +0000 (13:20 -0400)
Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD/Caption.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/MFHD/Date.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/mfhddata.txt
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/upgrade/0532.schema.fix_copy_count_funcs.sql [new file with mode: 0644]

index 55e6df9..9445834 100644 (file)
@@ -101,17 +101,6 @@ sub new {
 
     my $pat = $self->{_mfhdc_PATTERN};
 
-    # Sanity check publication frequency vs publication pattern:
-    # if the frequency is a number, then the pattern better
-    # have that number of values associated with it.
-    if (   exists($pat->{w})
-        && ($pat->{w} =~ /^\d+$/)
-        && ($pat->{w} != scalar(@{$pat->{y}->{p}}))) {
-        carp(
-"Caption::new: publication frequency '$pat->{w}' != publication pattern @{$pat->{y}->{p}}"
-        );
-    }
-
     # If there's a $x subfield and a $j, then it's compressible
     if (exists $pat->{x} && exists $self->{_mfhdc_CHRONS}->{'j'}) {
         $self->{_mfhdc_COMPRESSIBLE} = 1;
@@ -397,14 +386,15 @@ sub next_chron {
     my $freq    = $pattern->{w};
 
     foreach my $i (0..$#keys) {
-        $cur[$i] = $next->{$keys[$i]} if exists $next->{$keys[$i]};
+        if (exists $next->{$keys[$i]}) {
+            $cur[$i] = $next->{$keys[$i]};
+            # If the current issue has a combined date (eg, May/June)
+            # get rid of the first date and base the calculation
+            # on the final date in the combined issue.
+            $cur[$i] =~ s|^[^/]+/||;
+        }
     }
 
-    # If the current issue has a combined date (eg, May/June)
-    # get rid of the first date and base the calculation
-    # on the final date in the combined issue.
-    $cur[-1] =~ s|^[^/]+/||;
-
     if (defined $pattern->{y}->{p}) {
         # There is a $y publication pattern defined in the record:
         # use it to calculate the next issue date.
@@ -434,12 +424,12 @@ sub next_chron {
                     ($start, $end) = (undef, undef);
                 }
 
-                @candidate = $genfunc->($start || $pat, @cur);
+                @candidate = $genfunc->($start || $pat, \@cur, $self);
 
                 while ($self->is_omitted(@candidate)) {
                     #              printf("# pubpat omitting date '%s'\n",
                     #                     join('/', @candidate));
-                    @candidate = $genfunc->($start || $pat, @candidate);
+                    @candidate = $genfunc->($start || $pat, \@candidate, $self);
                 }
 
                 #              printf("# testing new candidate '%s' against '%s'\n",
@@ -451,7 +441,7 @@ sub next_chron {
                     # @candidate is the next issue.
                     @new = @candidate;
                     if (defined $end) {
-                        @newend = $genfunc->($end, @cur);
+                        @newend = $genfunc->($end, \@cur, $self);
                     } else {
                         $newend[0] = undef;
                     }
@@ -461,6 +451,8 @@ sub next_chron {
             }
         }
 
+        $new[1] = 24 if ($new[1] == 20); # restore fake early winter
+
         if (defined($newend[0])) {
             # The best match was a combined issue
             foreach my $i (0..$#new) {
@@ -488,10 +480,11 @@ sub next_chron {
 
             if ($self->is_combined(@new)) {
                 my @second_date = MFHD::Date::incr_date($freq, @new);
-
-                # I am cheating: This code assumes that only the smallest
-                # time increment is combined. So, no "Apr 15/May 1" allowed.
-                $new[-1] = $new[-1] . '/' . $second_date[-1];
+                foreach my $i (0..$#new) {
+                    # don't combine identical fields
+                    next if $new[$i] eq $second_date[$i];
+                    $new[$i] .= '/' . $second_date[$i];
+                }
             }
         }
     }
@@ -499,18 +492,57 @@ sub next_chron {
     for my $i (0..$#new) {
         $next->{$keys[$i]} = $new[$i];
     }
+
     # Figure out if we need to adjust volume number
-    # right now just use the $carry that was passed in.
-    # in long run, need to base this on ($carry or date_change)
-    if ($carry) {
-        # if $carry is set, the date doesn't matter: we're not
-        # going to increment the v. number twice at year-change.
+    #
+    # If we are incrementing based on date, $carry doesn't
+    # matter: we're not going to increment the v. number twice
+    #
+    # It is conceivable that a serial could increment based on date for some
+    # volumes and issue numbering for other volumes, but until a real case
+    # comes up, let's assume that defined calendar changes always trump $u
+    if (defined $pattern->{x}) {
+        my $increment = $self->calendar_increment(\@cur, \@new);
+        # if we hit a calendar change, restart dependant restarters
+        # regardless of whether they thought they should
+        if ($increment) {
+            $next->{a} += $increment;
+            foreach my $key ('b'..'f') {
+                next if !exists $next->{$key};
+                my $cap = $self->capfield($key);
+                if ($cap->{RESTART}) {
+                    $next->{$key} = 1;
+                    if ($self->enum_is_combined($key, $next->{$key})) {
+                        $next->{$key} .= '/' . ($next->{$key} + 1);
+                    }
+                } else {
+                    last; # if we find a non-restarting level, stop
+                }
+            }
+        }
+    } elsif ($carry) {
         $next->{a} += $carry;
-    } elsif (defined $pattern->{x}) {
-        $next->{a} += $self->calendar_increment(\@cur, \@new);
     }
 }
 
+sub winter_starts_year {
+    my $self = shift;
+
+    my $pubpats = $self->{_mfhdc_PATTERN}->{y}->{p};
+    my $freq = $self->{_mfhdc_PATTERN}->{w};
+
+    if ($freq =~ /^\d$/) {
+        foreach my $pubpat (@$pubpats) {
+            my $chroncode = substr($pubpat, 0, 1);
+            if ($chroncode eq 's' and substr($pubpat, 1, 2) == 24) {
+                return 1;        
+            }
+        }
+    }
+    return 0;
+}
+
+
 sub next_alt_enum {
     my $self = shift;
     my $next = shift;
@@ -653,6 +685,15 @@ sub next_enum {
                 && ($next->{$key} eq $cap->{COUNT})) {
                 $next->{$key} = 1;
                 $carry = 1;
+            } elsif ($cap->{COUNT} > 0 and !($next->{$key} % $cap->{COUNT})) {
+                # If we have a non-restarting enum, but we define a count,
+                # we need to carry to the next level when the current value
+                # divides evenly by the count
+                # XXX: this code naively assumes that there has never been an
+                # issue number anomaly of any kind (like an extra issue), but this
+                # limit is inherent in the standard
+                $next->{$key} += 1;
+                $carry = 1;
             } else {
                 # If I don't need to "carry" beyond here, then I just increment
                 # this level of the enumeration and stop looping, since the
index 34c85d9..a41a6ac 100644 (file)
@@ -62,9 +62,13 @@ sub match_day {
     }
 }
 
+# TODO: possible support for extraneous $yp information
+# ex. $ypdtu but on a bi-weekly (currently assumes weekly)
 sub subsequent_day {
     my $pat = shift;
-    my @cur = @_;
+    my $cur = shift;
+
+    my @cur = @$cur;
     my $dt  = DateTime->new(
         year  => $cur[0],
         month => $cur[1],
@@ -280,7 +284,9 @@ sub match_week {
 #
 sub subsequent_week {
     my $pat = shift;
-    my @cur = @_;
+    my $cur = shift;
+
+    my @cur = @$cur;
     my $candidate;
     my $dt;
 
@@ -388,7 +394,9 @@ sub match_month {
 
 sub subsequent_month {
     my $pat = shift;
-    my @cur = @_;
+    my $cur = shift;
+
+    my @cur = @$cur;
 
     if ($cur[1] >= $pat) {
         # Current date is on or after the patter date, so the next
@@ -411,7 +419,10 @@ sub match_season {
 
 sub subsequent_season {
     my $pat = shift;
-    my @cur = @_;
+    my $cur = shift;
+    my $caption = shift;
+
+    my @cur = @$cur;
 
 #     printf("# subsequent_season: pat='%s', cur='%s'\n", $pat, join('/',@cur));
 
@@ -420,6 +431,15 @@ sub subsequent_season {
         return undef;
     }
 
+    if ($caption->winter_starts_year()) {
+        if ($pat == 24) {
+            $pat = 20; # fake early winter
+        }
+        if ($cur[1] == 24) {
+            $cur[1] = 20; # fake early winter
+        }
+    }
+
     if ($cur[1] >= $pat) {
         # current season is on or past pattern season in this year,
         # advance to next year
@@ -445,6 +465,8 @@ sub subsequent_year {
     my $pat = shift;
     my $cur = shift;
 
+    my @cur = @$cur;
+
     # XXX WRITE ME
     return undef;
 }
@@ -463,6 +485,8 @@ sub subsequent_issue {
     my $pat = shift;
     my $cur = shift;
 
+    my @cur = @$cur;
+
     # Issue generation is handled separately
     return undef;
 }
@@ -509,6 +533,7 @@ my %increments = (
     i => {days   => 2},     # three times / week
     j => {days   => 10},    # three times /month
                             # k => continuous
+#    l => {weeks  => 3},     # triweekly (NON-STANDARD)
     m => {months => 1},     # monthly
     q => {months => 3},     # quarterly
     s => {days   => 15},    # semimonthly
index efd6027..ce08d4f 100644 (file)
@@ -362,16 +362,19 @@ sub format_part {
     }
 
     # Breaks in the sequence
-    if (defined($self->{_mfhdh_BREAK})) {
-        if ($self->{_mfhdh_BREAK} eq 'n') {
-            $str .= ' non-gap break';
-        } elsif ($self->{_mfhdh_BREAK} eq 'g') {
-            $str .= ' gap';
-        } else {
-            warn "unrecognized break indicator '$self->{_mfhdh_BREAK}'";
-        }
-    }
-
+# XXX: this is non-standard and also not the right place for this, since gaps
+# only make sense in the context of multiple holding segments, not a single
+# holding
+#    if (defined($self->{_mfhdh_BREAK})) {
+#        if ($self->{_mfhdh_BREAK} eq 'n') {
+#            $str .= ' non-gap break';
+#        } elsif ($self->{_mfhdh_BREAK} eq 'g') {
+#            $str .= ' gap';
+#        } else {
+#            warn "unrecognized break indicator '$self->{_mfhdh_BREAK}'";
+#        }
+#    }
+#
     return $str;
 }
 
@@ -635,13 +638,28 @@ sub chron_to_date {
                     $chrons[$i]->[1] = 9;
                     $chrons[$i]->[2] = 22;
                 } elsif ($seasons[$i] == 24) {
-                    $chrons[$i]->[1] = 12;
-                    $chrons[$i]->[2] = 21;
+                    # "winter" can come at the beginning or end of a year,
+                    if ($self->caption->winter_starts_year()) {
+                        $chrons[$i]->[1] = 1;
+                        $chrons[$i]->[2] = 1;
+                    } else { # default to astronomical
+                        $chrons[$i]->[1] = 12;
+                        $chrons[$i]->[2] = 21;
+                    }
                 }
             }
         }
     }
 
+    # if we have an an annual, set the month to ypm## if available
+    if (exists($self->caption->{_mfhdc_PATTERN}->{y}->{p}) and $self->caption->{_mfhdc_PATTERN}->{w} eq 'a') {
+        my $reg = $self->caption->{_mfhdc_PATTERN}->{y}->{p}->[0];
+        if ($reg =~ /^m(\d+)/) {
+            $chrons[0]->[1] = $1;
+            $chrons[1]->[1] = $1;
+        }
+    }
+
     my @dates;
     foreach my $chron (@chrons) {
         my $date = undef;
index e6dcf44..a478b32 100644 (file)
@@ -54,6 +54,10 @@ while ($rec = testlib::load_MARC_rec($testdata, $testno++)) {
                   if ($field->subfield('z') =~ /^TODO/);
                 is_deeply($field->next, right_answer($field),
                     $field->subfield('8') . ': ' . $field->subfield('z'));
+
+                if ($field->subfield('y')) {
+                    is($field->chron_to_date(), $field->subfield('y'), 'Chron-to-date test');
+                }
             }
         }
     }
index e3b4e1e..883bf3c 100644 (file)
 863 41 $820.3$a1$b5$i1990$j12$x|a2|b1|i1991|j02$zWrap at end of year/vol.
 
 245 00 $aEconomist: pub. w on Sa, except combined iss on last two weeks of year
-853 20 $821$av.$bno.$u12$vc$i(year)$j(month)$k(day)$ww$x01,04,07,10$ypdsa$yow1299
+853 20 $821$av.$bno.$vc$i(year)$j(month)$k(day)$ww$x01,04,07,10$ypdsa$yow1299
 863 41 $821.1$a100$b1200$i2008$j12$k06$x|a100|b1201|i2008|j12|k13$zwithin vol.
 863 41 $821.2$a100$b1201$i2008$j12$k13$x|a100|b1202|i2008|j12|k20$zwithin vol. combined iss.
 863 41 $821.3$a100$b1202$i2008$j12$k20$x|a101|b1203|i2009|j01|k03$zvolume change over omitted iss.
 863 41 $822.4$a2$b4$i2013$j04$k11$x|a2|b5|i2013|j05|k01$zpublished on Wed May 1st
 
 245 00 $aMFHD example: pub. every Mon, Thu, except on New Years, July 4, Labor Day, Thanksgiving, Christmas
-853 20 $823$av.$bno.$uvar$vr$i(year)$j(month)$k(day)$wc$x07$ypw00mo,00th$yod0101,0704,1225$yow0901mo,1104th
+853 20 $823$av.$bno.$uvar$i(year)$j(month)$k(day)$wc$x07$ypw00mo,00th$yod0101,0704,1225$yow0901mo,1104th
 863 41 $823.1$a1$b100$i2009$j02$k02$x|a1|b101|i2009|j02|k05$znormal: Mon to Thu
 863 41 $823.2$a1$b101$i2009$j02$k05$x|a1|b102|i2009|j02|k09$znormal: Thu to Mon
 863 41 $823.3$a1$b150$i2009$j06$k29$x|a2|b151|i2009|j07|k02$znormal: calendar change
 863 41 $826.1$a1$b1$i1990$j01$x|a1|b2|i1990|j02$znormal issue
 864 41 $827.1$a1$i1990$j09$x|a2|i1991|j09$zAnnual supplement
 865 41 $828.1$a1$i1990$j02$x|a2|i1991|j02$zAnnual Index
+
+# Issue numbering restarts at the calendar change
+245 00 $aIssue No. restarts at calendar change
+853 20 $829$av.$bno.$uvar$vr$i(year)$j(month)$k(day)$wd$x0101,0701
+863 41 $829.1$a1$b181$i2011$j06$k30$x|a2|b1|i2011|j07|k01$zJune 30 to July 1
+
+# Winter starts the calendar year
+# Requires a hacky use of MARC due to limitations in MARC standard
+245 00 $aWinter starts the calendar year
+853 20 $830$av.$bno.$u4$vr$i(year)$j(season)$w4$yps24,21,22,23
+863 41 $830.1$a1$b4$i2010$j23$x|a2|b1|i2011|j24$zAutumn 2010 to Winter 2011
+863 41 $830.2$a2$b1$i2011$j24$x|a2|b2|i2011|j21$y2011-01-01$zWinter 2011 to Spring 2011
+
+# Combined seasons
+245 00 $aCombined seasons, and Winter starts the calendar year
+853 20 $831$av.$bno.$u4$vr$i(year)$j(season)$w4$yps24/21,22,23
+863 41 $831.1$a1$b4$i2010$j23$x|a2|b1|i2011|j24/21$zAutumn 2010 to combined Winter/Spring 2011
+
+# Combined seasons, variation
+245 00 $aCombined seasons variation, and Winter starts the calendar year
+853 20 $832$av.$bno.$u4$vr$i(year)$j(season)$w4$yps24,21/22,23
+863 41 $832.1$a1$b4$i2011$j24$x|a2|b1|i2011|j21/22$zWinter 2011 to combined Spring/Summer 2011
+863 41 $832.2$a1$b4$i2011$j21/22$x|a2|b1|i2011|j23$zSpring/Summer 2011 to Autumn 2011
+
+# Defined unit count, non-restarting
+245 00 $aDefined unit count, non-restarting
+853 20 $833$av.$bno.$u4$vc$i(year)$j(month)$wf
+863 41 $833.1$a24$b95$i2011$j01$x|a24|b96|i2011|j07$z3rd Issue to 4th Issue in Volume
+863 41 $833.2$a24$b96$i2011$j07$x|a25|b97|i2012|j01$z4th Issue to 1st Issue in next Volume
+
+# Combined months to end the year
+245 00 $aCombined months to end the year
+853 20 $834$av.$bno.$u12$vr$i(year)$j(month)$wm$ycm12/01$yce22/3
+863 41 $834.1$a24$b1$i2011$j11$x|a24|b2/3|i2011/2012|j12/01$zNov. to Combined Dec./Jan.
+863 41 $834.2$a24$b2/3$i2011/2012$j12/1$x|a24|b4|i2012|j02$zCombined Dec./Jan. to Feb.
index 998b131..c922417 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 ('0531', :eg_version); -- gmc
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0532', :eg_version); -- gmc
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
index 591543f..3e46a19 100644 (file)
@@ -433,7 +433,7 @@ BEGIN
           FROM  
                 actor.org_unit_descendants(ans.id) d
                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
-                JOIN asset.copy cp ON (cp.id = av.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
           GROUP BY 1,2,6;
 
         IF NOT FOUND THEN
@@ -464,7 +464,7 @@ BEGIN
           FROM
                 actor.org_unit_descendants(ans.id) d
                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
-                JOIN asset.copy cp ON (cp.id = av.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
           GROUP BY 1,2,6;
 
         IF NOT FOUND THEN
@@ -577,7 +577,7 @@ BEGIN
           FROM  
                 actor.org_unit_descendants(ans.id) d
                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
-                JOIN asset.copy cp ON (cp.id = av.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
                 JOIN metabib.metarecord_source_map m ON (m.source = av.record)
           GROUP BY 1,2,6;
 
@@ -609,7 +609,7 @@ BEGIN
           FROM
                 actor.org_unit_descendants(ans.id) d
                 JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
-                JOIN asset.copy cp ON (cp.id = av.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
                 JOIN metabib.metarecord_source_map m ON (m.source = av.record)
           GROUP BY 1,2,6;
 
diff --git a/Open-ILS/src/sql/Pg/upgrade/0532.schema.fix_copy_count_funcs.sql b/Open-ILS/src/sql/Pg/upgrade/0532.schema.fix_copy_count_funcs.sql
new file mode 100644 (file)
index 0000000..06c3922
--- /dev/null
@@ -0,0 +1,131 @@
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('0532'); --gmc
+
+CREATE OR REPLACE FUNCTION asset.opac_ou_record_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE
+    ans RECORD;
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
+
+    FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
+        RETURN QUERY
+        SELECT  ans.depth,
+                ans.id,
+                COUNT( av.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( av.id ),
+                trans
+          FROM  
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.opac_lasso_record_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE
+    ans RECORD;
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
+
+    FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
+        RETURN QUERY
+        SELECT  -1,
+                ans.id,
+                COUNT( av.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( av.id ),
+                trans
+          FROM
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;   
+                
+    RETURN;     
+END;            
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.opac_ou_metarecord_copy_count (org INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE
+    ans RECORD;
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
+
+    FOR ans IN SELECT u.id, t.depth FROM actor.org_unit_ancestors(org) AS u JOIN actor.org_unit_type t ON (u.ou_type = t.id) LOOP
+        RETURN QUERY
+        SELECT  ans.depth,
+                ans.id,
+                COUNT( av.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( av.id ),
+                trans
+          FROM  
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
+                JOIN metabib.metarecord_source_map m ON (m.source = av.record)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;
+
+    RETURN;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.opac_lasso_metarecord_copy_count (i_lasso INT, rid BIGINT) RETURNS TABLE (depth INT, org_unit INT, visible BIGINT, available BIGINT, unshadow BIGINT, transcendant INT) AS $f$
+DECLARE
+    ans RECORD;
+    trans INT;
+BEGIN
+    SELECT 1 INTO trans FROM biblio.record_entry b JOIN config.bib_source src ON (b.source = src.id) WHERE src.transcendant AND b.id = rid;
+
+    FOR ans IN SELECT u.org_unit AS id FROM actor.org_lasso_map AS u WHERE lasso = i_lasso LOOP
+        RETURN QUERY
+        SELECT  -1,
+                ans.id,
+                COUNT( av.id ),
+                SUM( CASE WHEN cp.status IN (0,7,12) THEN 1 ELSE 0 END ),
+                COUNT( av.id ),
+                trans
+          FROM
+                actor.org_unit_descendants(ans.id) d
+                JOIN asset.opac_visible_copies av ON (av.record = rid AND av.circ_lib = d.id)
+                JOIN asset.copy cp ON (cp.id = av.copy_id)
+                JOIN metabib.metarecord_source_map m ON (m.source = av.record)
+          GROUP BY 1,2,6;
+
+        IF NOT FOUND THEN
+            RETURN QUERY SELECT ans.depth, ans.id, 0::BIGINT, 0::BIGINT, 0::BIGINT, trans;
+        END IF;
+
+    END LOOP;   
+                
+    RETURN;     
+END;            
+$f$ LANGUAGE PLPGSQL;
+
+COMMIT;