TPac: Hide place hold links when not holdable
authorThomas Berezansky <tsbere@mvlc.org>
Wed, 18 Apr 2012 21:03:05 +0000 (17:03 -0400)
committerDan Scott <dscott@laurentian.ca>
Wed, 1 Aug 2012 20:54:01 +0000 (16:54 -0400)
The only check done is on the holdable flags:
Copy
Status
Location

Hold rules themselves are not checked.

Note that place hold links are shown either way when you can have the
PLACE_UNFILLABLE_HOLD permission and are logged into the staff client.

Signed-off-by: Thomas Berezansky <tsbere@mvlc.org>
Signed-off-by: Dan Scott <dscott@laurentian.ca>

Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/990.schema.unapi.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.tpac_holdable_check.sql [new file with mode: 0644]
Open-ILS/src/templates/opac/parts/record/summary.tt2
Open-ILS/src/templates/opac/parts/result/table.tt2

index 0933acc..865674b 100644 (file)
@@ -276,6 +276,51 @@ sub record_id_to_copy_count {
     return [ sort { $a->{depth} <=> $b->{depth} } @count ];
 }
 
+__PACKAGE__->register_method(
+    method   => "record_has_holdable_copy",
+    api_name => "open-ils.search.biblio.record.has_holdable_copy",
+    signature => {
+        desc => q/Returns a boolean indicating if a record has any holdable copies./,
+        params => [
+            {desc => 'Record ID', type => 'number'}
+        ],
+        return => {
+            desc => q/bool indicating if the record has any holdable copies/,
+            type => 'bool'
+        }
+    }
+);
+
+__PACKAGE__->register_method(
+    method   => "record_has_holdable_copy",
+    api_name => "open-ils.search.biblio.metarecord.has_holdable_copy",
+    signature => {
+        desc => q/Returns a boolean indicating if a record has any holdable copies./,
+        params => [
+            {desc => 'Record ID', type => 'number'}
+        ],
+        return => {
+            desc => q/bool indicating if the record has any holdable copies/,
+            type => 'bool'
+        }
+    }
+);
+
+sub record_has_holdable_copy {
+    my($self, $client, $record_id ) = @_;
+
+    return 0 unless $record_id;
+
+    my $key = $self->api_name =~ /metarecord/ ? 'metarecord' : 'record';
+
+    my $data = $U->cstorereq(
+        "open-ils.cstore.json_query.atomic",
+        { from => ['asset.' . $key . '_has_holdable_copy' => $record_id ] }
+    );
+
+    return ${@$data[0]}{'asset.' . $key . '_has_holdable_copy'} eq 't';
+
+}
 
 __PACKAGE__->register_method(
     method   => "biblio_search_tcn",
index ca9b149..218e2d2 100644 (file)
@@ -262,6 +262,7 @@ sub load_common {
             $ctx->{authtoken} = $e->authtoken;
             $ctx->{authtime} = $e->authtime;
             $ctx->{user} = $e->requestor;
+            $ctx->{place_unfillable} = 1 if $e->requestor->wsid && $e->allowed('PLACE_UNFILLABLE_HOLD', $e->requestor->ws_ou);
 
             $ctx->{user_stats} = $U->simplereq(
                 'open-ils.actor', 
index d98fc75..7497878 100644 (file)
@@ -650,6 +650,28 @@ BEGIN
 END;
 $f$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION asset.record_has_holdable_copy ( rid BIGINT ) RETURNS BOOL AS $f$
+BEGIN
+    PERFORM 1
+        FROM
+            asset.copy acp
+            JOIN asset.call_number acn ON acp.call_number = acn.id
+            JOIN asset.copy_location acpl ON acp.location = acpl.id
+            JOIN config.copy_status ccs ON acp.status = ccs.id
+        WHERE
+            acn.record = rid
+            AND acp.holdable = true
+            AND acpl.holdable = true
+            AND ccs.holdable = true
+            AND acp.deleted = false
+        LIMIT 1;
+    IF FOUND THEN
+        RETURN true;
+    END IF;
+    RETURN FALSE;
+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;
@@ -798,6 +820,29 @@ BEGIN
 END;
 $f$ LANGUAGE PLPGSQL;
 
+CREATE OR REPLACE FUNCTION asset.metarecord_has_holdable_copy ( rid BIGINT ) RETURNS BOOL AS $f$
+BEGIN
+    PERFORM 1
+        FROM
+            asset.copy acp
+            JOIN asset.call_number acn ON acp.call_number = acn.id
+            JOIN asset.copy_location acpl ON acp.location = acpl.id
+            JOIN config.copy_status ccs ON acp.status = ccs.id
+            JOIN metabib.metarecord_source_map mmsm ON acn.record = mmsm.source
+        WHERE
+            mmsm.metarecord = rid
+            AND acp.holdable = true
+            AND acpl.holdable = true
+            AND ccs.holdable = true
+            AND acp.deleted = false
+        LIMIT 1;
+    IF FOUND THEN
+        RETURN true;
+    END IF;
+    RETURN FALSE;
+END;
+$f$ LANGUAGE PLPGSQL;
+
 CREATE OR REPLACE FUNCTION asset.autogenerate_placeholder_barcode ( ) RETURNS TRIGGER AS $f$
 BEGIN
        IF NEW.barcode LIKE '@@%' THEN
index ac33c66..05eafcf 100644 (file)
@@ -398,7 +398,8 @@ RETURNS XML AS $F$
                  name holdings,
                  XMLATTRIBUTES(
                     CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
-                    CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id
+                    CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id,
+                    (SELECT record_has_holdable_copy FROM asset.record_has_holdable_copy($1)) AS has_holdable
                  ),
                  XMLELEMENT(
                      name counts,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.tpac_holdable_check.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.tpac_holdable_check.sql
new file mode 100644 (file)
index 0000000..8212687
--- /dev/null
@@ -0,0 +1,137 @@
+CREATE OR REPLACE FUNCTION asset.record_has_holdable_copy ( rid BIGINT ) RETURNS BOOL AS $f$
+BEGIN
+    PERFORM 1
+        FROM
+            asset.copy acp
+            JOIN asset.call_number acn ON acp.call_number = acn.id
+            JOIN asset.copy_location acpl ON acp.location = acpl.id
+            JOIN config.copy_status ccs ON acp.status = ccs.id
+        WHERE
+            acn.record = rid
+            AND acp.holdable = true
+            AND acpl.holdable = true
+            AND ccs.holdable = true
+            AND acp.deleted = false
+        LIMIT 1;
+    IF FOUND THEN
+        RETURN true;
+    END IF;
+    RETURN FALSE;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION asset.metarecord_has_holdable_copy ( rid BIGINT ) RETURNS BOOL AS $f$
+BEGIN
+    PERFORM 1
+        FROM
+            asset.copy acp
+            JOIN asset.call_number acn ON acp.call_number = acn.id
+            JOIN asset.copy_location acpl ON acp.location = acpl.id
+            JOIN config.copy_status ccs ON acp.status = ccs.id
+            JOIN metabib.metarecord_source_map mmsm ON acn.record = mmsm.source
+        WHERE
+            mmsm.metarecord = rid
+            AND acp.holdable = true
+            AND acpl.holdable = true
+            AND ccs.holdable = true
+            AND acp.deleted = false
+        LIMIT 1;
+    IF FOUND THEN
+        RETURN true;
+    END IF;
+    RETURN FALSE;
+END;
+$f$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION unapi.holdings_xml (
+    bid BIGINT,
+    ouid INT,
+    org TEXT,
+    depth INT DEFAULT NULL,
+    includes TEXT[] DEFAULT NULL::TEXT[],
+    slimit HSTORE DEFAULT NULL,
+    soffset HSTORE DEFAULT NULL,
+    include_xmlns BOOL DEFAULT TRUE,
+    pref_lib INT DEFAULT NULL
+)
+RETURNS XML AS $F$
+     SELECT  XMLELEMENT(
+                 name holdings,
+                 XMLATTRIBUTES(
+                    CASE WHEN $8 THEN 'http://open-ils.org/spec/holdings/v1' ELSE NULL END AS xmlns,
+                    CASE WHEN ('bre' = ANY ($5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id,
+                    (SELECT record_has_holdable_copy FROM asset.record_has_holdable_copy($1)) AS has_holdable
+                 ),
+                 XMLELEMENT(
+                     name counts,
+                     (SELECT  XMLAGG(XMLELEMENT::XML) FROM (
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('public' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.opac_ou_record_copy_count($2,  $1)
+                                     UNION
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('staff' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.staff_ou_record_copy_count($2, $1)
+                                     UNION
+                         SELECT  XMLELEMENT(
+                                     name count,
+                                     XMLATTRIBUTES('pref_lib' as type, depth, org_unit, coalesce(transcendant,0) as transcendant, available, visible as count, unshadow)
+                                 )::text
+                           FROM  asset.opac_ou_record_copy_count($9,  $1)
+                                     ORDER BY 1
+                     )x)
+                 ),
+                 CASE 
+                     WHEN ('bmp' = ANY ($5)) THEN
+                        XMLELEMENT(
+                            name monograph_parts,
+                            (SELECT XMLAGG(bmp) FROM (
+                                SELECT  unapi.bmp( id, 'xml', 'monograph_part', evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7, FALSE)
+                                  FROM  biblio.monograph_part
+                                  WHERE record = $1
+                            )x)
+                        )
+                     ELSE NULL
+                 END,
+                 XMLELEMENT(
+                     name volumes,
+                     (SELECT XMLAGG(acn ORDER BY rank, name, label_sortkey) FROM (
+                        -- Physical copies
+                        SELECT  unapi.acn(y.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), y.rank, name, label_sortkey
+                        FROM evergreen.ranked_volumes($1, $2, $4, $6, $7, $9) AS y
+                        UNION ALL
+                        -- Located URIs
+                        SELECT unapi.acn(uris.id,'xml','volume',evergreen.array_remove_item_by_value( evergreen.array_remove_item_by_value($5,'holdings_xml'),'bre'), $3, $4, $6, $7, FALSE), 0, name, label_sortkey
+                        FROM evergreen.located_uris($1, $2, $9) AS uris
+                     )x)
+                 ),
+                 CASE WHEN ('ssub' = ANY ($5)) THEN 
+                     XMLELEMENT(
+                         name subscriptions,
+                         (SELECT XMLAGG(ssub) FROM (
+                            SELECT  unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7, FALSE)
+                              FROM  serial.subscription
+                              WHERE record_entry = $1
+                        )x)
+                     )
+                 ELSE NULL END,
+                 CASE WHEN ('acp' = ANY ($5)) THEN 
+                     XMLELEMENT(
+                         name foreign_copies,
+                         (SELECT XMLAGG(acp) FROM (
+                            SELECT  unapi.acp(p.target_copy,'xml','copy',evergreen.array_remove_item_by_value($5,'acp'), $3, $4, $6, $7, FALSE)
+                              FROM  biblio.peer_bib_copy_map p
+                                    JOIN asset.copy c ON (p.target_copy = c.id)
+                              WHERE NOT c.deleted AND p.peer_record = $1
+                            LIMIT ($6 -> 'acp')::INT
+                            OFFSET ($7 -> 'acp')::INT
+                        )x)
+                     )
+                 ELSE NULL END
+             );
+$F$ LANGUAGE SQL STABLE;
+
index 9f83fc6..7e3c883 100644 (file)
     </div>
 
     <div id="rdetail_actions_div">
+[% IF ctx.place_unfillable || attrs.marc_xml.findnodes('//*[local-name()="holdings" and @has_holdable="true"]').size > 0 %]
         <div class="rdetail_aux_utils place_hold">
             <a href="[% mkurl(ctx.opac_root _ '/place_hold', 
                 {hold_target => ctx.bre_id, hold_type => 'T', hold_source_page => mkurl()}, stop_parms) %]" 
             class="no-dec"><img src="[% ctx.media_prefix %]/images/green_check.png" alt="[% l('place hold') %]" /><span 
             class="place_hold">[% l('Place Hold') %]</span></a>
         </div>
+[%  END %]
         <div class="rdetail_aux_utils toggle_list">
         [%  IF ctx.user;
             INCLUDE "opac/parts/bookbag_actions.tt2";
index 662041e..426803c 100644 (file)
                                             <td nowrap='nowrap' width="1" align="right">
                                                 <div class="result_table_utils_cont">
                                                     <div class="result_table_utils">
+[% IF ctx.place_unfillable || attrs.marc_xml.findnodes('//*[local-name()="holdings" and @has_holdable="true"]').size > 0 %]
                                                         <div class="results_aux_utils place_hold"><a
                                                                 href="[% mkurl(ctx.opac_root _ '/place_hold', 
                                                                     {hold_target => rec.id, hold_type => 'T', hold_source_page => mkurl()}) %]" 
                                                                 src="[% ctx.media_prefix %]/images/green_check.png"
                                                                 alt=""/><span class="result_place_hold">[% l('Place Hold') %]</span></a>
                                                         </div>
+[% END %]
                                                         <div class="results_aux_utils result_util">
                                                             [%  IF ctx.user;
                                                                 INCLUDE "opac/parts/bookbag_actions.tt2";