From: miker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4> Date: Tue, 29 Mar 2011 15:24:44 +0000 (+0000) Subject: Monograph Parts; Unified vol/copy wizard; Call Number affixes; Instant Detail X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=bcd6f20b9a5c2cead82d9f28925a57d59be4b379;p=evergreen%2Fmasslnc.git Monograph Parts; Unified vol/copy wizard; Call Number affixes; Instant Detail * Monograph Bibliographic Parts - One MARC record should be able to support all volumes associated with the title, subdivide records in multiple ways, and the ability to support holds on individual Parts. * Unified Volume/Copy Wizard - The ability to enter call number, barcode number, and all copy information on the same screen. Also, the ability to include some information associated with the call number, such as classification scheme, prefix and suffix, in the item template feature of the Unified Volume/Copy Wizard. * Call Number Affixes - Delimiting the call number so that the prefix and suffix reside in separate fields. This prefix and suffix should display in the OPAC as part of the call number and should also be available for use in spine labels. * Instant Detail for One Record Hit List - When searching for records in the staff client, the ability to immediately land on the bibliographic record instead of the search results screen when there is only on matching search result in the system. This enhancement does not change the way search results are returned in the public catalog. These features were sponsored by the Massachusetts Library Network Cooperative. git-svn-id: svn://svn.open-ils.org/ILS/trunk@19883 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index 8836291546..04b85049fc 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -1814,14 +1814,57 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA </actions> </permacrud> </class> - <class id="acnc" controller="open-ils.cstore" oils_obj:fieldmapper="asset::call_number_class" oils_persist:tablename="asset.call_number_class" reporter:label="Call number classification scheme"> + <class id="acnc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::call_number_class" oils_persist:tablename="asset.call_number_class" reporter:label="Call number classification scheme"> <fields oils_persist:primary="id" oils_persist:sequence="asset.call_number_class_id_seq"> <field reporter:label="Call number class ID" name="id" reporter_datatype="id"/> <field reporter:label="Name" name="name" reporter:datatype="text"/> <field reporter:label="Normalizer function" name="normalizer" reporter:datatype="text"/> <field reporter:label="Call number fields" name="field" reporter:datatype="text"/> </fields> + <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> + <actions> + <retrieve/> + </actions> + </permacrud> </class> + <class id="acns" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::call_number_suffix" oils_persist:tablename="asset.call_number_suffix" reporter:label="Call Number/Volume Suffix"> + <fields oils_persist:primary="id" oils_persist:sequence="asset.call_number_suffix_id_seq"> + <field reporter:label="ID" name="id" reporter:datatype="id" /> + <field reporter:label="Label" name="label" reporter:datatype="text"/> + <field reporter:label="Label Sort Key" name="label_sortkey" reporter:datatype="text"/> + <field reporter:label="Owning Library" name="owning_lib" reporter:datatype="org_unit"/> + </fields> + <links> + <link field="owning_lib" reltype="has_a" key="id" map="" class="aou"/> + </links> + <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> + <actions> + <create permission="CREATE_VOLUME_SUFFIX" context_field="owning_lib"/> + <retrieve/> + <update permission="UPDATE_VOLUME_SUFFIX" context_field="owning_lib"/> + <delete permission="DELETE_VOLUME_SUFFIX" context_field="owning_lib"/> + </actions> + </permacrud> + </class> + <class id="acnp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::call_number_prefix" oils_persist:tablename="asset.call_number_prefix" reporter:label="Call Number/Volume Prefix"> + <fields oils_persist:primary="id" oils_persist:sequence="asset.call_number_prefix_id_seq"> + <field reporter:label="ID" name="id" reporter:datatype="id" /> + <field reporter:label="Label" name="label" reporter:datatype="text"/> + <field reporter:label="Label Sort Key" name="label_sortkey" reporter:datatype="text"/> + <field reporter:label="Owning Library" name="owning_lib" reporter:datatype="org_unit"/> + </fields> + <links> + <link field="owning_lib" reltype="has_a" key="id" map="" class="aou"/> + </links> + <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> + <actions> + <create permission="CREATE_VOLUME_PREFIX" context_field="owning_lib"/> + <retrieve/> + <update permission="UPDATE_VOLUME_PREFIX" context_field="owning_lib"/> + <delete permission="DELETE_VOLUME_PREFIX" context_field="owning_lib"/> + </actions> + </permacrud> + </class> <class id="acn" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::call_number" oils_persist:tablename="asset.call_number" reporter:label="Call Number/Volume"> <fields oils_persist:primary="id" oils_persist:sequence="asset.call_number_id_seq"> <field reporter:label="Copies" name="copies" oils_persist:virtual="true" reporter:datatype="link"/> @@ -1839,6 +1882,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA <field reporter:label="URIs" name="uris" oils_persist:virtual="true" reporter:datatype="link"/> <field reporter:label="Sort Key" name="label_sortkey" reporter:datatype="text"/> <field reporter:label="Classification Scheme" name="label_class" reporter:datatype="link"/> + <field reporter:label="Prefix" name="prefix" reporter:datatype="link"/> + <field reporter:label="Suffix" name="suffix" reporter:datatype="link"/> </fields> <links> <link field="editor" reltype="has_a" key="id" map="" class="au"/> @@ -1850,6 +1895,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA <link field="uris" reltype="has_many" key="call_number" map="uri" class="auricnm"/> <link field="uri_maps" reltype="has_many" key="call_number" map="" class="auricnm"/> <link field="label_class" reltype="has_a" key="id" map="" class="acnc"/> + <link field="prefix" reltype="has_a" key="id" map="" class="acnp"/> + <link field="suffix" reltype="has_a" key="id" map="" class="acns"/> </links> <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> <actions> @@ -2027,6 +2074,44 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA </actions> </permacrud> </class> + <class id="bmp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="biblio::monograph_part" oils_persist:tablename="biblio.monograph_part" reporter:label="Monograph Parts" oils_persist:field_safe="true"> + <fields oils_persist:primary="id" oils_persist:sequence="biblio.monograph_part_id_seq"> + <field name="id" reporter:datatype="id" /> + <field name="record" reporter:datatype="link"/> + <field name="label" reporter:datatype="text"/> + <field name="label_sortkey" reporter:datatype="text"/> + </fields> + <links> + <link field="record" reltype="has_a" key="id" map="" class="bre"/> + </links> + <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> + <actions> + <create permission="CREATE_MONOGRAPH_PART" global_required="true"/> + <retrieve/> + <update permission="UPDATE_MONOGRAPH_PART" global_required="true"/> + <delete permission="DELETE_MONOGRAPH_PART" global_required="true"/> + </actions> + </permacrud> + </class> + <class id="acpm" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="asset::copy_part_map" oils_persist:tablename="asset.copy_part_map" reporter:label="Copy Monograph Part Map"> + <fields oils_persist:primary="id" oils_persist:sequence="asset.copy_part_map_id_seq"> + <field name="id" reporter:datatype="id" /> + <field name="target_copy" reporter:datatype="link" /> + <field name="part" reporter:datatype="link"/> + </fields> + <links> + <link field="target_copy" reltype="has_a" key="id" map="" class="acp"/> + <link field="part" reltype="has_a" key="id" map="" class="bmp"/> + </links> + <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> + <actions> + <create permission="MAP_MONOGRAPH_PART" global_required="true"/> + <retrieve/> + <update permission="MAP_MONOGRAPH_PART" global_required="true"/> + <delete permission="MAP_MONOGRAPH_PART" global_required="true"/> + </actions> + </permacrud> + </class> <class id="aoucd" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::org_unit::closed_date" oils_persist:tablename="actor.org_unit_closed" reporter:label="Closed Dates"> <fields oils_persist:primary="id" oils_persist:sequence="actor.org_unit_closed_id_seq"> <field name="close_end" reporter:datatype="timestamp" /> @@ -4684,6 +4769,7 @@ SELECT usr, <field reporter:label="Total Circulations" name="total_circ_count" oils_persist:virtual="true" reporter:datatype="link"/> <field reporter:label="Holds" name="holds" oils_persist:virtual="true" reporter:datatype="link"/> <field reporter:label="Statistical Category Entries" name="stat_cat_entries" oils_persist:virtual="true" reporter:datatype="link"/> + <field reporter:label="Monograph Parts" name="parts" oils_persist:virtual="true" reporter:datatype="link"/> </fields> <links> <link field="age_protect" reltype="has_a" key="id" map="" class="crahp"/> @@ -4700,6 +4786,7 @@ SELECT usr, <link field="circulations" reltype="has_many" key="target_copy" map="" class="circ"/> <link field="total_circ_count" reltype="might_have" key="id" map="" class="erfcc"/> <link field="circ_modifier" reltype="has_a" key="code" map="" class="ccm"/> + <link field="parts" reltype="has_many" key="target_copy" map="part" class="acpm"/> </links> <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"> <actions> diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm index ec5c56fd93..8b297015bd 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm @@ -713,7 +713,7 @@ sub fetch_copy_location_by_name { } sub fetch_callnumber { - my( $self, $id ) = @_; + my( $self, $id, $flesh ) = @_; my $evt = undef; my $e = OpenILS::Event->new( 'ASSET_CALL_NUMBER_NOT_FOUND', id => $id ); @@ -726,6 +726,27 @@ sub fetch_callnumber { 'open-ils.cstore.direct.asset.call_number.retrieve', $id ); $evt = $e unless $cn; + if ($flesh && $cn) { + $cn->prefix( + $self->simplereq( + 'open-ils.cstore', + 'open-ils.cstore.direct.asset.call_number_prefix.retrieve', $cn->prefix + ) + ); + $cn->suffix( + $self->simplereq( + 'open-ils.cstore', + 'open-ils.cstore.direct.asset.call_number_suffix.retrieve', $cn->suffix + ) + ); + $cn->label_class( + $self->simplereq( + 'open-ils.cstore', + 'open-ils.cstore.direct.asset.call_number_class.retrieve', $cn->label_class + ) + ); + } + return ( $cn, $evt ); } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm index b787999899..525871d3c8 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat.pm @@ -660,16 +660,23 @@ sub _build_volume_list { $search_hash->{deleted} = 'f'; my $e = new_editor(); - my $vols = $e->search_asset_call_number([$search_hash, { 'order_by' => { - 'acn' => 'oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib' - } } ] ); + my $vols = $e->search_asset_call_number([ + $search_hash, + { + flesh => 1, + flesh_fields => { acn => ['prefix','suffix','label_class'] }, + 'order_by' => { 'acn' => 'oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib' } + } + ]); my @volumes; for my $volume (@$vols) { - my $copies = $e->search_asset_copy( - { call_number => $volume->id , deleted => 'f' }); + my $copies = $e->search_asset_copy([ + { call_number => $volume->id , deleted => 'f' }, + { flesh => 1, flesh_fields => { acp => ['parts'] } } + ]); $copies = [ sort { $a->barcode cmp $b->barcode } @$copies ]; @@ -912,6 +919,8 @@ sub update_volume { owning_lib => $vol->owning_lib, record => $vol->record, label => $vol->label, + prefix => $vol->prefix, + suffix => $vol->suffix, deleted => 'f', id => {'!=' => $vol->id} }); @@ -1034,6 +1043,8 @@ sub batch_volume_transfer { my $existing_vol = $e->search_asset_call_number( { label => $vol->label, + prefix => $vol->prefix, + suffix => $vol->suffix, record => $rec, owning_lib => $o_lib, deleted => 'f' @@ -1114,15 +1125,15 @@ __PACKAGE__->register_method( ); sub find_or_create_volume { - my( $self, $conn, $auth, $label, $record_id, $org_id ) = @_; + my( $self, $conn, $auth, $label, $record_id, $org_id, $prefix, $suffix, $label_class ) = @_; my $e = new_editor(authtoken=>$auth, xact=>1); return $e->die_event unless $e->checkauth; my ($vol, $evt, $exists) = - OpenILS::Application::Cat::AssetCommon->find_or_create_volume($e, $label, $record_id, $org_id); + OpenILS::Application::Cat::AssetCommon->find_or_create_volume($e, $label, $record_id, $org_id, $prefix, $suffix, $label_class); return $evt if $evt; $e->rollback if $exists; $e->commit if $vol; - return $vol->id; + return { 'acn_id' => $vol->id, 'existed' => $exists }; } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm index b2dffc5518..c24a9f3d50 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/AssetCommon.pm @@ -133,6 +133,63 @@ sub update_copy_stat_entries { return undef; } +# if 'delete_maps' is true, the copy->parts data is treated as the +# authoritative list for the copy. existing part maps not targeting +# these parts will be deleted from the DB +sub update_copy_parts { + my($class, $editor, $copy, $delete_maps) = @_; + + return undef if $copy->isdeleted; + return undef unless $copy->ischanged or $copy->isnew; + + my $evt; + my $incoming_parts = $copy->parts; + + if( $delete_maps ) { + $incoming_parts = ($incoming_parts and @$incoming_parts) ? $incoming_parts : []; + } else { + return undef unless ($incoming_parts and @$incoming_parts); + } + + my $maps = $editor->search_asset_copy_part_map({target_copy=>$copy->id}); + + if(!$copy->isnew) { + # if there is no part map on the copy who's id matches the + # current map's id, remove the map from the database + for my $map (@$maps) { + if(! grep { $_->id == $map->part } @$incoming_parts ) { + + $logger->info("copy update found stale ". + "monographic part map ".$map->id. " on copy ".$copy->id); + + $editor->delete_asset_copy_part_map($map) + or return $editor->event; + } + } + } + + # go through the part map update/create process + for my $incoming_part (@$incoming_parts) { + next unless $incoming_part; + + # if this link already exists in the DB, don't attempt to re-create it + next if( grep{$_->part == $incoming_part->id} @$maps ); + + my $new_map = Fieldmapper::asset::copy_part_map->new(); + + $new_map->part( $incoming_part->id ); + $new_map->target_copy( $copy->id ); + + $editor->create_asset_copy_part_map($new_map) + or return $editor->event; + + $logger->info("copy update created new monographic part copy map ".$editor->data); + } + + return undef; +} + + sub update_copy { my($class, $editor, $override, $vol, $copy, $retarget_holds, $force_delete_empty_bib) = @_; @@ -224,6 +281,9 @@ sub update_fleshed_copies { my $sc_entries = $copy->stat_cat_entries; $copy->clear_stat_cat_entries; + my $parts = $copy->parts; + $copy->clear_parts; + if( $copy->isdeleted ) { $evt = $class->delete_copy($editor, $override, $vol, $copy, $retarget_holds, $force_delete_empty_bib); return $evt if $evt; @@ -240,6 +300,9 @@ sub update_fleshed_copies { $copy->stat_cat_entries( $sc_entries ); $evt = $class->update_copy_stat_entries($editor, $copy, $delete_stats); + $copy->parts( $parts ); + # probably okay to use $delete_stats here for simplicity + $evt = $class->update_copy_parts($editor, $copy, $delete_stats); return $evt if $evt; } @@ -305,6 +368,8 @@ sub create_volume { owning_lib => $vol->owning_lib, record => $vol->record, label => $vol->label, + prefix => $vol->prefix, + suffix => $vol->suffix, deleted => 'f' } ); @@ -345,13 +410,16 @@ sub create_volume { # returns the volume if it exists sub volume_exists { - my($class, $e, $rec_id, $label, $owning_lib) = @_; + my($class, $e, $rec_id, $label, $owning_lib, $prefix, $suffix) = @_; return $e->search_asset_call_number( - {label => $label, record => $rec_id, owning_lib => $owning_lib, deleted => 'f'})->[0]; + {label => $label, record => $rec_id, owning_lib => $owning_lib, deleted => 'f', prefix => $prefix, suffix => $suffix})->[0]; } sub find_or_create_volume { - my($class, $e, $label, $record_id, $org_id) = @_; + my($class, $e, $label, $record_id, $org_id, $prefix, $suffix, $label_class) = @_; + + $prefix ||= '-1'; + $suffix ||= '-1'; my $vol; @@ -360,7 +428,7 @@ sub find_or_create_volume { or return (undef, $e->die_event); } else { - $vol = $class->volume_exists($e, $record_id, $label, $org_id); + $vol = $class->volume_exists($e, $record_id, $label, $org_id, $prefix, $suffix); } # If the volume exists, return the ID @@ -373,7 +441,10 @@ sub find_or_create_volume { $vol = Fieldmapper::asset::call_number->new; $vol->owning_lib($org_id); + $vol->label_class($label_class) if ($label_class); $vol->label($label); + $vol->prefix($prefix); + $vol->suffix($suffix); $vol->record($record_id); my $evt = OpenILS::Application::Cat::AssetCommon->create_volume(0, $e, $vol); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm index 67b2dc1a3d..88af4f55d8 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm @@ -973,8 +973,8 @@ sub copy_details { { flesh => 2, flesh_fields => { - acp => ['call_number'], - acn => ['record'] + acp => ['call_number','parts'], + acn => ['record','prefix','suffix','label_class'] } } ]) or return $e->event; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm index 74957f1845..428b986ff4 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm @@ -183,6 +183,8 @@ sub create_hold { return $e->die_event unless $e->allowed('TITLE_HOLDS', $porg); } elsif ( $t eq OILS_HOLD_TYPE_VOLUME ) { return $e->die_event unless $e->allowed('VOLUME_HOLDS', $porg); + } elsif ( $t eq OILS_HOLD_TYPE_MONOPART ) { + return $e->die_event unless $e->allowed('TITLE_HOLDS', $porg); } elsif ( $t eq OILS_HOLD_TYPE_ISSUANCE ) { return $e->die_event unless $e->allowed('ISSUANCE_HOLDS', $porg); } elsif ( $t eq OILS_HOLD_TYPE_COPY ) { @@ -1947,6 +1949,7 @@ The named fields in the hash are: pickup_lib - destination for hold, fallback value for selection_ou selection_ou - ID of org_unit establishing hard and soft hold boundary settings issuanceid - ID of the issuance to be held, required for Issuance level hold + partid - ID of the monograph part to be held, required for monograph part level hold titleid - ID (BRN) of the title to be held, required for Title level hold volume_id - required for Volume level hold copy_id - required for Copy level hold @@ -2038,6 +2041,7 @@ sub do_possibility_checks { my($e, $patron, $request_lib, $depth, %params) = @_; my $issuanceid = $params{issuanceid} || ""; + my $partid = $params{partid} || ""; my $titleid = $params{titleid} || ""; my $volid = $params{volume_id}; my $copyid = $params{copy_id}; @@ -2082,6 +2086,12 @@ sub do_possibility_checks { $issuanceid, $depth, $request_lib, $patron, $e->requestor, $pickup_lib, $selection_ou ); + } elsif( $hold_type eq OILS_HOLD_TYPE_MONOPART ) { + + return _check_monopart_hold_is_possible( + $partid, $depth, $request_lib, $patron, $e->requestor, $pickup_lib, $selection_ou + ); + } elsif( $hold_type eq OILS_HOLD_TYPE_METARECORD ) { my $maps = $e->search_metabib_metarecord_source_map({metarecord=>$mrid}); @@ -2379,6 +2389,140 @@ sub _check_issuance_hold_is_possible { return @status; } +sub _check_monopart_hold_is_possible { + my( $partid, $depth, $request_lib, $patron, $requestor, $pickup_lib, $selection_ou ) = @_; + + my $e = new_editor(); + my %org_filter = create_ranged_org_filter($e, $selection_ou, $depth); + + # this monster will grab the id and circ_lib of all of the "holdable" copies for the given record + my $copies = $e->json_query( + { + select => { acp => ['id', 'circ_lib'] }, + from => { + acp => { + acpm => { + field => 'target_copy', + fkey => 'id', + filter => { part => $partid } + }, + acpl => { field => 'id', filter => { holdable => 't'}, fkey => 'location' }, + ccs => { field => 'id', filter => { holdable => 't'}, fkey => 'status' } + } + }, + where => { + '+acp' => { circulate => 't', deleted => 'f', holdable => 't', %org_filter } + }, + distinct => 1 + } + ); + + $logger->info("monopart possible found ".scalar(@$copies)." potential copies"); + + my $empty_ok; + if (!@$copies) { + $empty_ok = $e->retrieve_config_global_flag('circ.holds.empty_part_ok'); + $empty_ok = ($empty_ok and $U->is_true($empty_ok->enabled)); + + return ( + 0, 0, [ + new OpenILS::Event( + "HIGH_LEVEL_HOLD_HAS_NO_COPIES", + "payload" => {"fail_part" => "no_ultimate_items"} + ) + ] + ) unless $empty_ok; + + return (1, 0); + } + + # ----------------------------------------------------------------------- + # sort the copies into buckets based on their circ_lib proximity to + # the patron's home_ou. + # ----------------------------------------------------------------------- + + my $home_org = $patron->home_ou; + my $req_org = $request_lib->id; + + $logger->info("prox cache $home_org " . $prox_cache{$home_org}); + + $prox_cache{$home_org} = + $e->search_actor_org_unit_proximity({from_org => $home_org}) + unless $prox_cache{$home_org}; + my $home_prox = $prox_cache{$home_org}; + + my %buckets; + my %hash = map { ($_->to_org => $_->prox) } @$home_prox; + push( @{$buckets{ $hash{$_->{circ_lib}} } }, $_->{id} ) for @$copies; + + my @keys = sort { $a <=> $b } keys %buckets; + + + if( $home_org ne $req_org ) { + # ----------------------------------------------------------------------- + # shove the copies close to the request_lib into the primary buckets + # directly before the farthest away copies. That way, they are not + # given priority, but they are checked before the farthest copies. + # ----------------------------------------------------------------------- + $prox_cache{$req_org} = + $e->search_actor_org_unit_proximity({from_org => $req_org}) + unless $prox_cache{$req_org}; + my $req_prox = $prox_cache{$req_org}; + + my %buckets2; + my %hash2 = map { ($_->to_org => $_->prox) } @$req_prox; + push( @{$buckets2{ $hash2{$_->{circ_lib}} } }, $_->{id} ) for @$copies; + + my $highest_key = $keys[@keys - 1]; # the farthest prox in the exising buckets + my $new_key = $highest_key - 0.5; # right before the farthest prox + my @keys2 = sort { $a <=> $b } keys %buckets2; + for my $key (@keys2) { + last if $key >= $highest_key; + push( @{$buckets{$new_key}}, $_ ) for @{$buckets2{$key}}; + } + } + + @keys = sort { $a <=> $b } keys %buckets; + + my $title; + my %seen; + my @status; + OUTER: for my $key (@keys) { + my @cps = @{$buckets{$key}}; + + $logger->info("looking at " . scalar(@{$buckets{$key}}). " copies in proximity bucket $key"); + + for my $copyid (@cps) { + + next if $seen{$copyid}; + $seen{$copyid} = 1; # there could be dupes given the merged buckets + my $copy = $e->retrieve_asset_copy($copyid); + $logger->debug("looking at bucket_key=$key, copy $copyid : circ_lib = " . $copy->circ_lib); + + unless($title) { # grab the title if we don't already have it + my $vol = $e->retrieve_asset_call_number( + [ $copy->call_number, { flesh => 1, flesh_fields => { bre => ['fixed_fields'], acn => ['record'] } } ] ); + $title = $vol->record; + } + + @status = verify_copy_for_hold( + $patron, $requestor, $title, $copy, $pickup_lib, $request_lib); + + last OUTER if $status[0]; + } + } + + if (!$status[0]) { + if (!defined($empty_ok)) { + $empty_ok = $e->retrieve_config_global_flag('circ.holds.empty_part_ok'); + $empty_ok = ($empty_ok and $U->is_true($empty_ok->enabled)); + } + + return (1,0) if ($empty_ok); + } + return @status; +} + sub _check_volume_hold_is_possible { my( $vol, $title, $depth, $request_lib, $patron, $requestor, $pickup_lib, $selection_ou ) = @_; @@ -2688,7 +2832,7 @@ sub uber_hold_impl { $hold->usr($user->id); - my( $mvr, $volume, $copy, $issuance, $bre ) = find_hold_mvr($e, $hold, $args->{suppress_mvr}); + my( $mvr, $volume, $copy, $issuance, $part, $bre ) = find_hold_mvr($e, $hold, $args->{suppress_mvr}); flesh_hold_notices([$hold], $e) unless $args->{suppress_notices}; flesh_hold_transits([$hold]) unless $args->{suppress_transits}; @@ -2697,12 +2841,15 @@ sub uber_hold_impl { my $resp = { hold => $hold, - copy => $copy, - volume => $volume, + ($copy ? (copy => $copy) : ()), + ($volume ? (volume => $volume) : ()), + ($issuance ? (issuance => $issuance) : ()), + ($part ? (part => $part) : ()), + ($args->{include_bre} ? (bre => $bre) : ()), + ($args->{suppress_mvr} ? () : (mvr => $mvr)), %$details }; - $resp->{mvr} = $mvr unless $args->{suppress_mvr}; unless($args->{suppress_patron_details}) { my $card = $e->retrieve_actor_card($user->card) or return $e->event; $resp->{patron_first} = $user->first_given_name, @@ -2711,8 +2858,6 @@ sub uber_hold_impl { $resp->{patron_alias} = $user->alias, }; - $resp->{bre} = $bre if $args->{include_bre}; - return $resp; } @@ -2729,6 +2874,7 @@ sub find_hold_mvr { my $copy; my $volume; my $issuance; + my $part; if( $hold->hold_type eq OILS_HOLD_TYPE_METARECORD ) { my $mr = $e->retrieve_metabib_metarecord($hold->target) @@ -2751,6 +2897,14 @@ sub find_hold_mvr { $tid = $issuance->subscription->record_entry; + } elsif( $hold->hold_type eq OILS_HOLD_TYPE_MONOPART ) { + $part = $e->retrieve_biblio_monographic_part([ + $hold->target, + {flesh => 1, flesh_fields => {bmp => [ qw/record/ ]}} + ]) or return $e->event; + + $tid = $part->record; + } elsif( $hold->hold_type eq OILS_HOLD_TYPE_COPY ) { $copy = $e->retrieve_asset_copy([ $hold->target, @@ -2772,7 +2926,7 @@ sub find_hold_mvr { # TODO return metarcord mvr for M holds my $title = $e->retrieve_biblio_record_entry($tid); - return ( ($no_mvr) ? undef : $U->record_to_mvr($title), $volume, $copy, $issuance, $title ); + return ( ($no_mvr) ? undef : $U->record_to_mvr($title), $volume, $copy, $issuance, $part, $title ); } __PACKAGE__->register_method( @@ -3139,6 +3293,14 @@ sub hold_item_is_checked_out { $query->{where}->{'+acp'}->{call_number} = $hold_target; + } elsif($hold_type eq 'P') { + + $query->{from}->{acp}->{acpm} = { + field => 'target_copy', + fkey => 'id', + filter => {part => $hold_target}, + }; + } elsif($hold_type eq 'I') { $query->{from}->{acp}->{sitem} = { @@ -3305,7 +3467,7 @@ sub rec_hold_count { '-or' => [ { '-and' => { - hold_type => 'C', + hold_type => [qw/C F R/], target => { in => { select => {acp => ['id']}, diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm index 946677f768..226ad4422c 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm @@ -446,7 +446,7 @@ sub fleshed_copy_retrieve_batch { "open-ils.cstore.direct.asset.copy.search.atomic", { id => $ids }, { flesh => 1, - flesh_fields => { acp => [ qw/ circ_lib location status stat_cat_entries / ] } + flesh_fields => { acp => [ qw/ circ_lib location status stat_cat_entries parts / ] } }); } @@ -494,7 +494,7 @@ sub fleshed_copy_retrieve2 { flesh => 2, flesh_fields => { acp => [ - qw/ location status stat_cat_entry_copy_maps notes age_protect / + qw/ location status stat_cat_entry_copy_maps notes age_protect parts / ], ascecm => [qw/ stat_cat stat_cat_entry /], } @@ -2227,6 +2227,21 @@ sub fetch_cn { } __PACKAGE__->register_method( + method => "fetch_fleshed_cn", + api_name => "open-ils.search.callnumber.fleshed.retrieve", + authoritative => 1, + notes => "retrieves a callnumber based on ID, fleshing prefix, suffix, and label_class", +); + +sub fetch_fleshed_cn { + my( $self, $client, $id ) = @_; + my( $cn, $evt ) = $apputils->fetch_callnumber( $id, 1 ); + return $evt if $evt; + return $cn; +} + + +__PACKAGE__->register_method( method => "fetch_copy_by_cn", api_name => 'open-ils.search.copies_by_call_number.retrieve', signature => q/ @@ -2342,6 +2357,52 @@ sub fetch_slim_record { return \@res; } +__PACKAGE__->register_method( + method => 'rec_hold_parts', + api_name => 'open-ils.search.biblio.record_hold_parts', + signature => q/ + Returns a list of {label :foo, id : bar} objects for viable monograph parts for a given record + / +); + +sub rec_hold_parts { + my( $self, $conn, $args ) = @_; + + my $rec = $$args{record}; + my $mrec = $$args{metarecord}; + my $pickup_lib = $$args{pickup_lib}; + my $e = new_editor(); + + my $query = { + select => {bmp => ['id', 'label']}, + from => 'bmp', + where => { + id => { + in => { + select => {'acpm' => ['part']}, + from => {acpm => {acp => {join => {acn => {join => 'bre'}}}}}, + where => { + '+acp' => {'deleted' => 'f'}, + '+bre' => {id => $rec} + }, + distinct => 1, + } + } + } + }; + + if(defined $pickup_lib) { + my $hard_boundary = $U->ou_ancestor_setting_value($pickup_lib, OILS_SETTING_HOLD_HARD_BOUNDARY); + if($hard_boundary) { + my $orgs = $e->json_query({from => ['actor.org_unit_descendants' => $pickup_lib, $hard_boundary]}); + $query->{where}->{'+acp'}->{circ_lib} = [ map { $_->{id} } @$orgs ]; + } + } + + return $e->json_query($query); +} + + __PACKAGE__->register_method( diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm index efd3da1c86..2ee5cba09f 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial.pm @@ -1360,6 +1360,7 @@ sub unitize_items { sub _find_or_create_call_number { my ($e, $lib, $cn_string, $record) = @_; + # FIXME: should suffix and prefix come into play here? my $existing = $e->search_asset_call_number({ "owning_lib" => $lib, "label" => $cn_string, diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI.pm index d004f3898d..4cec39f8f3 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI.pm @@ -640,6 +640,15 @@ sub modify_from_fieldmapper { asset::call_number->has_many( copies => 'asset::copy' ); asset::call_number->has_many( notes => 'asset::call_number_note' ); + asset::call_number->has_a( prefix => 'asset::call_number_prefix' ); + asset::call_number->has_a( suffix => 'asset::call_number_suffix' ); + + asset::call_number_prefix->has_a( owning_lib => 'actor::org_unit' ); + asset::call_number_suffix->has_a( owning_lib => 'actor::org_unit' ); + + asset::call_number_prefix->has_many( call_numbers => 'asset::call_number' ); + asset::call_number_suffix->has_many( call_numbers => 'asset::call_number' ); + authority::record_entry->has_many( record_descriptor => 'authority::record_descriptor' ); authority::record_entry->has_many( notes => 'authority::record_note' ); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/asset.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/asset.pm index 39eb01b2ad..b464de57f1 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/asset.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/asset.pm @@ -21,6 +21,22 @@ __PACKAGE__->columns( Primary => qw/id/ ); __PACKAGE__->columns( Essential => qw/location org position/ ); #------------------------------------------------------------------------------- +package asset::call_number_suffix; +use base qw/asset/; + +__PACKAGE__->table( 'asset_call_number_suffix' ); +__PACKAGE__->columns( Primary => qw/id/ ); +__PACKAGE__->columns( Essential => qw/owning_lib label label_sortkey/ ); + +#------------------------------------------------------------------------------- +package asset::call_number_prefix; +use base qw/asset/; + +__PACKAGE__->table( 'asset_call_number_prefix' ); +__PACKAGE__->columns( Primary => qw/id/ ); +__PACKAGE__->columns( Essential => qw/owning_lib label label_sortkey/ ); + +#------------------------------------------------------------------------------- package asset::call_number_class; use base qw/asset/; @@ -34,7 +50,7 @@ use base qw/asset/; __PACKAGE__->table( 'asset_call_number' ); __PACKAGE__->columns( Primary => qw/id/ ); -__PACKAGE__->columns( Essential => qw/record label creator create_date editor +__PACKAGE__->columns( Essential => qw/record label creator create_date editor prefix suffix edit_date record label owning_lib deleted label_class label_sortkey/ ); #------------------------------------------------------------------------------- @@ -59,6 +75,14 @@ __PACKAGE__->columns( Essential => qw/call_number barcode creator create_date ed age_protect floating cost status_changed_time/ ); #------------------------------------------------------------------------------- +package asset::copy_part_map; +use base qw/asset/; + +__PACKAGE__->table( 'asset_copy_part_map' ); +__PACKAGE__->columns( Primary => qw/id/ ); +__PACKAGE__->columns( Essential => qw/target_copy part/); + +#------------------------------------------------------------------------------- package asset::stat_cat; use base qw/asset/; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/biblio.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/biblio.pm index 46fefaab6e..3df2ee5aca 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/biblio.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/CDBI/biblio.pm @@ -22,5 +22,13 @@ biblio::record_note->columns( Essential => qw/id record value creator editor create_date edit_date pub/ ); #------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +package biblio::monograph_part; +use base qw/biblio/; + +biblio::monograph_part->table( 'biblio_monograph_part' ); +biblio::monograph_part->columns( Essential => qw/id record label label_sortkey/ ); +#------------------------------------------------------------------------------- + 1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/dbi.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/dbi.pm index 5ca6fd008b..623be5fbeb 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/dbi.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/dbi.pm @@ -1,5 +1,16 @@ { + #------------------------------------------------------------------------------- + package asset::copy_part_map; + + asset::copy_part_map->table( 'asset.copy_part_map' ); + + #------------------------------------------------------------------------------- + package biblio::monograph_part; + + biblio::monograph_part->table( 'biblio.monograph_part' ); + biblio::monograph_part->sequence( 'biblio.monograph_part_id_seq' ); + #------------------------------------------------------------------------------- package container::user_bucket; @@ -322,6 +333,18 @@ asset::call_number->sequence( 'asset.call_number_id_seq' ); #--------------------------------------------------------------------- + package asset::call_number_suffix; + + asset::call_number_suffix->table( 'asset.call_number_suffix' ); + asset::call_number_suffix->sequence( 'asset.call_number_suffix_id_seq' ); + + #--------------------------------------------------------------------- + package asset::call_number_prefix; + + asset::call_number_prefix->table( 'asset.call_number_prefix' ); + asset::call_number_prefix->sequence( 'asset.call_number_prefix_id_seq' ); + + #--------------------------------------------------------------------- package asset::call_number_class; asset::call_number_class->table( 'asset.call_number_class' ); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm index 0760fcb005..cd0cdb84a9 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm @@ -1233,6 +1233,15 @@ sub new_hold_copy_targeter { { id => [map {$_->id} @{ $vtree->copies }], deleted => 'f' } ) if ($vtree && @{ $vtree->copies }); + + } elsif ($hold->hold_type eq 'P') { + my @part_maps = asset::copy_part_map->search_where( { part => $hold->target } ); + $all_copies = [ + asset::copy->search_where( + { id => [map {$_->target_copy} @part_maps], + deleted => 'f' } + ) + ] if (@part_maps); } elsif ($hold->hold_type eq 'I') { my ($itree) = $self diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/biblio.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/biblio.pm index 5b609bee73..7c7c83078c 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/biblio.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/biblio.pm @@ -380,19 +380,28 @@ sub record_copy_status_count { my $descendants = "actor.org_unit_descendants(?,?)"; my $cn_table = asset::call_number->table; + my $cnp_table = asset::call_number_prefix->table; + my $cns_table = asset::call_number_prefix->table; my $cp_table = asset::copy->table; my $cl_table = asset::copy_location->table; my $cs_table = config::copy_status->table; my $sql = <<" SQL"; - SELECT cp.circ_lib, cn.label, cp.status, count(cp.id) + SELECT cp.circ_lib, + CASE WHEN cnp.id > -1 THEN cnp.label || ' ' ELSE '' END || cn.label || CASE WHEN cns.id > -1 THEN ' ' || cns.label ELSE '' END, + cp.status, + count(cp.id) FROM $cp_table cp, $cn_table cn, + $cns_table cns, + $cnp_table cnp, $cl_table cl, $cs_table cs, $descendants d WHERE cn.record = ? + AND cnp.id = cn.prefix + AND cns.id = cn.suffix AND cp.call_number = cn.id AND cp.location = cl.id AND cp.circ_lib = d.id @@ -440,6 +449,8 @@ sub record_copy_status_location_count { my $descendants = "actor.org_unit_descendants(?,?)"; my $cn_table = asset::call_number->table; + my $cnp_table = asset::call_number_prefix->table; + my $cns_table = asset::call_number_prefix->table; my $cp_table = asset::copy->table; my $cl_table = asset::copy_location->table; my $cs_table = config::copy_status->table; @@ -450,16 +461,20 @@ sub record_copy_status_location_count { my $sql = <<" SQL"; SELECT cp.circ_lib, - cn.label, + CASE WHEN cnp.id > -1 THEN cnp.label || ' ' ELSE '' END || cn.label || CASE WHEN cns.id > -1 THEN ' ' || cns.label ELSE '' END, oils_i18n_xlate('asset.copy_location', 'acpl', 'name', 'id', cl.id::TEXT, ?), cp.status, count(cp.id) FROM $cp_table cp, $cn_table cn, + $cns_table cns, + $cnp_table cnp, $cl_table cl, $cs_table cs, $descendants d WHERE cn.record = ? + AND cnp.id = cn.prefix + AND cns.id = cn.suffix AND cp.call_number = cn.id AND cp.location = cl.id AND cp.circ_lib = d.id diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm index b2419c21e5..68928a617b 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/SuperCat.pm @@ -330,7 +330,7 @@ sub cn_browse { @cp_filter }, { flesh => 1, - flesh_fields => { acn => [qw/record owning_lib/] }, + flesh_fields => { acn => [qw/record owning_lib prefix suffix/] }, order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" }, limit => $before_limit, offset => abs($page) * $page_size - $before_offset, @@ -348,7 +348,7 @@ sub cn_browse { @cp_filter }, { flesh => 1, - flesh_fields => { acn => [qw/record owning_lib/] }, + flesh_fields => { acn => [qw/record owning_lib prefix suffix/] }, order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" }, limit => $after_limit, offset => abs($page) * $page_size - $after_offset, @@ -455,7 +455,7 @@ sub cn_startwith { @cp_filter }, { flesh => 1, - flesh_fields => { acn => [qw/record owning_lib/] }, + flesh_fields => { acn => [qw/record owning_lib prefix suffix/] }, order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" }, limit => $limit, offset => $offset, @@ -473,7 +473,7 @@ sub cn_startwith { @cp_filter }, { flesh => 1, - flesh_fields => { acn => [qw/record owning_lib/] }, + flesh_fields => { acn => [qw/record owning_lib prefix suffix/] }, order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" }, limit => $limit, offset => $offset, @@ -1690,7 +1690,7 @@ sub retrieve_uri { flesh_fields => { auri => [qw/call_number_maps/], auricnm => [qw/call_number/], - acn => [qw/owning_lib record/], + acn => [qw/owning_lib record prefix suffix/], } }) ->gather(1)) @@ -1731,8 +1731,8 @@ sub retrieve_copy { $cpid, { flesh => 2, flesh_fields => { - acn => [qw/owning_lib record/], - acp => [qw/call_number location status circ_lib stat_cat_entries notes/], + acn => [qw/owning_lib record prefix suffix/], + acp => [qw/call_number location status circ_lib stat_cat_entries notes parts/], } }) ->gather(1)) @@ -1774,9 +1774,9 @@ sub retrieve_callnumber { $cnid, { flesh => 5, flesh_fields => { - acn => [qw/owning_lib record copies uri_maps/], + acn => [qw/owning_lib record copies uri_maps prefix suffix/], auricnm => [qw/uri/], - acp => [qw/location status circ_lib stat_cat_entries notes/], + acp => [qw/location status circ_lib stat_cat_entries notes parts/], } }) ->gather(1)) @@ -1823,8 +1823,8 @@ sub basic_record_holdings { { flesh => 5, flesh_fields => { bre => [qw/call_numbers/], - acn => [qw/copies owning_lib/], - acp => [qw/location status circ_lib/], + acn => [qw/copies owning_lib prefix suffix/], + acp => [qw/location status circ_lib parts/], } } )->gather(1); @@ -1975,9 +1975,9 @@ sub new_record_holdings { }, { flesh => 5, flesh_fields => { - acn => [qw/copies owning_lib uri_maps/], + acn => [qw/copies owning_lib uri_maps prefix suffix/], auricnm => [qw/uri/], - acp => [qw/circ_lib location status stat_cat_entries notes/], + acp => [qw/circ_lib location status stat_cat_entries notes parts/], asce => [qw/stat_cat/], }, ( $limit > -1 ? ( limit => $limit ) : () ), @@ -2049,7 +2049,7 @@ sub new_record_holdings { sstr => [qw/items/], sitem => [qw/notes unit/], sunit => [qw/notes location status circ_lib stat_cat_entries call_number/], - acn => [qw/owning_lib/], + acn => [qw/owning_lib prefix suffix/], }, ( $limit > -1 ? ( limit => $limit ) : () ), ( $offset ? ( offset => $offset ) : () ), @@ -3021,6 +3021,20 @@ sub as_xml { } + $xml .= ' <prefix '; + $xml .= 'ident="' . $self->obj->prefix->id . '" '; + $xml .= 'id="tag:open-ils.org:asset-call_number_prefix/' . $self->obj->prefix->id . '" '; + $xml .= 'label_sortkey="'.$self->escape( $self->obj->prefix->label_sortkey ) .'">'; + $xml .= $self->escape( $self->obj->prefix->label ) .'</prefix>'; + $xml .= "\n"; + + $xml .= ' <suffix '; + $xml .= 'ident="' . $self->obj->suffix->id . '" '; + $xml .= 'id="tag:open-ils.org:asset-call_number_suffix/' . $self->obj->suffix->id . '" '; + $xml .= 'label_sortkey="'.$self->escape( $self->obj->suffix->label_sortkey ) .'">'; + $xml .= $self->escape( $self->obj->suffix->label ) .'</suffix>'; + $xml .= "\n"; + $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" '; $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" '; $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" '; @@ -3453,6 +3467,15 @@ sub as_xml { $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'" opac_visible="'.$self->obj->circ_lib->opac_visible.'"/>'; $xml .= "\n"; + $xml .= " <monograph_parts>\n"; + if (ref($self->obj->parts) && $self->obj->parts) { + for my $part ( @{$self->obj->parts} ) { + $xml .= sprintf(' <monograph_part record="%s" sortkey="%s">%s</monograph_part>',$part->record, $self->escape($part->label_sortkey), $self->escape($part->label)); + $xml .= "\n"; + } + } + + $xml .= " </monograph_parts>\n"; $xml .= " <copy_notes>\n"; if (ref($self->obj->notes) && $self->obj->notes) { for my $note ( @{$self->obj->notes} ) { diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Const.pm b/Open-ILS/src/perlmods/lib/OpenILS/Const.pm index 281f465bd0..6da2ed60aa 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Const.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Const.pm @@ -102,6 +102,7 @@ econst OILS_HOLD_TYPE_ISSUANCE => 'I'; econst OILS_HOLD_TYPE_VOLUME => 'V'; econst OILS_HOLD_TYPE_TITLE => 'T'; econst OILS_HOLD_TYPE_METARECORD => 'M'; +econst OILS_HOLD_TYPE_MONOPART => 'P'; econst OILS_BILLING_TYPE_OVERDUE_MATERIALS => 'Overdue materials'; diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql index af39c34103..72ce6c5bd6 100644 --- a/Open-ILS/src/sql/Pg/002.schema.config.sql +++ b/Open-ILS/src/sql/Pg/002.schema.config.sql @@ -70,7 +70,7 @@ CREATE TABLE config.upgrade_log ( install_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); -INSERT INTO config.upgrade_log (version) VALUES ('0503'); -- miker for tsbere +INSERT INTO config.upgrade_log (version) VALUES ('0504'); -- miker for tsbere CREATE TABLE config.bib_source ( id SERIAL PRIMARY KEY, diff --git a/Open-ILS/src/sql/Pg/010.schema.biblio.sql b/Open-ILS/src/sql/Pg/010.schema.biblio.sql index 428b575481..cf404f2f54 100644 --- a/Open-ILS/src/sql/Pg/010.schema.biblio.sql +++ b/Open-ILS/src/sql/Pg/010.schema.biblio.sql @@ -79,4 +79,30 @@ CREATE INDEX biblio_record_note_record_idx ON biblio.record_note ( record ); CREATE INDEX biblio_record_note_creator_idx ON biblio.record_note ( creator ); CREATE INDEX biblio_record_note_editor_idx ON biblio.record_note ( editor ); +CREATE TABLE biblio.monograph_part ( + id SERIAL PRIMARY KEY, + record BIGINT NOT NULL REFERENCES biblio.record_entry (id), + label TEXT NOT NULL, + label_sortkey TEXT NOT NULL, + CONSTRAINT record_label_unique UNIQUE (record,label) +); + +CREATE OR REPLACE FUNCTION biblio.normalize_biblio_monograph_part_sortkey () RETURNS TRIGGER AS $$ +BEGIN + NEW.label_sortkey := REGEXP_REPLACE( + lpad_number_substrings( + naco_normalize(NEW.label), + '0', + 10 + ), + E'\\s+', + '', + 'g' + ); + RETURN NEW; +END; +$$ LANGUAGE PLPGSQL; + +CREATE TRIGGER norm_sort_label BEFORE INSERT OR UPDATE ON biblio.monograph_part FOR EACH ROW EXECUTE PROCEDURE biblio.normalize_biblio_monograph_part_sortkey(); + COMMIT; diff --git a/Open-ILS/src/sql/Pg/040.schema.asset.sql b/Open-ILS/src/sql/Pg/040.schema.asset.sql index 6509bca19f..dbe0c09fb2 100644 --- a/Open-ILS/src/sql/Pg/040.schema.asset.sql +++ b/Open-ILS/src/sql/Pg/040.schema.asset.sql @@ -91,6 +91,13 @@ CREATE INDEX cp_editor_idx ON asset.copy ( editor ); CREATE INDEX cp_create_date ON asset.copy (create_date); CREATE RULE protect_copy_delete AS ON DELETE TO asset.copy DO INSTEAD UPDATE asset.copy SET deleted = TRUE WHERE OLD.id = asset.copy.id; +CREATE TABLE asset.copy_part_map ( + id SERIAL PRIMARY KEY, + target_copy BIGINT NOT NULL, -- points o asset.copy + part INT NOT NULL REFERENCES biblio.monograph_part (id) ON DELETE CASCADE +); +CREATE UNIQUE INDEX copy_part_map_cp_part_idx ON asset.copy_part_map (target_copy, part); + CREATE TABLE asset.opac_visible_copies ( id BIGINT primary key, -- copy id record BIGINT, @@ -279,6 +286,42 @@ INSERT INTO asset.call_number_class (name, normalizer, field) VALUES ('Library of Congress (LC)', 'asset.label_normalizer_lc', '050ab,055ab,090abef') ; +CREATE OR REPLACE FUNCTION asset.normalize_affix_sortkey () RETURNS TRIGGER AS $$ +BEGIN + NEW.label_sortkey := REGEXP_REPLACE( + lpad_number_substrings( + naco_normalize(NEW.label), + '0', + 10 + ), + E'\\s+', + '', + 'g' + ); + RETURN NEW; +END; +$$ LANGUAGE PLPGSQL; + +CREATE TABLE asset.call_number_prefix ( + id SERIAL PRIMARY KEY, + owning_lib INT NOT NULL REFERENCES actor.org_unit (id), + label TEXT NOT NULL, -- i18n + label_sortkey TEXT +); +CREATE TRIGGER prefix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_prefix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey(); +CREATE UNIQUE INDEX asset_call_number_prefix_once_per_lib ON asset.call_number_prefix (label, owning_lib); +CREATE INDEX asset_call_number_prefix_sortkey_idx ON asset.call_number_prefix (label_sortkey); + +CREATE TABLE asset.call_number_suffix ( + id SERIAL PRIMARY KEY, + owning_lib INT NOT NULL REFERENCES actor.org_unit (id), + label TEXT NOT NULL, -- i18n + label_sortkey TEXT +); +CREATE TRIGGER suffix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_suffix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey(); +CREATE UNIQUE INDEX asset_call_number_suffix_once_per_lib ON asset.call_number_suffix (label, owning_lib); +CREATE INDEX asset_call_number_suffix_sortkey_idx ON asset.call_number_suffix (label_sortkey); + CREATE TABLE asset.call_number ( id bigserial PRIMARY KEY, creator BIGINT NOT NULL, @@ -286,9 +329,11 @@ CREATE TABLE asset.call_number ( editor BIGINT NOT NULL, edit_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(), record bigint NOT NULL, - owning_lib INT NOT NULL, + owning_lib INT NOT NULL, label TEXT NOT NULL, deleted BOOL NOT NULL DEFAULT FALSE, + prefix INT NOT NULL DEFAULT -1 REFERENCES asset.call_number_prefix(id) DEFERRABLE INITIALLY DEFERRED, + suffix INT NOT NULL DEFAULT -1 REFERENCES asset.call_number_suffix(id) DEFERRABLE INITIALLY DEFERRED, label_class BIGINT DEFAULT 1 NOT NULL REFERENCES asset.call_number_class(id) DEFERRABLE INITIALLY DEFERRED, @@ -300,7 +345,7 @@ CREATE INDEX asset_call_number_editor_idx ON asset.call_number (editor); CREATE INDEX asset_call_number_dewey_idx ON asset.call_number (public.call_number_dewey(label)); CREATE INDEX asset_call_number_upper_label_id_owning_lib_idx ON asset.call_number (oils_text_as_bytea(label),id,owning_lib); CREATE INDEX asset_call_number_label_sortkey ON asset.call_number(oils_text_as_bytea(label_sortkey)); -CREATE UNIQUE INDEX asset_call_number_label_once_per_lib ON asset.call_number (record, owning_lib, label) WHERE deleted = FALSE OR deleted IS FALSE; +CREATE UNIQUE INDEX asset_call_number_label_once_per_lib ON asset.call_number (record, owning_lib, label, prefix, suffix) WHERE deleted = FALSE OR deleted IS FALSE; CREATE INDEX asset_call_number_label_sortkey_browse ON asset.call_number(oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib) WHERE deleted IS FALSE OR deleted = FALSE; CREATE RULE protect_cn_delete AS ON DELETE TO asset.call_number DO INSTEAD UPDATE asset.call_number SET deleted = TRUE WHERE OLD.id = asset.call_number.id; CREATE TRIGGER asset_label_sortkey_trigger diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index bad385a37a..549d03474e 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -1588,6 +1588,8 @@ INSERT INTO biblio.record_entry VALUES (-1,1,1,1,-1,NOW(),NOW(),FALSE,FALSE,'',' INSERT INTO asset.copy_location (id, name,owning_lib) VALUES (1, oils_i18n_gettext(1, 'Stacks', 'acpl', 'name'),1); SELECT SETVAL('asset.copy_location_id_seq'::TEXT, 100); +INSERT INTO asset.call_number_suffix (id, owning_lib, label) VALUES (-1, 1, ''); +INSERT INTO asset.call_number_prefix (id, owning_lib, label) VALUES (-1, 1, ''); INSERT INTO asset.call_number VALUES (-1,1,NOW(),1,NOW(),-1,1,'UNCATALOGED'); -- circ matrix @@ -7941,3 +7943,15 @@ INSERT INTO action_trigger.environment (event_def, path) VALUES (37, 'circ_lib.billing_address') ; +INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES ( + 'ui.cat.volume_copy_editor.horizontal', + oils_i18n_gettext( + 'ui.cat.volume_copy_editor.horizontal', + 'GUI: Horizontal layout for Volume/Copy Creator/Editor.', + 'coust', 'label'), + oils_i18n_gettext( + 'ui.cat.volume_copy_editor.horizontal', + 'The main entry point for this interface is in Holdings Maintenance, Actions for Selected Rows, Edit Item Attributes / Call Numbers / Replace Barcodes. This setting changes the top and bottom panes for that interface into left and right panes.', + 'coust', 'description'), + 'bool' +); diff --git a/Open-ILS/src/sql/Pg/990.schema.unapi.sql b/Open-ILS/src/sql/Pg/990.schema.unapi.sql index e7abed50b0..f84b3fe782 100644 --- a/Open-ILS/src/sql/Pg/990.schema.unapi.sql +++ b/Open-ILS/src/sql/Pg/990.schema.unapi.sql @@ -27,6 +27,8 @@ INSERT INTO unapi.bre_output_layout -- Dummy functions, so we can create the real ones out of order CREATE OR REPLACE FUNCTION unapi.aou ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION unapi.acnp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION unapi.acns ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.acn ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.ssub ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.sdist ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; @@ -44,6 +46,7 @@ CREATE OR REPLACE FUNCTION unapi.acl ( obj_id BIGINT, format TEXT, ename TEXT CREATE OR REPLACE FUNCTION unapi.ccs ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.bre ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION unapi.bmp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.holdings_xml ( bid BIGINT, ouid INT, org TEXT, depth INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[], slimit INT DEFAULT NULL, soffset INT DEFAULT NULL) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.biblio_record_entry_feed ( id_list BIGINT[], format TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL, title TEXT DEFAULT NULL, description TEXT DEFAULT NULL, creator TEXT DEFAULT NULL, update_ts TEXT DEFAULT NULL, unapi_url TEXT DEFAULT NULL, header_xml XML DEFAULT NULL ) RETURNS XML AS $F$ SELECT NULL::XML $F$ LANGUAGE SQL; @@ -230,6 +233,13 @@ CREATE OR REPLACE FUNCTION unapi.holdings_xml (bid BIGINT, ouid INT, org TEXT, d ORDER BY 1 )x) ), + CASE + WHEN ('bmp' = ANY ($5)) THEN + XMLELEMENT( name monograph_parts, + XMLAGG((SELECT unapi.bmp( id, 'xml', 'monograph_part', array_remove_item_by_value( array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7) FROM biblio.monograph_part WHERE record = $1)) + ) + ELSE NULL + END, CASE WHEN ('acn' = ANY ('{acn,auri}'::TEXT[] || $5)) THEN XMLELEMENT( name volumes, @@ -540,6 +550,39 @@ CREATE OR REPLACE FUNCTION unapi.ascecm ( obj_id BIGINT, format TEXT, ename TEX WHERE asce.id = $1; $F$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION unapi.bmp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name monograph_part, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + 'tag:open-ils.org:U2@bmp/' || id AS id, + id AS ident, + label, + label_sortkey, + 'tag:open-ils.org:U2@bre/' || record AS record + ), + CASE + WHEN ('acp' = ANY ($4)) THEN + XMLELEMENT( name copies, + (SELECT XMLAGG(acp) FROM ( + SELECT unapi.acp( cp.id, 'xml', 'copy', array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8) + FROM asset.copy cp + JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id) + WHERE cpm.part = $1 + ORDER BY COALESCE(cp.copy_number,0), cp.barcode + LIMIT $7 + OFFSET $8 + )x) + ) + ELSE NULL + END, + CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( record, 'marcxml', 'record', array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8) ELSE NULL END + ) + FROM biblio.monograph_part + WHERE id = $1 + GROUP BY id, label, label_sortkey, record; +$F$ LANGUAGE SQL; + CREATE OR REPLACE FUNCTION unapi.acp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ SELECT XMLELEMENT( name copy, @@ -565,10 +608,17 @@ CREATE OR REPLACE FUNCTION unapi.acp ( obj_id BIGINT, format TEXT, ename TEXT, XMLELEMENT( name statcats, CASE WHEN ('ascecm' = ANY ($4)) THEN - XMLAGG((SELECT unapi.acpn( stat_cat_entry, 'xml', 'statcat', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) FROM asset.stat_cat_entry_copy_map WHERE owning_copy = cp.id)) + XMLAGG((SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) FROM asset.stat_cat_entry_copy_map WHERE owning_copy = cp.id)) ELSE NULL END - ) + ), + CASE + WHEN ('bmp' = ANY ($4)) THEN + XMLELEMENT( name monograph_parts, + XMLAGG((SELECT unapi.bmp( part, 'xml', 'monograph_part', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) FROM asset.copy_part_map WHERE target_copy = cp.id)) + ) + ELSE NULL + END ) FROM asset.copy cp WHERE id = $1 @@ -645,12 +695,44 @@ CREATE OR REPLACE FUNCTION unapi.acn ( obj_id BIGINT, format TEXT, ename TEXT, name uris, (SELECT XMLAGG(auri) FROM (SELECT unapi.auri(uri,'xml','uri', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) FROM asset.uri_call_number_map WHERE call_number = acn.id)x) ), + CASE WHEN ('acnp' = ANY ($4)) THEN unapi.acnp( acn.prefix, 'marcxml', 'prefix', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) ELSE NULL END, + CASE WHEN ('acns' = ANY ($4)) THEN unapi.acns( acn.suffix, 'marcxml', 'suffix', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) ELSE NULL END, CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( acn.record, 'marcxml', 'record', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) ELSE NULL END ) AS x FROM asset.call_number acn JOIN actor.org_unit o ON (o.id = acn.owning_lib) WHERE acn.id = $1 - GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record; + GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix; +$F$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION unapi.acnp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name call_number_prefix, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + id AS ident, + label, + label_sortkey + ), + unapi.aou( owning_lib, $2, 'owning_lib', array_remove_item_by_value($4,'acnp'), $5, $6, $7, $8) + ) + FROM asset.call_number_prefix + WHERE id = $1; +$F$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION unapi.acns ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name call_number_suffix, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + id AS ident, + label, + label_sortkey + ), + unapi.aou( owning_lib, $2, 'owning_lib', array_remove_item_by_value($4,'acns'), $5, $6, $7, $8) + ) + FROM asset.call_number_suffix + WHERE id = $1; $F$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION unapi.auri ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ diff --git a/Open-ILS/src/sql/Pg/upgrade/0504.schema.parts_and_cnaffix.sql b/Open-ILS/src/sql/Pg/upgrade/0504.schema.parts_and_cnaffix.sql new file mode 100644 index 0000000000..5245e37f6c --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/0504.schema.parts_and_cnaffix.sql @@ -0,0 +1,321 @@ +BEGIN; + +INSERT INTO config.upgrade_log (version) VALUES ('0504'); -- miker + +CREATE TABLE biblio.monograph_part ( + id SERIAL PRIMARY KEY, + record BIGINT NOT NULL REFERENCES biblio.record_entry (id), + label TEXT NOT NULL, + label_sortkey TEXT NOT NULL, + CONSTRAINT record_label_unique UNIQUE (record,label) +); + +CREATE OR REPLACE FUNCTION biblio.normalize_biblio_monograph_part_sortkey () RETURNS TRIGGER AS $$ +BEGIN + NEW.label_sortkey := REGEXP_REPLACE( + lpad_number_substrings( + naco_normalize(NEW.label), + '0', + 10 + ), + E'\\s+', + '', + 'g' + ); + RETURN NEW; +END; +$$ LANGUAGE PLPGSQL; + +CREATE TRIGGER norm_sort_label BEFORE INSERT OR UPDATE ON biblio.monograph_part FOR EACH ROW EXECUTE PROCEDURE biblio.normalize_biblio_monograph_part_sortkey(); + +CREATE TABLE asset.copy_part_map ( + id SERIAL PRIMARY KEY, + target_copy BIGINT NOT NULL, -- points o asset.copy + part INT NOT NULL REFERENCES biblio.monograph_part (id) ON DELETE CASCADE +); +CREATE UNIQUE INDEX copy_part_map_cp_part_idx ON asset.copy_part_map (target_copy, part); + +CREATE OR REPLACE FUNCTION asset.normalize_affix_sortkey () RETURNS TRIGGER AS $$ +BEGIN + NEW.label_sortkey := REGEXP_REPLACE( + lpad_number_substrings( + naco_normalize(NEW.label), + '0', + 10 + ), + E'\\s+', + '', + 'g' + ); + RETURN NEW; +END; +$$ LANGUAGE PLPGSQL; + +CREATE TABLE asset.call_number_prefix ( + id SERIAL PRIMARY KEY, + owning_lib INT NOT NULL REFERENCES actor.org_unit (id), + label TEXT NOT NULL, -- i18n + label_sortkey TEXT +); +CREATE TRIGGER prefix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_prefix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey(); +CREATE UNIQUE INDEX asset_call_number_prefix_once_per_lib ON asset.call_number_prefix (label, owning_lib); +CREATE INDEX asset_call_number_prefix_sortkey_idx ON asset.call_number_prefix (label_sortkey); + +CREATE TABLE asset.call_number_suffix ( + id SERIAL PRIMARY KEY, + owning_lib INT NOT NULL REFERENCES actor.org_unit (id), + label TEXT NOT NULL, -- i18n + label_sortkey TEXT +); +CREATE TRIGGER suffix_normalize_tgr BEFORE INSERT OR UPDATE ON asset.call_number_suffix FOR EACH ROW EXECUTE PROCEDURE asset.normalize_affix_sortkey(); +CREATE UNIQUE INDEX asset_call_number_suffix_once_per_lib ON asset.call_number_suffix (label, owning_lib); +CREATE INDEX asset_call_number_suffix_sortkey_idx ON asset.call_number_suffix (label_sortkey); + +INSERT INTO asset.call_number_suffix (id, owning_lib, label) VALUES (-1, 1, ''); +INSERT INTO asset.call_number_prefix (id, owning_lib, label) VALUES (-1, 1, ''); + +DROP INDEX IF EXISTS asset.asset_call_number_label_once_per_lib; + +ALTER TABLE asset.call_number + ADD COLUMN prefix INT NOT NULL DEFAULT -1 REFERENCES asset.call_number_prefix(id) DEFERRABLE INITIALLY DEFERRED, + ADD COLUMN suffix INT NOT NULL DEFAULT -1 REFERENCES asset.call_number_suffix(id) DEFERRABLE INITIALLY DEFERRED; + +CREATE UNIQUE INDEX asset_call_number_label_once_per_lib ON asset.call_number (record, owning_lib, label, prefix, suffix) WHERE deleted = FALSE OR deleted IS FALSE; + +INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES ( + 'ui.cat.volume_copy_editor.horizontal', + oils_i18n_gettext( + 'ui.cat.volume_copy_editor.horizontal', + 'GUI: Horizontal layout for Volume/Copy Creator/Editor.', + 'coust', 'label'), + oils_i18n_gettext( + 'ui.cat.volume_copy_editor.horizontal', + 'The main entry point for this interface is in Holdings Maintenance, Actions for Selected Rows, Edit Item Attributes / Call Numbers / Replace Barcodes. This setting changes the top and bottom panes for that interface into left and right panes.', + 'coust', 'description'), + 'bool' +); + + +CREATE OR REPLACE FUNCTION unapi.bmp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name monograph_part, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + 'tag:open-ils.org:U2@bmp/' || id AS id, + id AS ident, + label, + label_sortkey, + 'tag:open-ils.org:U2@bre/' || record AS record + ), + CASE + WHEN ('acp' = ANY ($4)) THEN + XMLELEMENT( name copies, + (SELECT XMLAGG(acp) FROM ( + SELECT unapi.acp( cp.id, 'xml', 'copy', array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8) + FROM asset.copy cp + JOIN asset.copy_part_map cpm ON (cpm.target_copy = cp.id) + WHERE cpm.part = $1 + ORDER BY COALESCE(cp.copy_number,0), cp.barcode + LIMIT $7 + OFFSET $8 + )x) + ) + ELSE NULL + END, + CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( record, 'marcxml', 'record', array_remove_item_by_value($4,'bmp'), $5, $6, $7, $8) ELSE NULL END + ) + FROM biblio.monograph_part + WHERE id = $1 + GROUP BY id, label, label_sortkey, record; +$F$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION unapi.acnp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name call_number_prefix, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + id AS ident, + label, + label_sortkey + ), + unapi.aou( owning_lib, $2, 'owning_lib', array_remove_item_by_value($4,'acnp'), $5, $6, $7, $8) + ) + FROM asset.call_number_prefix + WHERE id = $1; +$F$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION unapi.acns ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name call_number_suffix, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + id AS ident, + label, + label_sortkey + ), + unapi.aou( owning_lib, $2, 'owning_lib', array_remove_item_by_value($4,'acns'), $5, $6, $7, $8) + ) + FROM asset.call_number_suffix + WHERE id = $1; +$F$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION unapi.holdings_xml (bid BIGINT, ouid INT, org TEXT, depth INT DEFAULT NULL, includes TEXT[] DEFAULT NULL::TEXT[], slimit INT DEFAULT NULL, soffset INT DEFAULT NULL) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name holdings, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + CASE WHEN ('bre' = ANY ('{acn,auri}'::TEXT[] || $5)) THEN 'tag:open-ils.org:U2@bre/' || $1 || '/' || $3 ELSE NULL END AS id + ), + 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) + ORDER BY 1 + )x) + ), + CASE + WHEN ('bmp' = ANY ($5)) THEN + XMLELEMENT( name monograph_parts, + XMLAGG((SELECT unapi.bmp( id, 'xml', 'monograph_part', array_remove_item_by_value( array_remove_item_by_value($5,'bre'), 'holdings_xml'), $3, $4, $6, $7) FROM biblio.monograph_part WHERE record = $1)) + ) + ELSE NULL + END, + CASE WHEN ('acn' = ANY ('{acn,auri}'::TEXT[] || $5)) THEN + XMLELEMENT( + name volumes, + (SELECT XMLAGG(acn) FROM ( + SELECT unapi.acn(acn.id,'xml','volume',array_remove_item_by_value(array_remove_item_by_value('{acn,auri}'::TEXT[] || $5,'holdings_xml'),'bre'), $3, $4, $6, $7) + FROM asset.call_number acn + WHERE acn.record = $1 + AND EXISTS ( + SELECT 1 + FROM asset.copy acp + JOIN actor.org_unit_descendants( + $2, + (COALESCE( + $4, + (SELECT aout.depth + FROM actor.org_unit_type aout + JOIN actor.org_unit aou ON (aou.ou_type = aout.id AND aou.id = $2) + ) + )) + ) aoud ON (acp.circ_lib = aoud.id) + LIMIT 1 + ) + ORDER BY label_sortkey + LIMIT $6 + OFFSET $7 + )x) + ) + ELSE NULL END, + CASE WHEN ('ssub' = ANY ('{acn,auri}'::TEXT[] || $5)) THEN + XMLELEMENT( + name subscriptions, + (SELECT XMLAGG(ssub) FROM ( + SELECT unapi.ssub(id,'xml','subscription','{}'::TEXT[], $3, $4, $6, $7) + FROM serial.subscription + WHERE record_entry = $1 + )x) + ) + ELSE NULL END + ); +$F$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION unapi.acp ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name copy, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + 'tag:open-ils.org:U2@acp/' || id AS id, + create_date, edit_date, copy_number, circulate, deposit, + ref, holdable, deleted, deposit_amount, price, barcode, + circ_modifier, circ_as_type, opac_visible + ), + unapi.ccs( status, $2, 'status', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8), + unapi.acl( location, $2, 'location', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8), + unapi.aou( circ_lib, $2, 'circ_lib', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8), + unapi.aou( circ_lib, $2, 'circlib', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8), + CASE WHEN ('acn' = ANY ($4)) THEN unapi.acn( call_number, $2, 'call_number', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) ELSE NULL END, + XMLELEMENT( name copy_notes, + CASE + WHEN ('acpn' = ANY ($4)) THEN + XMLAGG((SELECT unapi.acpn( id, 'xml', 'copy_note', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) FROM asset.copy_note WHERE owning_copy = cp.id AND pub)) + ELSE NULL + END + ), + XMLELEMENT( name statcats, + CASE + WHEN ('ascecm' = ANY ($4)) THEN + XMLAGG((SELECT unapi.ascecm( stat_cat_entry, 'xml', 'statcat', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) FROM asset.stat_cat_entry_copy_map WHERE owning_copy = cp.id)) + ELSE NULL + END + ), + CASE + WHEN ('bmp' = ANY ($4)) THEN + XMLELEMENT( name monograph_parts, + XMLAGG((SELECT unapi.bmp( part, 'xml', 'monograph_part', array_remove_item_by_value($4,'acp'), $5, $6, $7, $8) FROM asset.copy_part_map WHERE target_copy = cp.id)) + ) + ELSE NULL + END + ) + FROM asset.copy cp + WHERE id = $1 + GROUP BY id, status, location, circ_lib, call_number, create_date, edit_date, copy_number, circulate, deposit, ref, holdable, deleted, deposit_amount, price, barcode, circ_modifier, circ_as_type, opac_visible; +$F$ LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION unapi.acn ( obj_id BIGINT, format TEXT, ename TEXT, includes TEXT[], org TEXT, depth INT DEFAULT NULL, slimit INT DEFAULT NULL, soffset INT DEFAULT NULL ) RETURNS XML AS $F$ + SELECT XMLELEMENT( + name volume, + XMLATTRIBUTES( + 'http://open-ils.org/spec/holdings/v1' AS xmlns, + 'tag:open-ils.org:U2@acn/' || acn.id AS id, + o.shortname AS lib, + o.opac_visible AS opac_visible, + deleted, label, label_sortkey, label_class, record + ), + unapi.aou( owning_lib, $2, 'owning_lib', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8), + XMLELEMENT( name copies, + CASE + WHEN ('acp' = ANY ($4)) THEN + (SELECT XMLAGG(acp) FROM ( + SELECT unapi.acp( cp.id, 'xml', 'copy', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) + FROM asset.copy cp + JOIN actor.org_unit_descendants( + (SELECT id FROM actor.org_unit WHERE shortname = $5), + (COALESCE($6,(SELECT aout.depth FROM actor.org_unit_type aout JOIN actor.org_unit aou ON (aou.ou_type = aout.id AND aou.shortname = $5)))) + ) aoud ON (cp.circ_lib = aoud.id) + WHERE cp.call_number = acn.id + ORDER BY COALESCE(cp.copy_number,0), cp.barcode + LIMIT $7 + OFFSET $8 + )x) + ELSE NULL + END + ), + XMLELEMENT( + name uris, + (SELECT XMLAGG(auri) FROM (SELECT unapi.auri(uri,'xml','uri', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) FROM asset.uri_call_number_map WHERE call_number = acn.id)x) + ), + CASE WHEN ('acnp' = ANY ($4)) THEN unapi.acnp( acn.prefix, 'marcxml', 'prefix', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) ELSE NULL END, + CASE WHEN ('acns' = ANY ($4)) THEN unapi.acns( acn.suffix, 'marcxml', 'suffix', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) ELSE NULL END, + CASE WHEN ('bre' = ANY ($4)) THEN unapi.bre( acn.record, 'marcxml', 'record', array_remove_item_by_value($4,'acn'), $5, $6, $7, $8) ELSE NULL END + ) AS x + FROM asset.call_number acn + JOIN actor.org_unit o ON (o.id = acn.owning_lib) + WHERE acn.id = $1 + GROUP BY acn.id, o.shortname, o.opac_visible, deleted, label, label_sortkey, label_class, owning_lib, record, acn.prefix, acn.suffix; +$F$ LANGUAGE SQL; + + +COMMIT; + diff --git a/Open-ILS/web/js/ui/default/acq/common/li_table.js b/Open-ILS/web/js/ui/default/acq/common/li_table.js index acb96734d1..044335ef72 100644 --- a/Open-ILS/web/js/ui/default/acq/common/li_table.js +++ b/Open-ILS/web/js/ui/default/acq/common/li_table.js @@ -2467,7 +2467,6 @@ function AcqLiTable() { copyList.push(copy); } if (xulG) { - // If we need to, we can pass in an update_copy function to handle the update instead of volume_item_creator xulG.volume_item_creator( { 'existing_copies' : copyList } ); } } catch(E) { diff --git a/Open-ILS/web/js/ui/default/conify/global/config/acn_prefix.js b/Open-ILS/web/js/ui/default/conify/global/config/acn_prefix.js new file mode 100644 index 0000000000..2d307a8906 --- /dev/null +++ b/Open-ILS/web/js/ui/default/conify/global/config/acn_prefix.js @@ -0,0 +1,72 @@ +dojo.require('dojox.grid.DataGrid'); +dojo.require('openils.widget.AutoGrid'); +dojo.require('dojox.grid.cells.dijit'); +dojo.require('dojo.data.ItemFileWriteStore'); +dojo.require('dijit.form.CurrencyTextBox'); +dojo.require('dijit.Dialog'); +dojo.require('dojox.widget.PlaceholderMenuItem'); +dojo.require('fieldmapper.OrgUtils'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('openils.PermaCrud'); +dojo.require('openils.widget.OrgUnitFilteringSelect'); + +var thingContextOrg; +var thingList; + +/** really need to put this in a shared location... */ +function getOrgInfo(rowIndex, item) { + if(!item) return ''; + var orgId = this.grid.store.getValue(item, this.field); + return fieldmapper.aou.findOrgUnit(orgId).shortname(); +} + +function thingInit() { + + thingGrid.disableSelectorForRow = function(rowIdx) { + var item = thingGrid.getItem(rowIdx); + return (thingGrid.store.getValue(item, 'id') < 0); + } + + buildGrid(); + var connect = function() { + dojo.connect(thingContextOrgSelect, 'onChange', + function() { + thingContextOrg = this.getValue(); + thingGrid.resetStore(); + buildGrid(); + } + ); + }; + // go ahead and let staff see everything + new openils.User().buildPermOrgSelector('STAFF_LOGIN', thingContextOrgSelect, null, connect); +} + +function buildGrid() { + if(thingContextOrg == null) + thingContextOrg = openils.User.user.ws_ou(); + + fieldmapper.standardRequest( + ['open-ils.pcrud', 'open-ils.pcrud.search.acnp.atomic'], + { async: true, + params: [ + openils.User.authtoken, + {"owning_lib":fieldmapper.aou.descendantNodeList(thingContextOrg,true)}, + {"order_by":{"acnp":"label_sortkey"}} + ], + oncomplete: function(r) { + if(thingList = openils.Util.readResponse(r)) { + thingList = openils.Util.objectSort(thingList); + dojo.forEach(thingList, + function(e) { + thingGrid.store.newItem(acnp.toStoreItem(e)); + } + ); + } + } + } + ); +} + +openils.Util.addOnLoad(thingInit); + + diff --git a/Open-ILS/web/js/ui/default/conify/global/config/acn_suffix.js b/Open-ILS/web/js/ui/default/conify/global/config/acn_suffix.js new file mode 100644 index 0000000000..c79cd0e569 --- /dev/null +++ b/Open-ILS/web/js/ui/default/conify/global/config/acn_suffix.js @@ -0,0 +1,72 @@ +dojo.require('dojox.grid.DataGrid'); +dojo.require('openils.widget.AutoGrid'); +dojo.require('dojox.grid.cells.dijit'); +dojo.require('dojo.data.ItemFileWriteStore'); +dojo.require('dijit.form.CurrencyTextBox'); +dojo.require('dijit.Dialog'); +dojo.require('dojox.widget.PlaceholderMenuItem'); +dojo.require('fieldmapper.OrgUtils'); +dojo.require('dijit.form.FilteringSelect'); +dojo.require('openils.PermaCrud'); +dojo.require('openils.widget.OrgUnitFilteringSelect'); + +var thingContextOrg; +var thingList; + +/** really need to put this in a shared location... */ +function getOrgInfo(rowIndex, item) { + if(!item) return ''; + var orgId = this.grid.store.getValue(item, this.field); + return fieldmapper.aou.findOrgUnit(orgId).shortname(); +} + +function thingInit() { + + thingGrid.disableSelectorForRow = function(rowIdx) { + var item = thingGrid.getItem(rowIdx); + return (thingGrid.store.getValue(item, 'id') < 0); + } + + buildGrid(); + var connect = function() { + dojo.connect(thingContextOrgSelect, 'onChange', + function() { + thingContextOrg = this.getValue(); + thingGrid.resetStore(); + buildGrid(); + } + ); + }; + // go ahead and let staff see everything + new openils.User().buildPermOrgSelector('STAFF_LOGIN', thingContextOrgSelect, null, connect); +} + +function buildGrid() { + if(thingContextOrg == null) + thingContextOrg = openils.User.user.ws_ou(); + + fieldmapper.standardRequest( + ['open-ils.pcrud', 'open-ils.pcrud.search.acns.atomic'], + { async: true, + params: [ + openils.User.authtoken, + {"owning_lib":fieldmapper.aou.descendantNodeList(thingContextOrg,true)}, + {"order_by":{"acns":"label_sortkey"}} + ], + oncomplete: function(r) { + if(thingList = openils.Util.readResponse(r)) { + thingList = openils.Util.objectSort(thingList); + dojo.forEach(thingList, + function(e) { + thingGrid.store.newItem(acns.toStoreItem(e)); + } + ); + } + } + } + ); +} + +openils.Util.addOnLoad(thingInit); + + diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index f9fcd6eb83..e8bb029fb8 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -284,6 +284,8 @@ <!ENTITY staff.cat.opac.copy_browse.accesskey "H"> <!ENTITY staff.cat.opac.copy_browse.label "Holdings Maintenance"> <!ENTITY staff.cat.opac.default.label "Set bottom interface as Default"> +<!ENTITY staff.cat.opac.manage_parts.accesskey "P"> +<!ENTITY staff.cat.opac.manage_parts.label "Manage Parts"> <!ENTITY staff.cat.opac.marc_edit.accesskey "E"> <!ENTITY staff.cat.opac.marc_edit.label "MARC Edit"> <!ENTITY staff.cat.opac.marc_view.accesskey "V"> @@ -708,6 +710,8 @@ <!ENTITY staff.main.menu.admin.server_admin.conify.copy_status.label "Copy Statuses"> <!ENTITY staff.main.menu.admin.server_admin.conify.marc_record_attrs.label "MARC Record Attributes"> <!ENTITY staff.main.menu.admin.server_admin.conify.coded_value_maps.label "MARC Coded Value Maps"> +<!ENTITY staff.main.menu.admin.server_admin.conify.acn_prefix.label "Call Number Prefixes"> +<!ENTITY staff.main.menu.admin.server_admin.conify.acn_suffix.label "Call Number Suffixes"> <!ENTITY staff.main.menu.admin.server_admin.conify.billing_type.label "Billing Types"> <!ENTITY staff.main.menu.admin.server_admin.conify.z3950_source.label "Z39.50 Servers"> <!ENTITY staff.main.menu.admin.server_admin.conify.circulation_modifier.label "Circulation Modifiers"> @@ -2368,6 +2372,7 @@ <!ENTITY staff.cat.bib_brief.title.label "Title:"> <!ENTITY staff.cat.bib_brief.title.accesskey ""> <!ENTITY staff.cat.bib_brief.view_marc "View MARC"> +<!ENTITY staff.cat.bib_brief.add_volumes "Add Volumes"> <!ENTITY staff.cat.bib_brief.author.label "Author:"> <!ENTITY staff.cat.bib_brief.author.accesskey ""> <!ENTITY staff.cat.bib_brief.edition.label "Edition:"> @@ -2462,7 +2467,7 @@ <!ENTITY staff.cat.copy_browser.actions.cmd_create_brt.accesskey "K"> <!ENTITY staff.cat.copy_browser.actions.sel_patron.label "Show Last Few Circulations"> <!ENTITY staff.cat.copy_browser.actions.sel_patron.accesskey "L"> -<!ENTITY staff.cat.copy_browser.actions.cmd_edit_items.label "Edit Item Attributes"> +<!ENTITY staff.cat.copy_browser.actions.cmd_edit_items.label "Edit Item Attributes / Call Numbers / Replace Barcodes"> <!ENTITY staff.cat.copy_browser.actions.cmd_edit_items.accesskey "E"> <!ENTITY staff.cat.copy_browser.actions.cmd_transfer_items.label "Transfer Items to Previously Marked Volume"> <!ENTITY staff.cat.copy_browser.actions.cmd_transfer_items.accesskey "T"> @@ -2486,7 +2491,6 @@ <!ENTITY staff.cat.copy_browser.actions.sel_mark_items_missing.accesskey "g"> <!ENTITY staff.cat.copy_browser.actions.cmd_print_spine_labels.label "Print Item Spine Labels"> <!ENTITY staff.cat.copy_browser.actions.cmd_print_spine_labels.accesskey "P"> -<!ENTITY staff.cat.copy_browser.actions.cmd_replace_barcode.label "Replace Barcode"> <!ENTITY staff.cat.copy_browser.actions.save_columns.label "Save Columns"> <!ENTITY staff.cat.copy_browser.actions.cmd_refresh_list.label "Refresh Listing"> <!ENTITY staff.cat.copy_browser.actions.cmd_refresh_list.accesskey "R"> @@ -2513,7 +2517,7 @@ <!ENTITY staff.cat.copy_browser.holdings_maintenance.sel_patron.label "Show Last Few Circulations"> <!ENTITY staff.cat.copy_browser.holdings_maintenance.sel_patron.accesskey "L"> -<!ENTITY staff.cat.copy_browser.holdings_maintenance.cmd_edit_items.label "Edit Item Attributes"> +<!ENTITY staff.cat.copy_browser.holdings_maintenance.cmd_edit_items.label "Edit Item Attributes / Call Numbers / Replace Barcodes"> <!ENTITY staff.cat.copy_browser.holdings_maintenance.cmd_edit_items.accesskey "E"> <!ENTITY staff.cat.copy_browser.holdings_maintenance.cmd_transfer_items.label "Transfer Items to Previously Marked Volume"> <!ENTITY staff.cat.copy_browser.holdings_maintenance.cmd_transfer_items.accesskey "T"> @@ -2766,6 +2770,12 @@ <!ENTITY staff.cat.volume_copy_creator.print_labels.accesskey "P"> <!ENTITY staff.cat.volume_copy_creator.library_label.value "Library"> <!ENTITY staff.cat.volume_copy_creator.num_of_volumes_label.value "# of volumes"> +<!ENTITY staff.cat.volume_copy_creator.batch_bar "BATCH"> +<!ENTITY staff.cat.volume_copy_creator.batch_bar.call_number.classification "Classification:"> +<!ENTITY staff.cat.volume_copy_creator.batch_bar.call_number.prefix "Prefix:"> +<!ENTITY staff.cat.volume_copy_creator.batch_bar.call_number.label.label "Label:"> +<!ENTITY staff.cat.volume_copy_creator.batch_bar.call_number.label.accesskey "L"> +<!ENTITY staff.cat.volume_copy_creator.batch_bar.call_number.suffix "Suffix:"> <!ENTITY staff.cat.volume_editor.title "Volumes"> <!ENTITY staff.cat.volume_editor.caption.label "Volume Editor"> <!ENTITY staff.cat.volume_editor.modify.label "Modify"> @@ -2774,6 +2784,11 @@ <!ENTITY staff.cat.volume_editor.cancel.accesskey "C"> <!ENTITY staff.cat.volume_editor.automerge.label "Auto-Merge on Volume Collision"> <!ENTITY staff.cat.volume_editor.automerge.accesskey "A"> +<!ENTITY staff.cat.volume_editor.owning_lib "Owning lib"> +<!ENTITY staff.cat.volume_editor.classification "Classification"> +<!ENTITY staff.cat.volume_editor.prefix "Prefix"> +<!ENTITY staff.cat.volume_editor.label "Label"> +<!ENTITY staff.cat.volume_editor.suffix "Suffix"> <!ENTITY staff.cat.z3950.marc_import.label "MARC Import via Z39.50"> <!ENTITY staff.cat.z3950.marc_import.accesskey "I"> <!ENTITY staff.cat.z3950.service_credentials.label "Service and Credentials"> diff --git a/Open-ILS/web/opac/locale/en-US/opac.dtd b/Open-ILS/web/opac/locale/en-US/opac.dtd index aef4bbb430..c1ebf6bec9 100644 --- a/Open-ILS/web/opac/locale/en-US/opac.dtd +++ b/Open-ILS/web/opac/locale/en-US/opac.dtd @@ -202,6 +202,7 @@ avoid using bookbags all together. Thank you."> <!-- ================================================================= MyOPAC Holds Page ================================================================= --> + <!ENTITY myopac.holds.formats "Formats"> <!ENTITY myopac.holds.location "Pickup Location"> <!ENTITY myopac.holds.edit "Edit"> @@ -488,6 +489,7 @@ Please see a librarian to renew your account."> Rdetail ================================================================= --> <!ENTITY rdetail.print "print these details"> +<!ENTITY rdetail.cn.part "Part"> <!ENTITY rdetail.cn.barcode "Barcode"> <!ENTITY rdetail.cn.location "Location"> <!ENTITY rdetail.cn.hold.age "Age Hold Protection"> @@ -592,6 +594,7 @@ We recommend that you remove this title from any bookbags it may have been added <!ENTITY common.call.number.label "Call Number:"> <!ENTITY common.isbn.label "ISBN:"> <!ENTITY common.issn.label "ISSN:"> +<!ENTITY common.mono_parts.label "Monograph Parts:"> <!ENTITY common.copy.barcode.label "Copy Barcode:"> <!ENTITY common.issuance_label.label "Issuance Label:"> <!ENTITY common.hold.place "Place hold for my account"> diff --git a/Open-ILS/web/opac/skin/default/js/copy_details.js b/Open-ILS/web/opac/skin/default/js/copy_details.js index e5c50c0939..04e8e416f5 100644 --- a/Open-ILS/web/opac/skin/default/js/copy_details.js +++ b/Open-ILS/web/opac/skin/default/js/copy_details.js @@ -208,6 +208,7 @@ function cpdDrawCopies(r) { function cpdDrawCopy(r) { var copy = r.getResultObject(); var row = r.row; + var trow = r.args.templateRow; if (r.args.copy_location && copy.location().name() != r.args.copy_location) { hideMe(row); @@ -218,6 +219,18 @@ function cpdDrawCopy(r) { $n(row, 'location').appendChild(text(copy.location().name())); $n(row, 'status').appendChild(text(copy.status().name())); + // append comma-separated list of part this copy is linked to + if(copy.parts() && copy.parts().length) { + unHideMe($n(trow, 'copy_part_label')); + unHideMe($n(row, 'copy_part')); + for(var i = 0; i < copy.parts().length; i++) { + var part = copy.parts()[i]; + var node = $n(row, 'copy_part'); + if(i > 0) node.appendChild(text(',')); + node.appendChild(text(part.label())); + } + } + if(isXUL()) { /* show the hold link */ var l = $n(row, 'copy_hold_link'); diff --git a/Open-ILS/web/opac/skin/default/js/holds.js b/Open-ILS/web/opac/skin/default/js/holds.js index 748f98c27e..73514c1243 100644 --- a/Open-ILS/web/opac/skin/default/js/holds.js +++ b/Open-ILS/web/opac/skin/default/js/holds.js @@ -16,7 +16,8 @@ var holdTargetTypeMap = { T : 'record', V : 'volume', I : 'issuance', - C : 'copy' + C : 'copy', + P : 'part' }; @@ -228,8 +229,11 @@ function holdFetchObjects(hold, doneCallback) { } else if( type == 'I' ) { _h_set_issuance(args, doneCallback); + } else if( type == 'P' ) { + _h_set_parts(args, doneCallback); + } else { - if( type == 'T' ) { + if( type == 'T') { _h_set_rec(args, doneCallback); } else { _h_set_rec_descriptors(args, doneCallback); @@ -240,6 +244,25 @@ function holdFetchObjects(hold, doneCallback) { return args; } +function _h_set_parts(args, doneCallback) { + + var preq = new Request( + 'open-ils.fielder:open-ils.fielder.bmp.atomic', + {"cache":1, "fields":["label", "record"],"query": {"id":args.part}} + ); + + preq.callback( + function(r) { + var part = r.getResultObject()[0]; + args.record = part.record; + args.partObject = part; + _h_set_rec(args, doneCallback); + } + ); + + preq.send(); +} + function _h_set_vol(args, doneCallback) { if( args.volumeObject ) { @@ -291,10 +314,13 @@ function _h_set_rec(args, doneCallback) { else args.recordObject = findRecord( args.record, 'T' ); - if( args.type == 'T' || args.type == 'M' ) + if( args.type == 'T' || args.type == 'M' ) { _h_set_rec_descriptors(args, doneCallback); - else + //} else if(args.type == 'P') { + //_h_get_parts(args, doneCallback); + } else { if(doneCallback) doneCallback(args); + } } @@ -304,7 +330,7 @@ function _h_set_rec_descriptors(args, doneCallback) { args.pickup_lib = getSelectorVal($('holds_org_selector')); if(args.pickup_lib === null) - args.pickup_lib = holdArgs.recipient.home_ou(); + args.pickup_lib = args.recipient.home_ou(); // grab the list of record desciptors attached to this records metarecord if( ! args.recordDescriptors ) { @@ -332,23 +358,47 @@ function _h_set_rec_descriptors(args, doneCallback) { req.callback( function(r) { var data = r.getResultObject(); - holdArgs.recordDescriptors = args.recordDescriptors = data.descriptors; - holdArgs.metarecord = args.metarecord = data.metarecord; + args.recordDescriptors = args.recordDescriptors = data.descriptors; + args.metarecord = args.metarecord = data.metarecord; if( args.type == 'M' && ! args.metarecordObject) - holdArgs.metarecordObject = args.metarecordObject = findRecord(args.metarecord, 'M'); + args.metarecordObject = args.metarecordObject = findRecord(args.metarecord, 'M'); - if(doneCallback) doneCallback(args); + _h_get_parts(args, doneCallback); } ); req.send(); } else { - if(doneCallback) doneCallback(args); + _h_get_parts(args, doneCallback); } return args; } +function _h_get_parts(args, doneCallback) { + + if(args.type == 'M' || args.editHold || args.holdParts) { + if(doneCallback) + doneCallback(args); + + } else { + + var req = new Request( + 'open-ils.search:open-ils.search.biblio.record_hold_parts', + {pickup_lib: args.pickup_lib, record: args.record} + ); + + req.callback( + function(r) { + args.recordParts = r.getResultObject(); + if(doneCallback) + doneCallback(args); + } + ); + req.send(); + } +} + function holdsDrawWindow() { @@ -453,6 +503,30 @@ function __holdsDrawWindow() { hideMe($('holds_issuance_row')); } + if(holdArgs.recordParts && holdArgs.recordParts.length) { + var selector = $('holds_parts_selector'); + unHideMe($('holds_parts_row')); + unHideMe(selector); + + var nodeList = []; + dojo.forEach(selector.options, + function(node) { if(node.value != '') nodeList.push(node) } ); + + dojo.forEach(nodeList, function(node) { selector.removeChild(node); }); + + dojo.forEach( + holdArgs.recordParts, + function(part) { + insertSelectorVal(selector, -1, part.label, part.id); + } + ); + + } else if(holdArgs.type == 'P') { + unHideMe($('holds_parts_row')); + unHideMe($('holds_parts_label')); + appendClear( $('holds_parts_label'), text(holdArgs.partObject.label)); + } + removeChildren($('holds_format')); var mods_formats = rec.types_of_resource(); @@ -623,6 +697,20 @@ function holdsSetFormatSelector() { if(type=='M') opt.selected=true; unHideMe(opt); } + + // If the user selects a format, P-type holds are no longer an option + // disable and reset the P-type form control + selector.onchange = function() { + var partsSel = $('holds_parts_selector'); + for(var i = 0; i < selector.options.length; i++) { + if(selector.options[i].selected) { + partsSel.selectedIndex = 0; // none selected + partsSel.disabled = true; + return; + } + } + partsSel.disabled = false; + } } function findFormatSelectorOptByParts( sel, val ) { @@ -747,7 +835,8 @@ function holdsCheckPossibility(pickuplib, hold, recurse) { hold_type : holdArgs.type, patronid : holdArgs.recipient.id(), depth : 0, - pickup_lib : pickuplib + pickup_lib : pickuplib, + partid : holdArgs.part }; if(recurse) { @@ -839,8 +928,16 @@ function holdsBuildHoldFromWindow() { else hold.email_notify(0); + var part = getSelectorVal($('holds_parts_selector')); + if(part) { + holdArgs.type = 'P'; + holdArgs.part = part; + } + var target = holdArgs[holdTargetTypeMap[holdArgs.type]]; + // a mono part is selected + hold.pickup_lib(org); //hold.request_lib(org); hold.requestor(holdArgs.requestor.id()); diff --git a/Open-ILS/web/opac/skin/default/js/myopac.js b/Open-ILS/web/opac/skin/default/js/myopac.js index 6fd49f99c8..819f8aa048 100644 --- a/Open-ILS/web/opac/skin/default/js/myopac.js +++ b/Open-ILS/web/opac/skin/default/js/myopac.js @@ -499,7 +499,7 @@ function myOShowHoldStatus(r) { function myOPACDrawHoldTitle(hold) { var method; - if( hold.hold_type() == 'T' || hold.hold_type() == 'M' ) { + if( hold.hold_type() == 'T' || hold.hold_type() == 'M') { if(hold.hold_type() == "M") method = FETCH_MRMODS; if(hold.hold_type() == "T") method = FETCH_RMODS; var req = new Request(method, hold.target()); @@ -521,7 +521,7 @@ function myOPACFleshHoldTitle(r) { function _myOPACFleshHoldTitle(hold, holdObjects) { - var record = holdObjects.recordObject; + var record = holdObjects.recordObject; var volume = holdObjects.volumeObject; var copy = holdObjects.copyObject; @@ -539,6 +539,11 @@ function _myOPACFleshHoldTitle(hold, holdObjects) { buildTitleDetailLink(record, title_link); buildSearchLink(STYPE_AUTHOR, record.author(), author_link); + if(hold.hold_type() == 'P') { + unHideMe($n(row, 'vol_copy')); + $n(row, 'part').appendChild(text(holdObjects.partObject.label)); + } + if( volume ) { $n(row, 'volume').appendChild(text(volume.label())); unHideMe($n(row, 'vol_copy')); diff --git a/Open-ILS/web/opac/skin/default/js/rresult.js b/Open-ILS/web/opac/skin/default/js/rresult.js index ff94bb8158..6dccc497ec 100644 --- a/Open-ILS/web/opac/skin/default/js/rresult.js +++ b/Open-ILS/web/opac/skin/default/js/rresult.js @@ -279,6 +279,17 @@ function rresultCollectRecords(ids, base) { runEvt("result", "preCollectRecords"); var x = 0; + // don't perform rdetail redirect if user was on rdetail and cliecked Back + if(findCurrentPage() == RRESULT && isXUL()) { + if(ids.length == 1 && !xulG.fromBack) { + var args = {}; + args.page = RDETAIL; + args[PARAM_OFFSET] = 0; + args[PARAM_RID] = ids[0]; + location.href = buildOPACLink(args); + } + } + if (!base) base = 0; if( rresultIsPaged ) base = 0; diff --git a/Open-ILS/web/opac/skin/default/xml/common/holds.xml b/Open-ILS/web/opac/skin/default/xml/common/holds.xml index 962a366fbe..7d8712d235 100644 --- a/Open-ILS/web/opac/skin/default/xml/common/holds.xml +++ b/Open-ILS/web/opac/skin/default/xml/common/holds.xml @@ -51,6 +51,16 @@ <td class='holds_cell' id='holds_physical_desc'> </td> </tr> + <tr class='hide_me' id='holds_parts_row'> + <td class='holds_cell'>&common.mono_parts.label;</td> + <td class='holds_cell'> + <span class='hide_me' id='holds_parts_label'></span> + <select id='holds_parts_selector' class='hide_me'> + <option value=''></option> + </select> + </td> + </tr> + <tr class='hide_me' id='holds_cn_row'> <td class='holds_cell'>&common.call.number.label;</td> <td class='holds_cell'><b id='holds_cn'/> </td> diff --git a/Open-ILS/web/opac/skin/default/xml/myopac/myopac_holds.xml b/Open-ILS/web/opac/skin/default/xml/myopac/myopac_holds.xml index 4d5b46db66..08d16906d1 100644 --- a/Open-ILS/web/opac/skin/default/xml/myopac/myopac_holds.xml +++ b/Open-ILS/web/opac/skin/default/xml/myopac/myopac_holds.xml @@ -71,8 +71,9 @@ <td name='myopac_holds_title'> <a href='javascript:void(0);' name='myopac_holds_title_link'> </a> <div name='vol_copy' style='border: 1px solid #808080; width:98%; margin-top: 2px;' class='hide_me'> - <div style='font-size: 90%' name='volume'/> - <div style='font-size: 90%' name='copy'/> + <div style='font-size: 90%' name='part'></div> + <div style='font-size: 90%' name='volume'></div> + <div style='font-size: 90%' name='copy'></div> </div> </td> diff --git a/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml b/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml index 09765fd4e9..45a5c85d5d 100644 --- a/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml +++ b/Open-ILS/web/opac/skin/default/xml/rdetail/rdetail_cn_details.xml @@ -13,6 +13,7 @@ <td width='33%'>&rdetail.cn.barcode;</td> <td>&common.status;</td> <td>&rdetail.cn.location;</td> + <td name='copy_part_label' class='hide_me'>&rdetail.cn.part;</td> <td name='age_protect_label' class='hide_me'>&rdetail.cn.hold.age;</td> <td name='create_date_label' class='hide_me'>&rdetail.cn.genesis;</td> <td name='holdable_label' class='hide_me'>&rdetail.cn.holdable;</td> @@ -35,6 +36,7 @@ <td name='status'> </td> <td name='location'> </td> + <td name='copy_part' class='hide_me'> </td> <td name='age_protect_value' class='hide_me'>&rdetail.cn.disabled;</td> <td name='create_date_value' class='hide_me'> </td> diff --git a/Open-ILS/web/templates/default/conify/global/biblio/monograph_part.tt2 b/Open-ILS/web/templates/default/conify/global/biblio/monograph_part.tt2 new file mode 100644 index 0000000000..ee1256b7e4 --- /dev/null +++ b/Open-ILS/web/templates/default/conify/global/biblio/monograph_part.tt2 @@ -0,0 +1,37 @@ +[% WRAPPER default/base.tt2 %] +[% ctx.page_title = 'Configure Monograph Parts' %] +<div dojoType="dijit.layout.ContentPane" layoutAlign="client"> + <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'> + <div>Monograph Parts</div> + <div> + <button dojoType='dijit.form.Button' onClick='monoPartGrid.showCreateDialog()'>New Monograph Part</button> + <button dojoType='dijit.form.Button' onClick='monoPartGrid.deleteSelected()'>Delete Selected</button> + </div> + </div> + <div> + <table jsId="monoPartGrid" + dojoType="openils.widget.AutoGrid" + autoHeight='true' + fieldOrder="['label']" + suppressFields="['id','record','label_sortkey']" + suppressEditFields="['id','label_sortkey']" + query="{id: null}" + fmClass='bmp' + editOnEnter='true'/> +</div> + +<script type="text/javascript"> + dojo.require('openils.CGI'); + dojo.require('openils.Util'); + dojo.require('openils.widget.AutoGrid'); + + var cgi = new openils.CGI(); + openils.Util.addOnLoad( function() { + monoPartGrid.overrideEditWidgets.record = new dijit.form.TextBox({"disabled": true}); + monoPartGrid.overrideEditWidgets.record.shove = { create : cgi.param('r') }; + monoPartGrid.loadAll({order_by : {bmp : 'label'}}, {record : cgi.param('r')}); + }); +</script> +[% END %] + + diff --git a/Open-ILS/web/templates/default/conify/global/config/acn_prefix.tt2 b/Open-ILS/web/templates/default/conify/global/config/acn_prefix.tt2 new file mode 100644 index 0000000000..5e7f64080a --- /dev/null +++ b/Open-ILS/web/templates/default/conify/global/config/acn_prefix.tt2 @@ -0,0 +1,37 @@ +[% WRAPPER default/base.tt2 %] +[% ctx.page_title = 'Call Number Prefixes' %] +<script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/conify/global/config/acn_prefix.js'> </script> + +<!-- grid --> + + <div dojoType="dijit.layout.ContentPane" layoutAlign="client"> + <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'> + <div>Call Number Prefixes</div> + <div> + <button dojoType='dijit.form.Button' onClick='thingGrid.showCreateDialog()'>New Prefix</button> + <button dojoType='dijit.form.Button' onClick='thingGrid.deleteSelected()'>Delete Selected</button> + </div> + </div> + <div> + <span>Context Org Unit</span> + <select dojoType="openils.widget.OrgUnitFilteringSelect" jsId='thingContextOrgSelect' + searchAttr='shortname' labelAttr='shortname'> </select> + </div> + <table jsId="thingGrid" + dojoType="openils.widget.AutoGrid" + fieldOrder="['id', 'label', 'owning_lib']" + suppressFields="['label_sortkey']" + suppressEditFields="['label_sortkey']" + query="{id: '*'}" + defaultCellWidth='20' + fmClass='acnp' + editOnEnter='true'> + <thead> + <tr><th field='owning_lib' get='getOrgInfo'/></tr> + </thead> + </table> + </div> +</div> +[% END %] + + diff --git a/Open-ILS/web/templates/default/conify/global/config/acn_suffix.tt2 b/Open-ILS/web/templates/default/conify/global/config/acn_suffix.tt2 new file mode 100644 index 0000000000..40dcbb962f --- /dev/null +++ b/Open-ILS/web/templates/default/conify/global/config/acn_suffix.tt2 @@ -0,0 +1,37 @@ +[% WRAPPER default/base.tt2 %] +[% ctx.page_title = 'Call Number Suffixes' %] +<script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/conify/global/config/acn_suffix.js'> </script> + +<!-- grid --> + + <div dojoType="dijit.layout.ContentPane" layoutAlign="client"> + <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'> + <div>Call Number Suffixes</div> + <div> + <button dojoType='dijit.form.Button' onClick='thingGrid.showCreateDialog()'>New Suffix</button> + <button dojoType='dijit.form.Button' onClick='thingGrid.deleteSelected()'>Delete Selected</button> + </div> + </div> + <div> + <span>Context Org Unit</span> + <select dojoType="openils.widget.OrgUnitFilteringSelect" jsId='thingContextOrgSelect' + searchAttr='shortname' labelAttr='shortname'> </select> + </div> + <table jsId="thingGrid" + dojoType="openils.widget.AutoGrid" + fieldOrder="['id', 'label', 'owning_lib']" + suppressFields="['label_sortkey']" + suppressEditFields="['label_sortkey']" + query="{id: '*'}" + defaultCellWidth='20' + fmClass='acns' + editOnEnter='true'> + <thead> + <tr><th field='owning_lib' get='getOrgInfo'/></tr> + </thead> + </table> + </div> +</div> +[% END %] + + diff --git a/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js b/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js index 6db3c02064..e66fd4a75a 100644 --- a/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js +++ b/Open-ILS/xul/staff_client/chrome/content/OpenILS/data.js @@ -534,6 +534,9 @@ OpenILS.data.prototype = { } } + // If we don't clear these, then things like obj.list['acnp_for_lib_1'] may stick around + obj.hash = {}; obj.list = {}; + this.chain = []; this.chain.push( @@ -598,6 +601,27 @@ OpenILS.data.prototype = { this.chain.push( function() { var f = gen_fm_retrieval_func( + 'acnc', + [ + api.FM_ACNC_RETRIEVE_VIA_PCRUD.app, + api.FM_ACNC_RETRIEVE_VIA_PCRUD.method, + [ obj.session.key, {"id":{"!=":null}}, {"order_by":{"acnc":"name"}} ], + false + ] + ); + try { + f(); + } catch(E) { + var error = 'Error: ' + js2JSON(E); + obj.error.sdump('D_ERROR',error); + throw(E); + } + } + ); + + this.chain.push( + function() { + var f = gen_fm_retrieval_func( 'ahrcc', [ api.FM_AHRCC_PCRUD_SEARCH.app, @@ -827,7 +851,6 @@ OpenILS.data.prototype = { } ); - this.chain.push( function() { var f = gen_fm_retrieval_func( @@ -852,6 +875,50 @@ OpenILS.data.prototype = { this.chain.push( function() { var f = gen_fm_retrieval_func( + 'acnp', + [ + api.FM_ACNP_RETRIEVE_VIA_PCRUD.app, + api.FM_ACNP_RETRIEVE_VIA_PCRUD.method, + [ obj.session.key, {"owning_lib":{"=":obj.list.au[0].ws_ou()}}, {"order_by":{"acnp":"label_sortkey"}} ], + false + ] + ); + try { + f(); + obj.list['acnp_for_lib_'+obj.list.au[0].ws_ou()] = obj.list.acnp; + } catch(E) { + var error = 'Error: ' + js2JSON(E); + obj.error.sdump('D_ERROR',error); + throw(E); + } + } + ); + + this.chain.push( + function() { + var f = gen_fm_retrieval_func( + 'acns', + [ + api.FM_ACNS_RETRIEVE_VIA_PCRUD.app, + api.FM_ACNS_RETRIEVE_VIA_PCRUD.method, + [ obj.session.key, {"owning_lib":{"=":obj.list.au[0].ws_ou()}}, {"order_by":{"acns":"label_sortkey"}} ], + false + ] + ); + try { + f(); + obj.list['acns_for_lib_'+obj.list.au[0].ws_ou()] = obj.list.acns; + } catch(E) { + var error = 'Error: ' + js2JSON(E); + obj.error.sdump('D_ERROR',error); + throw(E); + } + } + ); + + this.chain.push( + function() { + var f = gen_fm_retrieval_func( 'cbt', [ api.FM_CBT_RETRIEVE.app, diff --git a/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js b/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js index 364d573121..befb693aec 100644 --- a/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js +++ b/Open-ILS/xul/staff_client/chrome/content/OpenILS/global_util.js @@ -413,6 +413,12 @@ function update_modal_xulG(v) { try { + if (typeof xulG != "undefined" && xulG.not_modal) { + xulG = v; + xulG.not_modal = true; + return; + } + JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'}); var key = location.pathname + location.search + location.hash; if (typeof data.modal_xulG_stack != 'undefined' && typeof data.modal_xulG_stack[key] != 'undefined') { @@ -453,11 +459,15 @@ } if (typeof _params.no_xulG == 'undefined') { if (typeof _params.modal_xulG != 'undefined') { - JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'}); - var key = location.pathname + location.search + location.hash; - //dump('xul_param, considering modal key = ' + key + '\n'); - if (typeof data.modal_xulG_stack != 'undefined' && typeof data.modal_xulG_stack[key] != 'undefined') { - xulG = data.modal_xulG_stack[key][ data.modal_xulG_stack[key].length - 1 ]; + if (typeof xulG != 'undefined' && xulG.not_modal) { + // for interfaces that used to be modal but aren't now, do nothing + } else { + JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'}); + var key = location.pathname + location.search + location.hash; + //dump('xul_param, considering modal key = ' + key + '\n'); + if (typeof data.modal_xulG_stack != 'undefined' && typeof data.modal_xulG_stack[key] != 'undefined') { + xulG = data.modal_xulG_stack[key][ data.modal_xulG_stack[key].length - 1 ]; + } } } if (typeof xulG == 'object' && typeof xulG[ param_name ] != 'undefined') { diff --git a/Open-ILS/xul/staff_client/chrome/content/cat/opac.js b/Open-ILS/xul/staff_client/chrome/content/cat/opac.js index b5b15aba55..7c64f928d8 100644 --- a/Open-ILS/xul/staff_client/chrome/content/cat/opac.js +++ b/Open-ILS/xul/staff_client/chrome/content/cat/opac.js @@ -89,18 +89,27 @@ function opac_wrapper_set_help_context() { function set_brief_view() { var url = xulG.url_prefix( urls.XUL_BIB_BRIEF ) + '?docid=' + window.escape(docid); dump('spawning ' + url + '\n'); + + var content_params = { + 'set_tab_name' : function(n) { + if (typeof window.xulG == 'object' && typeof window.xulG.set_tab_name == 'function') { + try { window.xulG.set_tab_name(document.getElementById('offlineStrings').getFormattedString("cat.bib_record", [n])); } catch(E) { alert(E); } + } else { + dump('no set_tab_name\n'); + } + } + }; + + ["url_prefix", "new_tab", "set_tab", "close_tab", "new_patron_tab", + "set_patron_tab", "volume_item_creator", "get_new_session", + "holdings_maintenance_tab", "open_chrome_window", "url_prefix", + "network_meter", "page_meter", "set_statusbar", "set_help_context" + ].forEach(function(k) { content_params[k] = xulG[k]; }); + top_pane.set_iframe( url, - {}, - { - 'set_tab_name' : function(n) { - if (typeof window.xulG == 'object' && typeof window.xulG.set_tab_name == 'function') { - try { window.xulG.set_tab_name(document.getElementById('offlineStrings').getFormattedString("cat.bib_record", [n])); } catch(E) { alert(E); } - } else { - dump('no set_tab_name\n'); - } - } - } + {}, + content_params ); } @@ -172,13 +181,13 @@ function set_marc_edit() { JSAN.use('util.error'); error = new util.error(); JSAN.use('util.network'); var network = new util.network(); - var acn_id = network.simple_request( + var acn_blob = network.simple_request( 'FM_ACN_FIND_OR_CREATE', [ ses(), cn_label, doc_id, ses('ws_ou') ] ); - if (typeof acn_id.ilsevent != 'undefined') { - error.standard_unexpected_error_alert('Error in chrome/content/cat/opac.js, cat.util.fast_item_add', acn_id); + if (typeof acn_blob.ilsevent != 'undefined') { + error.standard_unexpected_error_alert('Error in chrome/content/cat/opac.js, cat.util.fast_item_add', acn_blob); return; } @@ -186,7 +195,7 @@ function set_marc_edit() { copy_obj.id( -1 ); copy_obj.isnew('1'); copy_obj.barcode( cp_barcode ); - copy_obj.call_number( acn_id ); + copy_obj.call_number( acn_blob.acn_id ); copy_obj.circ_lib( ses('ws_ou') ); /* FIXME -- use constants */ copy_obj.deposit(0); @@ -822,8 +831,10 @@ function add_volumes() { var title = document.getElementById('offlineStrings').getFormattedString('staff.circ.copy_status.add_volumes.title', [docid]); + var horizontal_interface = String( g.data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true'; + var url = window.xulG.url_prefix( horizontal_interface ? urls.XUL_VOLUME_COPY_CREATOR_HORIZONTAL : urls.XUL_VOLUME_COPY_CREATOR ); var w = xulG.new_tab( - window.xulG.url_prefix(urls.XUL_VOLUME_COPY_CREATOR), + url, { 'tab_name' : title }, { 'doc_id' : docid, 'ou_ids' : [ ses('ws_ou') ] } ); @@ -831,3 +842,20 @@ function add_volumes() { alert('Error in chrome/content/cat/opac.js, add_volumes(): ' + E); } } + +function manage_parts() { + try { + var title = document.getElementById('offlineStrings').getFormattedString('staff.cat.manage_parts.title', [docid]); + var loc = urls.XUL_BROWSER + "?url=" + window.escape( + window.xulG.url_prefix(urls.CONIFY_MANAGE_PARTS) + '?r=' + docid + ); + var w = xulG.new_tab( + loc, + { 'tab_name' : title }, + {} + ); + } catch(E) { + alert('Error in chrome/content/cat/opac.js, manage_parts(): ' + E); + } +} + diff --git a/Open-ILS/xul/staff_client/chrome/content/cat/opac.xul b/Open-ILS/xul/staff_client/chrome/content/cat/opac.xul index 5197d89c85..9e8549d53e 100644 --- a/Open-ILS/xul/staff_client/chrome/content/cat/opac.xul +++ b/Open-ILS/xul/staff_client/chrome/content/cat/opac.xul @@ -59,6 +59,7 @@ <menuitem label="&staff.cat.copy_browser.holdings_maintenance.cmd_add_volumes.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.cmd_add_volumes.accesskey;" id="add_volumes" oncommand="add_volumes();"/> <menuitem label="&staff.cat.opac.mark_for_hold_transfer.label;" accesskey="&staff.cat.opac.mark_for_hold_transfer.accesskey;" id="mark_for_hold_transfer" oncommand="mark_for_hold_transfer();"/> <menuitem label="&staff.cat.opac.transfer_title_holds.label;" accesskey="&staff.cat.opac.transfer_title_holds.accesskey;" id="transfer_title_holds" oncommand="transfer_title_holds();"/> + <menuitem label="&staff.cat.opac.manage_parts.label;" accesskey="&staff.cat.opac.manage_parts.accesskey;" id="manage_parts" oncommand="manage_parts();"/> <menuseparator/> <menuitem label="&staff.cat.opac.bib_in_new_tab.label;" id="bib_in_new_tab" oncommand="bib_in_new_tab();"/> <menuitem label="&staff.cat.opac.remove_me.label;" id="remove_me" oncommand="remove_me();"/> diff --git a/Open-ILS/xul/staff_client/chrome/content/main/constants.js b/Open-ILS/xul/staff_client/chrome/content/main/constants.js index 8e8663edfd..802592d185 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/constants.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/constants.js @@ -81,13 +81,16 @@ var api = { 'CHECKOUT_RENEW' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.renew' }, 'CIRC_MODIFIER_LIST' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.circ_modifier.retrieve.all' }, 'CLEAR_HOLD_SHELF' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.hold.clear_shelf.process', 'secure' : false }, - 'FM_ACN_RETRIEVE' : { 'app' : 'open-ils.search', 'method' : 'open-ils.search.callnumber.retrieve', 'secure' : false }, - 'FM_ACN_RETRIEVE.authoritative' : { 'app' : 'open-ils.search', 'method' : 'open-ils.search.callnumber.retrieve.authoritative', 'secure' : false }, + 'FM_ACN_RETRIEVE' : { 'app' : 'open-ils.search', 'method' : 'open-ils.search.callnumber.fleshed.retrieve', 'secure' : false }, + 'FM_ACN_RETRIEVE.authoritative' : { 'app' : 'open-ils.search', 'method' : 'open-ils.search.callnumber.fleshed.retrieve.authoritative', 'secure' : false }, 'FM_ACN_TREE_UPDATE' : { 'app' : 'open-ils.cat', 'method' : 'open-ils.cat.asset.volume.fleshed.batch.update' }, 'FM_ACN_TREE_LIST_RETRIEVE_VIA_RECORD_ID_AND_ORG_IDS' : { 'app' : 'open-ils.cat', 'method' : 'open-ils.cat.asset.copy_tree.retrieve', 'secure' : false }, 'FM_ACN_TREE_LIST_RETRIEVE_VIA_RECORD_ID_AND_ORG_IDS.authoritative' : { 'app' : 'open-ils.cat', 'method' : 'open-ils.cat.asset.copy_tree.retrieve.authoritative', 'secure' : false }, 'FM_ACN_TRANSFER' : { 'app' : 'open-ils.cat', 'method' : 'open-ils.cat.asset.volume.batch.transfer' }, - 'FM_ACN_FIND_OR_CREATE' : { 'app' : 'open-ils.cat', 'method' : 'open-ils.cat.call_number.find_or_create', 'secure' : false }, + 'FM_ACN_FIND_OR_CREATE' : { 'app' : 'open-ils.cat', 'method' : 'open-ils.cat.call_number.find_or_create' }, + 'FM_ACNC_RETRIEVE_VIA_PCRUD' : { 'app' : 'open-ils.pcrud', 'method' : 'open-ils.pcrud.search.acnc.atomic' }, + 'FM_ACNP_RETRIEVE_VIA_PCRUD' : { 'app' : 'open-ils.pcrud', 'method' : 'open-ils.pcrud.search.acnp.atomic' }, + 'FM_ACNS_RETRIEVE_VIA_PCRUD' : { 'app' : 'open-ils.pcrud', 'method' : 'open-ils.pcrud.search.acns.atomic' }, 'FM_ACP_DETAILS' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.copy_details.retrieve' }, 'FM_ACP_DETAILS_VIA_BARCODE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.copy_details.retrieve.barcode' }, 'FM_ACP_DETAILS_VIA_BARCODE.authoritative' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.copy_details.retrieve.barcode.authoritative' }, @@ -471,7 +474,9 @@ var urls = { 'XUL_USER_BUCKETS' : '/xul/server/patron/user_buckets.xul', 'XUL_VERIFY_CREDENTIALS' : '/xul/server/main/verify_credentials.xul', 'XUL_VOLUME_BUCKETS' : '/xul/server/cat/volume_buckets.xul', - 'XUL_VOLUME_COPY_CREATOR' : '/xul/server/cat/volume_copy_creator.xul', + 'XUL_VOLUME_COPY_CREATOR' : '/xul/server/cat/volume_copy_editor.xul', + 'XUL_VOLUME_COPY_CREATOR_HORIZONTAL' : '/xul/server/cat/volume_copy_editor_horiz.xul', + 'XUL_VOLUME_COPY_CREATOR_ORIGINAL' : '/xul/server/cat/volume_copy_creator.xul', 'XUL_VOLUME_EDITOR' : '/xul/server/cat/volume_editor.xul', 'XUL_WORK_LOG' : '/xul/server/admin/work_log.xul', 'XUL_WORKSTATION_INFO' : '/xul/server/main/ws_info.xul', @@ -479,6 +484,7 @@ var urls = { 'TEST_HTML' : '/xul/server/main/test.html', 'TEST_XUL' : '/xul/server/main/test.xul', 'CONIFY' : '/conify/' + LOCALE + '/global', + 'CONIFY_MANAGE_PARTS' : '/eg/conify/global/biblio/monograph_part', 'EG_WEB_BASE' : '/eg', 'XUL_LOCAL_ADMIN_BASE' : '/xul/server/admin', 'XUL_REPORTS' : '/reports/oils_rpt.xhtml', diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu.js b/Open-ILS/xul/staff_client/chrome/content/main/menu.js index 28c83afacc..9a219a8f03 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu.js @@ -732,9 +732,13 @@ main.menu.prototype = { ['oncommand'], function() { open_eg_web_page('conify/global/config/record_attr_definition'); } ], - 'cmd_server_admin_coded_value_map' : [ + 'cmd_server_admin_acn_prefix' : [ ['oncommand'], - function() { open_eg_web_page('conify/global/config/coded_value_map'); } + function() { open_eg_web_page('conify/global/config/acn_prefix'); } + ], + 'cmd_server_admin_acn_suffix' : [ + ['oncommand'], + function() { open_eg_web_page('conify/global/config/acn_suffix'); } ], 'cmd_server_admin_billing_type' : [ ['oncommand'], @@ -1588,8 +1592,10 @@ main.menu.prototype = { }, 'volume_item_creator' : function(params) { var obj = this; + var horizontal_interface = String( obj.data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true'; + var url = obj.url_prefix( horizontal_interface ? urls.XUL_VOLUME_COPY_CREATOR_HORIZONTAL : urls.XUL_VOLUME_COPY_CREATOR ); var w = obj.new_tab( - obj.url_prefix(urls.XUL_VOLUME_COPY_CREATOR), + url, { 'tab_name' : document.getElementById('offlineStrings').getString('staff.cat.create_or_rebarcode_items') }, params ); diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul index 0e71b6efb2..40a23ff7d5 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul +++ b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul @@ -141,6 +141,8 @@ <command id="cmd_server_admin_marc_code"/> <command id="cmd_server_admin_coded_value_map"/> <command id="cmd_server_admin_billing_type"/> + <command id="cmd_server_admin_acn_prefix"/> + <command id="cmd_server_admin_acn_suffix"/> <command id="cmd_server_admin_acq_invoice_item_type"/> <command id="cmd_server_admin_acq_invoice_payment_method"/> <command id="cmd_server_admin_acq_cancel_reason"/> @@ -392,6 +394,8 @@ <menuitem label="&staff.main.menu.admin.server_admin.conify.grp_tree.label;" command="cmd_server_admin_grp_tree"/> <menuitem label="&staff.main.menu.admin.server_admin.conify.perm_list.label;" command="cmd_server_admin_perm_list"/> <menuitem label="&staff.main.menu.admin.server_admin.conify.copy_status.label;" command="cmd_server_admin_copy_status"/> + <menuitem label="&staff.main.menu.admin.server_admin.conify.acn_prefix.label;" command="cmd_server_admin_acn_prefix"/> + <menuitem label="&staff.main.menu.admin.server_admin.conify.acn_suffix.label;" command="cmd_server_admin_acn_suffix"/> <menuitem label="&staff.main.menu.admin.server_admin.conify.marc_record_attrs.label;" command="cmd_server_admin_marc_code"/> <menuitem label="&staff.main.menu.admin.server_admin.conify.coded_value_maps.label;" command="cmd_server_admin_coded_value_map"/> <menuitem label="&staff.main.menu.admin.server_admin.conify.billing_type.label;" command="cmd_server_admin_billing_type"/> diff --git a/Open-ILS/xul/staff_client/chrome/content/util/browser.js b/Open-ILS/xul/staff_client/chrome/content/util/browser.js index 41ff0d2c6a..63b00bec39 100644 --- a/Open-ILS/xul/staff_client/chrome/content/util/browser.js +++ b/Open-ILS/xul/staff_client/chrome/content/util/browser.js @@ -14,6 +14,9 @@ util.browser.prototype = { 'lock_reload' : false, // as opposed to lock 'n load :) + 'back_button_clicked' : false, + 'from_back' : false, + 'init' : function( params ) { try { @@ -115,7 +118,10 @@ util.browser.prototype = { try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var n = obj.getWebNavigation(); - if (n.canGoBack) n.goBack(); + if (n.canGoBack) { + obj.back_button_clicked = true; + n.goBack(); + } } catch(E) { var err = 'cmd_back: ' + E; obj.error.sdump('D_ERROR',err); @@ -265,6 +271,7 @@ util.browser.prototype = { cw.IAMXUL = true; cw.XUL_BUILD_ID = '/xul/server/'.split(/\//)[2]; cw.xulG = obj.passthru_content_params || {}; + cw.xulG.fromBack = obj.from_back; if (!cw.xulG.set_tab) { cw.xulG.set_tab = function(a,b,c) { return window.xulG.set_tab(a,b,c); }; } if (!cw.xulG.new_tab) { cw.xulG.new_tab = function(a,b,c) { return window.xulG.new_tab(a,b,c); }; } if (!cw.xulG.close_tab) { cw.xulG.close_tab = function(a) { return window.xulG.close_tab(a); }; } @@ -412,6 +419,7 @@ util.browser.prototype = { if (stateFlags & nsIWebProgressListener.STATE_IS_DOCUMENT) { s += ('\tSTATE_IS_DOCUMENT\n'); if( stateFlags & nsIWebProgressListener.STATE_STOP ) { + var alert_string = 'document has stopped: ' + new Date() + '\n'; dump(alert_string); obj.push_variables(); obj.updateNavButtons(); if (typeof obj.on_url_load == 'function') { try { @@ -448,6 +456,8 @@ util.browser.prototype = { } if (stateFlags & nsIWebProgressListener.STATE_START) { s += ('\tSTATE_START\n'); + obj.from_back = obj.back_button_clicked; + obj.back_button_clicked = false; } if (stateFlags & nsIWebProgressListener.STATE_REDIRECTING) { s += ('\tSTATE_REDIRECTING\n'); diff --git a/Open-ILS/xul/staff_client/chrome/content/util/list.js b/Open-ILS/xul/staff_client/chrome/content/util/list.js index dc1570a841..fb46564b2c 100644 --- a/Open-ILS/xul/staff_client/chrome/content/util/list.js +++ b/Open-ILS/xul/staff_client/chrome/content/util/list.js @@ -30,6 +30,7 @@ util.list.prototype = { 'init' : function (params) { var obj = this; + obj.scratch_data = {}; JSAN.use('util.widgets'); @@ -1016,11 +1017,11 @@ util.list.prototype = { if (typeof params.map_row_to_column == 'function') { - label = params.map_row_to_column(params.row,this.columns[i]); + label = params.map_row_to_column(params.row,this.columns[i],this.scratch_data); } else if (typeof this.map_row_to_column == 'function') { - label = this.map_row_to_column(params.row,this.columns[i]); + label = this.map_row_to_column(params.row,this.columns[i],this.scratch_data); } if (this.columns[i].type == 'checkbox') { treecell.setAttribute('value',label); } else { treecell.setAttribute('label',label ? label : ''); } @@ -1032,11 +1033,11 @@ util.list.prototype = { if (typeof params.map_row_to_columns == 'function') { - labels = params.map_row_to_columns(params.row,this.columns); + labels = params.map_row_to_columns(params.row,this.columns,this.scratch_data); } else if (typeof this.map_row_to_columns == 'function') { - labels = this.map_row_to_columns(params.row,this.columns); + labels = this.map_row_to_columns(params.row,this.columns,this.scratch_data); } for (var i = 0; i < labels.length; i++) { @@ -1070,13 +1071,13 @@ util.list.prototype = { var value = ''; if (typeof params.map_row_to_column == 'function') { - value = params.map_row_to_column(params.row,this.columns[i]); + value = params.map_row_to_column(params.row,this.columns[i],this.scratch_data); } else { if (typeof this.map_row_to_column == 'function') { - value = this.map_row_to_column(params.row,this.columns[i]); + value = this.map_row_to_column(params.row,this.columns[i],this.scratch_data); } } if (typeof value == 'string' || typeof value == 'number') { @@ -1817,9 +1818,11 @@ util.list.prototype = { }, // Default for the map_row_to_columns function for .init 'std_map_row_to_columns' : function(error_value) { - return function(row,cols) { + return function(row,cols,scratch) { // row contains { 'my' : { 'acp' : {}, 'circ' : {}, 'mvr' : {} } } // cols contains all of the objects listed above in columns + // scratch is a temporary space shared by all cells/rows (or just per row if not explicitly passed in) + if (!scratch) { scratch = {}; } var obj = {}; JSAN.use('util.error'); obj.error = new util.error(); @@ -1833,7 +1836,7 @@ util.list.prototype = { try { for (var i = 0; i < cols.length; i++) { switch (typeof cols[i].render) { - case 'function': try { values[i] = cols[i].render(my); } catch(E) { values[i] = error_value; obj.error.sdump('D_COLUMN_RENDER_ERROR',E); } break; + case 'function': try { values[i] = cols[i].render(my,scratch); } catch(E) { values[i] = error_value; obj.error.sdump('D_COLUMN_RENDER_ERROR',E); } break; case 'string' : cmd += 'try { ' + cols[i].render + '; values['+i+'] = v; } catch(E) { values['+i+'] = error_value; }'; break; default: cmd += 'values['+i+'] = "??? '+(typeof cols[i].render)+'"; '; } diff --git a/Open-ILS/xul/staff_client/chrome/content/util/widgets.js b/Open-ILS/xul/staff_client/chrome/content/util/widgets.js index 358519a09a..2bad0b009f 100644 --- a/Open-ILS/xul/staff_client/chrome/content/util/widgets.js +++ b/Open-ILS/xul/staff_client/chrome/content/util/widgets.js @@ -134,7 +134,9 @@ util.widgets.make_menulist = function( items, dvalue ) { menuitem.setAttribute('disabled','true'); } } - menulist.setAttribute('value',dvalue); + if (typeof dvalue != 'undefined') { + menulist.setAttribute('value',dvalue); + } return menulist; } @@ -209,7 +211,7 @@ util.widgets.insertAfter = function(parent_node,new_node,sibling_node) { } } -util.widgets.apply_vertical_tab_on_enter_handler = function(node,onfailure) { +util.widgets.apply_vertical_tab_on_enter_handler = function(node,onfailure,no_enter_func) { try { node.addEventListener( 'keypress', @@ -224,9 +226,14 @@ util.widgets.apply_vertical_tab_on_enter_handler = function(node,onfailure) { ev.preventDefault(); ev.stopPropagation(); return true; } else { + dump('keypress: attempting onfailure\n'); if (typeof onfailure == 'function') return onfailure(ev); return false; } + } else { + if (typeof no_enter_func == 'function') { + no_enter_func(ev); + } } }, false diff --git a/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties b/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties index 6a125de544..7de93f97a5 100644 --- a/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties +++ b/Open-ILS/xul/staff_client/chrome/locale/en-US/offline.properties @@ -256,6 +256,7 @@ staff.cat.util.copy_editor.edit=Edit staff.cat.util.copy_editor.view=View staff.circ.copy_status.add_volumes.perm_failure=You do not have permission to add volumes to the workstation library. staff.circ.copy_status.add_volumes.title=Add Volume/Item for Record # %1$s +staff.cat.manage_parts.title=Manage Parts for Record # %1$s staff.cat.z3950.marked_record_for_overlay_indicator.tcn.label=Record with TCN %1$s marked for overlay. staff.cat.z3950.marked_record_for_overlay_indicator.record_id.label=Record with ID %1$s marked for overlay. staff.cat.opac.marked_record_for_hold_transfer_indicator.tcn.label=Record with TCN %1$s marked for title hold transfer. diff --git a/Open-ILS/xul/staff_client/server/cat/bib_brief.js b/Open-ILS/xul/staff_client/server/cat/bib_brief.js index b4e283e58c..db87e5b484 100644 --- a/Open-ILS/xul/staff_client/server/cat/bib_brief.js +++ b/Open-ILS/xul/staff_client/server/cat/bib_brief.js @@ -9,13 +9,13 @@ function my_init() { JSAN.use('util.error'); g.error = new util.error(); g.error.sdump('D_TRACE','my_init() for cat_bib_brief.xul'); - JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'}); + JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'}); docid = xul_param('docid'); var key = location.pathname + location.search + location.hash; - if (!docid && typeof data.modal_xulG_stack != 'undefined' && typeof data.modal_xulG_stack[key] != 'undefined') { - var xulG = data.modal_xulG_stack[key][ data.modal_xulG_stack[key].length - 1 ]; + if (!docid && typeof g.data.modal_xulG_stack != 'undefined' && typeof g.data.modal_xulG_stack[key] != 'undefined') { + var xulG = g.data.modal_xulG_stack[key][ g.data.modal_xulG_stack[key].length - 1 ]; if (typeof xulG == 'object') { docid = xulG.docid; } @@ -28,7 +28,7 @@ function my_init() { if (docid > -1) { - data.last_record = docid; data.stash('last_record'); + g.data.last_record = docid; g.data.stash('last_record'); g.network.simple_request( 'MODS_SLIM_RECORD_RETRIEVE.authoritative', @@ -87,6 +87,14 @@ function my_init() { } } +function unhide_add_volumes_button() { + if (xulG && typeof xulG == 'object' && typeof xulG['new_tab'] == 'function') { + document.getElementById('add_volumes').hidden = false; + document.getElementById('add_volumes_left_paren').hidden = false; + document.getElementById('add_volumes_right_paren').hidden = false; + } +} + function view_marc() { try { JSAN.use('util.window'); var win = new util.window(); @@ -114,4 +122,39 @@ function spawn_patron(span) { } } +function add_volumes() { + try { + var edit = 0; + try { + edit = g.network.request( + api.PERM_MULTI_ORG_CHECK.app, + api.PERM_MULTI_ORG_CHECK.method, + [ + ses(), + ses('staff_id'), + [ ses('ws_ou') ], + [ 'CREATE_VOLUME', 'CREATE_COPY' ] + ] + ).length == 0 ? 1 : 0; + } catch(E) { + g.error.sdump('D_ERROR','batch permission check: ' + E); + } + if (edit==0) { + alert(document.getElementById('offlineStrings').getString('staff.circ.copy_status.add_volumes.perm_failure')); + return; // no read-only view for this interface + } + + var title = document.getElementById('offlineStrings').getFormattedString('staff.circ.copy_status.add_volumes.title', [docid]); + + var horizontal_interface = String( g.data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true'; + var url = window.xulG.url_prefix( horizontal_interface ? urls.XUL_VOLUME_COPY_CREATOR_HORIZONTAL : urls.XUL_VOLUME_COPY_CREATOR ); + var w = xulG.new_tab( + url, + { 'tab_name' : title }, + { 'doc_id' : docid, 'ou_ids' : [ ses('ws_ou') ] } + ); + } catch(E) { + alert('Error in server/cat/bib_brief.js, add_volumes(): ' + E); + } +} diff --git a/Open-ILS/xul/staff_client/server/cat/bib_brief.xul b/Open-ILS/xul/staff_client/server/cat/bib_brief.xul index 21c36c8cbb..bce81b73ae 100644 --- a/Open-ILS/xul/staff_client/server/cat/bib_brief.xul +++ b/Open-ILS/xul/staff_client/server/cat/bib_brief.xul @@ -22,7 +22,7 @@ vim: noet:sw=4:ts=4: <?xul-overlay href="/xul/server/cat/bib_brief_overlay.xul"?> <window id="cat_bib_brief_win" - onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" + onload="try { my_init(); font_helper(); persist_helper(); unhide_add_volumes_button(); } catch(E) { alert(E); }" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> @@ -39,7 +39,13 @@ vim: noet:sw=4:ts=4: <messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties"/> <groupbox id="groupbox" flex="1"> - <caption id="caption"><label value="&staff.cat.bib_brief.record_summary;"/>(<label value="&staff.cat.bib_brief.view_marc;" class="click_link" onclick="view_marc();"/>)</caption> + <caption id="caption"> + <label value="&staff.cat.bib_brief.record_summary;"/> + <label id="add_volumes_left_paren" value="(" hidden="true"/> + <label id="add_volumes" value="&staff.cat.bib_brief.add_volumes;" class="click_link" onclick="add_volumes();" hidden="true"/> + <label id="add_volumes_right_paren" value=")" hidden="true"/> + (<label value="&staff.cat.bib_brief.view_marc;" class="click_link" onclick="view_marc();"/>) + </caption> <grid id="bib_brief_grid" /> </groupbox> diff --git a/Open-ILS/xul/staff_client/server/cat/copy_browser.js b/Open-ILS/xul/staff_client/server/cat/copy_browser.js index e8d1644968..e962dfd19a 100644 --- a/Open-ILS/xul/staff_client/server/cat/copy_browser.js +++ b/Open-ILS/xul/staff_client/server/cat/copy_browser.js @@ -288,14 +288,16 @@ cat.copy_browser.prototype = { var title = document.getElementById('catStrings').getString('staff.cat.copy_browser.add_item.title'); + var horizontal_interface = String( obj.data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true'; + var url = window.xulG.url_prefix( horizontal_interface ? urls.XUL_VOLUME_COPY_CREATOR_HORIZONTAL : urls.XUL_VOLUME_COPY_CREATOR ); var w = xulG.new_tab( - window.xulG.url_prefix(urls.XUL_VOLUME_COPY_CREATOR), + url, { 'tab_name' : title }, { 'doc_id' : obj.docid, 'ou_ids' : list, 'copy_shortcut' : copy_shortcut, - 'refresh' : function() { obj.refresh_list(); } + 'onrefresh' : function() { obj.refresh_list(); } } ); } catch(E) { @@ -331,7 +333,7 @@ cat.copy_browser.prototype = { } } ], - 'cmd_replace_barcode' : [ + 'cmd_edit_items' : [ ['command'], function() { try { @@ -356,12 +358,12 @@ cat.copy_browser.prototype = { xulG.volume_item_creator( {'existing_copies':list, 'onrefresh' : function() { obj.refresh_list(); } } ); } catch(E) { - obj.error.standard_unexpected_error_alert(document.getElementById('catStrings').getString('staff.cat.copy_browser.replace_barcode.error'),E); + obj.error.standard_unexpected_error_alert(document.getElementById('catStrings').getString('staff.cat.copy_browser.edit_items.error'),E); obj.refresh_list(); } } ], - 'cmd_edit_items' : [ + 'old_cmd_edit_items' : [ ['command'], function() { try { @@ -536,10 +538,12 @@ cat.copy_browser.prototype = { var title = document.getElementById('catStrings').getString('staff.cat.copy_browser.add_volume.title'); + var horizontal_interface = String( obj.data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true'; + var url = window.xulG.url_prefix( horizontal_interface ? urls.XUL_VOLUME_COPY_CREATOR_HORIZONTAL : urls.XUL_VOLUME_COPY_CREATOR ); var w = xulG.new_tab( - window.xulG.url_prefix(urls.XUL_VOLUME_COPY_CREATOR), + url, { 'tab_name' : title }, - { 'doc_id' : obj.docid, 'ou_ids' : list, 'refresh' : function() { obj.refresh_list(); } } + { 'doc_id' : obj.docid, 'ou_ids' : list, 'onrefresh' : function() { obj.refresh_list(); } } ); } catch(E) { @@ -639,7 +643,6 @@ cat.copy_browser.prototype = { } if (robj.ilsevent != 0) throw(robj); } - alert(document.getElementById('catStrings').getString('staff.cat.copy_browser.delete_volume.success')); obj.refresh_list(); } } catch(E) { @@ -1448,6 +1451,7 @@ cat.copy_browser.prototype = { var data = { 'row' : { 'my' : { + 'doc_id' : obj.docid, 'aou' : obj.data.hash.aou[ acn_tree.owning_lib() ], 'acn' : acn_tree, 'acp' : acp_item, @@ -1517,6 +1521,7 @@ cat.copy_browser.prototype = { 'circ_lib' : { 'hidden' : false }, 'owning_lib' : { 'hidden' : false }, 'call_number' : { 'hidden' : false }, + 'parts' : { 'hidden' : false }, 'due_date' : { 'hidden' : false }, 'acp_status' : { 'hidden' : false }, }, @@ -1525,8 +1530,12 @@ cat.copy_browser.prototype = { 'due_date', 'owning_lib', 'circ_lib', + 'label_class', + 'prefix', 'call_number', + 'suffix', 'copy_number', + 'parts', 'location', 'barcode', 'loan_duration', @@ -1668,7 +1677,6 @@ cat.copy_browser.prototype = { obj.controller.view.cmd_add_items.setAttribute('disabled','true'); obj.controller.view.cmd_add_items_to_buckets.setAttribute('disabled','true'); obj.controller.view.cmd_edit_items.setAttribute('disabled','true'); - obj.controller.view.cmd_replace_barcode.setAttribute('disabled','true'); obj.controller.view.cmd_delete_items.setAttribute('disabled','true'); obj.controller.view.cmd_print_spine_labels.setAttribute('disabled','true'); obj.controller.view.cmd_add_volumes.setAttribute('disabled','true'); @@ -1700,7 +1708,6 @@ cat.copy_browser.prototype = { obj.controller.view.sel_mark_items_missing.setAttribute('disabled','false'); obj.controller.view.cmd_add_items_to_buckets.setAttribute('disabled','false'); obj.controller.view.cmd_edit_items.setAttribute('disabled','false'); - obj.controller.view.cmd_replace_barcode.setAttribute('disabled','false'); obj.controller.view.cmd_delete_items.setAttribute('disabled','false'); obj.controller.view.cmd_print_spine_labels.setAttribute('disabled','false'); obj.controller.view.cmd_transfer_items.setAttribute('disabled','false'); diff --git a/Open-ILS/xul/staff_client/server/cat/copy_browser.xul b/Open-ILS/xul/staff_client/server/cat/copy_browser.xul index 242804317f..319ae35480 100644 --- a/Open-ILS/xul/staff_client/server/cat/copy_browser.xul +++ b/Open-ILS/xul/staff_client/server/cat/copy_browser.xul @@ -86,7 +86,6 @@ vim:noet:sw=4:ts=4: <command id="cmd_add_items"/> <command id="cmd_add_items_to_buckets"/> <command id="cmd_edit_items"/> - <command id="cmd_replace_barcode"/> <command id="cmd_delete_items"/> <command id="cmd_transfer_items"/> <command id="cmd_print_spine_labels"/> @@ -128,7 +127,6 @@ vim:noet:sw=4:ts=4: <menuitem command="sel_mark_items_missing" label="&staff.cat.copy_browser.actions.sel_mark_items_missing.label;" accesskey="&staff.cat.copy_browser.actions.sel_mark_items_missing.accesskey;"/> <menuseparator/> <menuitem command="cmd_print_spine_labels" label="&staff.cat.copy_browser.actions.cmd_print_spine_labels.label;" accesskey="&staff.cat.copy_browser.actions.cmd_print_spine_labels.accesskey;"/> - <menuitem command="cmd_replace_barcode" label="&staff.cat.copy_browser.actions.cmd_replace_barcode.label;" accesskey=""/> <menuitem command="save_columns" label="&staff.cat.copy_browser.actions.save_columns.label;"/> <menuitem command="cmd_refresh_list" label="&staff.cat.copy_browser.actions.cmd_refresh_list.label;" accesskey="&staff.cat.copy_browser.actions.cmd_refresh_list.accesskey;"/> </popup> @@ -142,8 +140,6 @@ vim:noet:sw=4:ts=4: <label value="&staff.cat.copy_browser.holdings_maintenance.depth_filter_menu;" /> <hbox id="x_depth_menu"/> <spacer flex="1"/> - <label value="&staff.cat.copy_browser.holdings_maintenance.consortial_total;"/><label id="consortial_total"/> - <label value="&staff.cat.copy_browser.holdings_maintenance.consortial_available;"/><label id="consortial_available"/> </hbox> <hbox> <checkbox id="show_acns" label="&staff.cat.copy_browser.holdings_maintenance.show_acns;" /> @@ -155,6 +151,8 @@ vim:noet:sw=4:ts=4: <button label="Show All Libs" command="cmd_show_all_libs" accesskey=""/> --> <spacer flex="1"/> + <label value="&staff.cat.copy_browser.holdings_maintenance.consortial_total;"/><label id="consortial_total"/> + <label value="&staff.cat.copy_browser.holdings_maintenance.consortial_available;"/><label id="consortial_available"/> <menubar> <menu label="&staff.cat.copy_browser.holdings_maintenance.actions.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.actions.accesskey;"> <menupopup> @@ -182,7 +180,6 @@ vim:noet:sw=4:ts=4: <menuitem command="sel_mark_items_missing" label="&staff.cat.copy_browser.holdings_maintenance.sel_mark_items_missing.label;" accesskey="&staff.cat.copy_browser.holdings_maintenance.sel_mark_items_missing.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=""/> <menuitem command="save_columns" label="&staff.cat.copy_browser.holdings_maintenance.save_columns.label;"/> <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;"/> </menupopup> diff --git a/Open-ILS/xul/staff_client/server/cat/copy_editor.js b/Open-ILS/xul/staff_client/server/cat/copy_editor.js index 8680351e93..7daa37c3ba 100644 --- a/Open-ILS/xul/staff_client/server/cat/copy_editor.js +++ b/Open-ILS/xul/staff_client/server/cat/copy_editor.js @@ -2,8 +2,6 @@ var g = {}; g.map_acn = {}; -var xulG = {}; - function $(id) { return document.getElementById(id); } function my_init() { @@ -24,6 +22,10 @@ function my_init() { JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'}); JSAN.use('util.network'); g.network = new util.network(); + if (xulG.unified_interface) { + $('non_unified_buttons').hidden = true; + } + g.docid = xul_param('docid',{'modal_xulG':true}); g.handle_update = xul_param('handle_update',{'modal_xulG':true}); @@ -63,44 +65,50 @@ function my_init() { if (xul_param('edit',{'modal_xulG':true}) == '1') { - // Editor desired, but let's check permissions - g.edit = false; + g.edit = true; - try { - var check = g.network.simple_request( - 'PERM_MULTI_ORG_CHECK', - [ - ses(), - g.data.list.au[0].id(), - util.functional.map_list( - g.copies, - function (o) { - var lib; - var cn_id = o.call_number(); - if (cn_id == -1) { - lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat - } else { - if (! g.map_acn[ cn_id ]) { - var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]); - if (typeof req.ilsevent == 'undefined') { - g.map_acn[ cn_id ] = req; - lib = g.map_acn[ cn_id ].owning_lib(); + if (g.copies.length > 0) { // When loaded in the unified interface, there may be no copies yet (from the volum/item creator) + + // Editor desired, but let's check permissions + g.edit = false; + + try { + var check = g.network.simple_request( + 'PERM_MULTI_ORG_CHECK', + [ + ses(), + g.data.list.au[0].id(), + util.functional.map_list( + g.copies, + function (o) { + var lib; + var cn_id = o.call_number(); + if (cn_id == -1) { + lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat + } else { + if (! g.map_acn[ cn_id ]) { + var req = g.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]); + if (typeof req.ilsevent == 'undefined') { + g.map_acn[ cn_id ] = req; + lib = g.map_acn[ cn_id ].owning_lib(); + } else { + lib = o.circ_lib(); + } } else { - lib = o.circ_lib(); + lib = g.map_acn[ cn_id ].owning_lib(); } - } else { - lib = g.map_acn[ cn_id ].owning_lib(); } + return typeof lib == 'object' ? lib.id() : lib; } - return typeof lib == 'object' ? lib.id() : lib; - } - ), - g.copies.length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ] - ] - ); - g.edit = check.length == 0; - } catch(E) { - g.error.standard_unexpected_error_alert('batch permission check',E); + ), + g.copies.length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ] + ] + ); + g.edit = check.length == 0; + } catch(E) { + g.error.standard_unexpected_error_alert('batch permission check',E); + } + } if (g.edit) { @@ -110,6 +118,7 @@ function my_init() { } else { $('top_nav').setAttribute('hidden','true'); } + } else { $('top_nav').setAttribute('hidden','true'); } @@ -162,6 +171,29 @@ function my_init() { g.render(); g.check_for_unmet_required_fields(); + if (xulG.unified_interface) { + xulG.refresh_copy_editor = function() { + try { + g.copies = xulG.copies; + g.original_copies = js2JSON( g.copies ); + for (var i = 0; i < g.applied_templates.length; i++) { + g._apply_template( g.applied_templates[i] ); + } + g.summarize( g.copies ); + g.render(); + g.check_for_unmet_required_fields(); + } catch(E) { + alert('Error in copy_editor.js, xulG.refresh_copy_editor(): ' + E); + } + }; + xulG.unlock_copy_editor = function() { + oils_unlock_page(); + }; + xulG.notify_of_templatable_field_change = function(id,v) { + g.changed[ 'volume_copy_creator.'+id ] = { 'type' : 'volume_copy_creator', 'field' : id, 'value' : v }; + } + } + } catch(E) { var err_msg = $("commonStrings").getFormattedString('common.exception', ['cat/copy_editor.js', E]); try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); } @@ -191,6 +223,25 @@ g.retrieve_templates = function() { function() { g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value }; g.save_attributes(); }, false ); + + if (xulG.unified_interface) { + if (typeof xulG.update_unified_template_list == 'function') { + xulG.update_unified_template_list(list); + // functions the unified wrapper should use to let the item attribute editor do the heavy lifting for templates + xulG.update_item_editor_template_selection = function(new_value) { + g.template_menu.value = new_value; + g.copy_editor_prefs[ 'template_menu' ] = { 'value' : g.template_menu.value }; + g.save_attributes(); + } + xulG.item_editor_apply_template = function() { g.apply_template(); }; + xulG.item_editor_delete_template = function() { g.delete_template(); }; + xulG.item_editor_save_template = function() { g.save_template(); }; + xulG.item_editor_import_templates = function() { g.import_templates(); }; + xulG.item_editor_export_templates = function() { g.export_templates(); }; + xulG.item_editor_reset = function() { g.reset(); }; + } + } + } catch(E) { g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.retrieve_templates.error'), E); } @@ -199,10 +250,26 @@ g.retrieve_templates = function() { /******************************************************************************************************/ /* Apply Template */ +g.applied_templates = []; + g.apply_template = function() { try { var name = g.template_menu.value; if (g.templates[ name ] != 'undefined') { + g.applied_templates.push( name ); + g._apply_template(name); + g.summarize( g.copies ); + g.render(); + g.check_for_unmet_required_fields(); + } + } catch(E) { + g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.apply_templates.error'), E); + } +} + +g._apply_template = function(name) { + try { + if (g.templates[ name ] != 'undefined') { var template = g.templates[ name ]; for (var i in template) { g.changed[ i ] = template[ i ]; @@ -216,14 +283,16 @@ g.apply_template = function() { case 'owning_lib' : g.apply_owning_lib(template[i].value); break; + case 'volume_copy_creator' : + if (xulG.unified_interface) { + xulG.apply_template_to_batch(template[i].field,template[i].value); + } + break; } } - g.summarize( g.copies ); - g.render(); - g.check_for_unmet_required_fields(); } } catch(E) { - g.error.standard_unexpected_error_alert($('catStrings').getString('staff.cat.copy_editor.apply_templates.error'), E); + alert('Error in copy_editor.js, g._apply_template('+name+'): ' + E); } } @@ -383,12 +452,16 @@ g.import_templates = function() { /* Restore backup copies */ g.reset = function() { + g.applied_templates = []; g.changed = {}; g.copies = JSON2js( g.original_copies ); g.summarize( g.copies ); g.render(); g.check_for_unmet_required_fields(); oils_unlock_page(); + if (xulG.unified_interface) { + xulG.reset_batch_menus(); + } } /******************************************************************************************************/ @@ -459,6 +532,8 @@ g.apply_stat_cat = function(sc_id,entry_id) { g.apply_owning_lib = function(ou_id) { g.error.sdump('D_TRACE','ou_id = ' + ou_id + '\n'); + // but don't allow this when bundled with the volume/copy creator UI, or if we're editing pre-cats + if (! g.safe_to_change_owning_lib() ) { return; } for (var i = 0; i < g.copies.length; i++) { var copy = g.copies[i]; try { @@ -471,15 +546,15 @@ g.apply_owning_lib = function(ou_id) { g.map_acn[copy.call_number()] = volume; } var old_volume = g.map_acn[copy.call_number()]; - var acn_id = g.network.simple_request( + var acn_blob = g.network.simple_request( 'FM_ACN_FIND_OR_CREATE', - [ses(),old_volume.label(),old_volume.record(),ou_id] + [ses(),old_volume.label(),old_volume.record(),ou_id,old_volume.prefix(),old_volume.suffix(),old_volume.label_class()] ); - if (typeof acn_id.ilsevent != 'undefined') { - g.error.standard_unexpected_error_alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_owning_lib.call_number.error', [copy.barcode()]), acn_id); + if (typeof acn_blob.ilsevent != 'undefined') { + g.error.standard_unexpected_error_alert($('catStrings').getFormattedString('staff.cat.copy_editor.apply_owning_lib.call_number.error', [copy.barcode()]), acn_blob); continue; } - copy.call_number(acn_id); + copy.call_number(acn_blob.acn_id); copy.ischanged('1'); } catch(E) { g.error.standard_unexpected_error_alert('apply_stat_cat',E); @@ -490,10 +565,11 @@ g.apply_owning_lib = function(ou_id) { } /******************************************************************************************************/ -/* This returns true if none of the copies being edited are pre-cats */ +/* This returns false if any of the copies being edited are pre-cats, or if we're embedded in the unified volume/copy UI */ g.safe_to_change_owning_lib = function() { try { + if (xulG.unified_interface) { return false; } var safe = true; for (var i = 0; i < g.copies.length; i++) { var cn = g.copies[i].call_number(); diff --git a/Open-ILS/xul/staff_client/server/cat/copy_editor.xul b/Open-ILS/xul/staff_client/server/cat/copy_editor.xul index a456da43b1..5105ec13e5 100644 --- a/Open-ILS/xul/staff_client/server/cat/copy_editor.xul +++ b/Open-ILS/xul/staff_client/server/cat/copy_editor.xul @@ -20,7 +20,6 @@ <window id="cat_copy_editor_win" onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" - width="800" height="580" oils_persist="width height" title="&staff.cat.copy_editor.window.label;" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> @@ -42,7 +41,7 @@ <caption id="caption" label="&staff.cat.copy_editor.groupbox1.label;"/> <hbox id="top_nav"> - <hbox style="background: grey"> + <hbox id="template_bar" style="background: grey" flex="1"> <vbox><spacer flex="1"/><label value="&staff.cat.copy_editor.templates.label;" style="font-weight: bold"/><spacer flex="1"/></vbox> <hbox id="template_placeholder"/> <button id="apply_template" label="&staff.cat.copy_editor.templates.apply_template.label;" accesskey="&staff.cat.copy_editor.templates.apply_template.accesskey;" oncommand="g.apply_template()"/> @@ -50,9 +49,9 @@ <button id="import_templates" label="&staff.cat.copy_editor.templates.import_template.label;" oncommand="g.import_templates()"/> <button id="export_templates" label="&staff.cat.copy_editor.templates.export_template.label;" oncommand="g.export_templates()"/> <button id="save_template" label="&staff.cat.copy_editor.templates.save_template.label;" oncommand="g.save_template()"/> + <spacer flex="1"/> + <button label="&staff.cat.copy_editor.templates.reset.label;" accesskey="&staff.cat.copy_editor.templates.reset.accesskey;" oncommand="g.reset()"/> </hbox> - <spacer flex="1"/> - <button label="&staff.cat.copy_editor.templates.reset.label;" accesskey="&staff.cat.copy_editor.templates.reset.accesskey;" oncommand="g.reset()"/> </hbox> <hbox flex="1" style="overflow: scroll"> @@ -88,8 +87,10 @@ <hbox id="nav"> <spacer flex="1"/> <button id="copy_notes" label="&staff.cat.copy_editor.copy_notes.label;" accesskey="&staff.cat.copy_editor.copy_notes.accesskey;" oncommand="g.copy_notes();"/> - <button id="save" label="&staff.cat.copy_editor.save.label;" hidden="true" accesskey="&staff.cat.copy_editor.save.accesskey;" oncommand="g.stash_and_close();"/> - <button id="cancel" label="&staff.cat.copy_editor.cancel.label;" accesskey="&staff.cat.copy_editor.cancel.accesskey;" oncommand="JSAN.use('util.widgets'); util.widgets.dispatch('close',window);"/> + <hbox id="non_unified_buttons"> + <button id="save" label="&staff.cat.copy_editor.save.label;" hidden="true" accesskey="&staff.cat.copy_editor.save.accesskey;" oncommand="g.stash_and_close();"/> + <button id="cancel" label="&staff.cat.copy_editor.cancel.label;" accesskey="&staff.cat.copy_editor.cancel.accesskey;" oncommand="JSAN.use('util.widgets'); util.widgets.dispatch('close',window);"/> + </hbox> </hbox> <spacer/> diff --git a/Open-ILS/xul/staff_client/server/cat/spine_labels.js b/Open-ILS/xul/staff_client/server/cat/spine_labels.js index 35d5d28cde..2db3a6ed23 100644 --- a/Open-ILS/xul/staff_client/server/cat/spine_labels.js +++ b/Open-ILS/xul/staff_client/server/cat/spine_labels.js @@ -44,9 +44,16 @@ var record = g.network.simple_request('MODS_SLIM_RECORD_RETRIEVE.authoritative', [ volume.record() ]); volume.record( record ); - /* Jam the prefixes and suffixes into the volume object */ - volume.prefix = label_prefix; - volume.suffix = label_suffix; + /* The volume object has native prefix and suffixes now, so affix the ones coming from copy locations */ + var temp_prefix = label_prefix + ' ' + (typeof volume.prefix() == 'object' ? volume.prefix().label() : volume.prefix()); + var temp_suffix = (typeof volume.suffix() == 'object' ? volume.suffix().label() : volume.suffix()) + ' ' + label_suffix; + + /* And assume that leading and trailing spaces can be trimmed */ + temp_prefix = temp_prefix.replace(/\s+$/,'').replace(/^\s+/,''); + temp_suffix = temp_suffix.replace(/\s+$/,'').replace(/^\s+/,''); + + volume.prefix( temp_prefix ); + volume.suffix( temp_suffix ); g.volumes[ volume.id() ] = volume; } @@ -183,11 +190,11 @@ /* Only add the prefixes and suffixes once */ if (!override || volume.id() != override.acn) { - if (volume.prefix) { - callnum = volume.prefix + ' ' + callnum; + if (volume.prefix()) { + callnum = volume.prefix() + ' ' + callnum; } - if (volume.suffix) { - callnum += ' ' + volume.suffix; + if (volume.suffix()) { + callnum += ' ' + volume.suffix(); } } diff --git a/Open-ILS/xul/staff_client/server/cat/util.js b/Open-ILS/xul/staff_client/server/cat/util.js index 5e70b3d3e0..818917ce6d 100644 --- a/Open-ILS/xul/staff_client/server/cat/util.js +++ b/Open-ILS/xul/staff_client/server/cat/util.js @@ -522,13 +522,13 @@ cat.util.fast_item_add = function(doc_id,cn_label,cp_barcode) { JSAN.use('util.error'); error = new util.error(); JSAN.use('util.network'); var network = new util.network(); - var acn_id = network.simple_request( + var acn_blob = network.simple_request( 'FM_ACN_FIND_OR_CREATE', [ ses(), cn_label, doc_id, ses('ws_ou') ] ); - if (typeof acn_id.ilsevent != 'undefined') { - error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.volume_copy_creator.stash_and_close.problem_with_volume', [cn]), acn_id); + if (typeof acn_blob.ilsevent != 'undefined') { + error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.volume_copy_creator.stash_and_close.problem_with_volume', [cn]), acn_blob); return; } @@ -536,7 +536,7 @@ cat.util.fast_item_add = function(doc_id,cn_label,cp_barcode) { copy_obj.id( -1 ); copy_obj.isnew('1'); copy_obj.barcode( cp_barcode ); - copy_obj.call_number( acn_id ); + copy_obj.call_number( acn_blob.acn_id ); copy_obj.circ_lib( ses('ws_ou') ); /* FIXME -- use constants */ copy_obj.deposit(0); diff --git a/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.js b/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.js index e82366adbf..9bc22bd3fa 100644 --- a/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.js +++ b/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.js @@ -1,5 +1,16 @@ const g_max_copies_that_can_be_added_at_a_time_per_volume = 999; +const rel_vert_pos_volume_count = 1; +const rel_vert_pos_call_number_classification = 2; +const rel_vert_pos_call_number_prefix = 3; +const rel_vert_pos_call_number = 4; +const rel_vert_pos_call_number_suffix = 5; +const rel_vert_pos_copy_count = 6; +const rel_vert_pos_barcode = 7; +const rel_vert_pos_part = 8; +const update_timer = 1000; var g = {}; +g.use_defaults = true; +g.acn_map = {}; // store retrieved acn objects here by id function my_init() { try { @@ -15,12 +26,38 @@ function my_init() { g.error.sdump('D_TRACE','my_init() for cat/volume_copy_creator.xul'); JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'}); - JSAN.use('util.widgets'); JSAN.use('util.functional'); + JSAN.use('util.widgets'); JSAN.use('util.functional'); JSAN.use('util.fm_utils'); JSAN.use('util.network'); g.network = new util.network(); g.refresh = xul_param('onrefresh'); + if (xulG.unified_interface) { + $('non_unified_buttons').hidden = true; + xulG.reset_batch_menus = function() { + $('batch_class_menulist').value = false; + util.widgets.dispatch('command',$('batch_class_menulist')); + $('batch_prefix_menulist').value = false; + util.widgets.dispatch('command',$('batch_prefix_menulist')); + $('batch_suffix_menulist').value = false; + util.widgets.dispatch('command',$('batch_suffix_menulist')); + } + xulG.apply_template_to_batch = function(id,value) { + if (!isNaN(Number(value))) { + $(id).value = value; + util.widgets.dispatch('command',$(id)); + } + setTimeout( + function() { + // TODO: Only apply batch to columns that haven't been adjusted manually? + util.widgets.dispatch('command',$('batch_button')); + },0 + ); + } + } else { + $('Create').hidden = true; + } + /***********************************************************************************************************/ /* Am I adding just copies or copies and volumes? Or am I rebarcoding existing copies? */ @@ -43,11 +80,15 @@ function my_init() { set_attr('EditThenCreate','accesskey','staff.cat.volume_copy_creator.edit_then_rebarcode.btn.accesskey'); set_attr('CreateWithDefaults','label','staff.cat.volume_copy_creator.rebarcode.btn.label'); set_attr('CreateWithDefaults','accesskey','staff.cat.volume_copy_creator.rebarcode.btn.accesskey'); + set_attr('Create','label','staff.cat.volume_copy_creator.rebarcode.btn.label'); + set_attr('Create','accesskey','staff.cat.volume_copy_creator.rebarcode.btn.accesskey'); } else { set_attr('EditThenCreate','label','staff.cat.volume_copy_creator.edit_then_create.btn.label'); set_attr('EditThenCreate','accesskey','staff.cat.volume_copy_creator.edit_then_create.btn.accesskey'); set_attr('CreateWithDefaults','label','staff.cat.volume_copy_creator.create_with_defaults.btn.label'); set_attr('CreateWithDefaults','accesskey','staff.cat.volume_copy_creator.create_with_defaults.btn.accesskey'); + set_attr('Create','label','staff.cat.volume_copy_creator.create.btn.label'); + set_attr('Create','accesskey','staff.cat.volume_copy_creator.create.btn.accesskey'); } //g.error.sdump('D_ERROR','location.href = ' + location.href + '\n\ncopy_short cut = ' + g.copy_shortcut + '\n\nou_ids = ' + xul_param('ou_ids')); @@ -56,18 +97,19 @@ function my_init() { // Get the default callnumber classification scheme from OU settings dojo.require('fieldmapper.OrgUtils'); - var label_class = g.data.hash.aous['cat.default_classification_scheme']; //fieldmapper.aou.fetchOrgSettingDefault(ses('ws_ou'), 'cat.default_classification_scheme'); + //fieldmapper.aou.fetchOrgSettingDefault(ses('ws_ou'), 'cat.default_classification_scheme'); + g.label_class = g.data.hash.aous['cat.default_classification_scheme']; - // Assign a default value if none was returned - if (!label_class) { - label_class = 1; + // Assign a default value if none was returned + if (!g.label_class) { + g.label_class = g.data.list.acnc[0].id(); } /***********************************************************************************************************/ - /* If we're passed existing_copies, rig up a copy_shortcut object to leverage existing code for rendering the volume labels, etc. - * Also make a lookup object for existing copies keyed on org id and callnumber label, and another keyed on copy id. */ + /* If we're passed existing_copies, rig up a copy_shortcut object to leverage existing code for rendering the volume labels, etc. + * Also make a lookup object for existing copies keyed on org id and callnumber composite key, and another keyed on copy id. */ - // g.org_label_existing_copy_map = { ou_id : { callnumber_label : [ copy1, copy2, ... ] }, ... } + // g.org_label_existing_copy_map = { ou_id : { callnumber_composite_key : [ copy1, copy2, ... ] }, ... } g.org_label_existing_copy_map = {}; // g.id_copy_map = { acp_id : acp, ... } g.id_copy_map = {}; @@ -82,11 +124,17 @@ function my_init() { g.copy_shortcut[ call_number.owning_lib() ] = {}; g.org_label_existing_copy_map[ call_number.owning_lib() ] = {}; } - g.copy_shortcut[ call_number.owning_lib() ][ call_number.label() ] = call_number.id(); - if (! g.org_label_existing_copy_map[ call_number.owning_lib() ][ call_number.label() ]) { - g.org_label_existing_copy_map[ call_number.owning_lib() ][ call_number.label() ] = []; + var acnc_id = call_number.label_class() ? + ( typeof call_number.label_class() == 'object' ? call_number.label_class().id() : call_number.label_class() ) + : g.label_class; + var acnp_id = typeof call_number.prefix() == 'object' ? call_number.prefix().id() : call_number.prefix(); + var acns_id = typeof call_number.suffix() == 'object' ? call_number.suffix().id() : call_number.suffix(); + var callnumber_composite_key = acnc_id + ':' + acnp_id + ':' + call_number.label() + ':' + acns_id; + g.copy_shortcut[ call_number.owning_lib() ][ callnumber_composite_key ] = call_number.id(); + if (! g.org_label_existing_copy_map[ call_number.owning_lib() ][ callnumber_composite_key ]) { + g.org_label_existing_copy_map[ call_number.owning_lib() ][ callnumber_composite_key ] = []; } - g.org_label_existing_copy_map[ call_number.owning_lib() ][ call_number.label() ].push( copy ); + g.org_label_existing_copy_map[ call_number.owning_lib() ][ callnumber_composite_key ].push( copy ); } /***********************************************************************************************************/ @@ -104,18 +152,26 @@ function my_init() { get_contentWindow(summary).xulG = { 'docid' : g.doc_id }; /***********************************************************************************************************/ - /* For the call number drop down */ + /* Setup pcrud and fetch the monographic parts for this bib */ - if (g.existing_copies.length > 0 || !g.copy_shortcut) { - g.list_callnumbers(g.doc_id, label_class); - } + dojo.require('openils.PermaCrud'); + g.pcrud = new openils.PermaCrud({'authtoken':ses()}); + g.parts = g.pcrud.search('bmp',{'record':g.doc_id},{'order_by': { 'bmp' : 'label_sortkey' } }); + g.parts_hash = util.functional.convert_object_list_to_hash( g.parts ); + + /***********************************************************************************************************/ + /* For the batch drop downs */ + + g.list_classes(); + g.list_callnumbers(g.doc_id, g.label_class); + g.render_batch_button(); /***********************************************************************************************************/ /* render the orgs and volumes/input */ var rows = document.getElementById('rows'); - var node_id = 0; + g.ou_ids = []; for (var i = 0; i < ou_ids.length; i++) { try { var org = g.data.hash.aou[ ou_ids[i] ]; @@ -123,14 +179,34 @@ function my_init() { var row = document.createElement('row'); rows.appendChild(row); row.setAttribute('ou_id',ou_ids[i]); g.render_library_label(row,ou_ids[i]); g.render_volume_count_entry( row, ou_ids[i] ); + g.ou_ids.push( ou_ids[i] ); } } catch(E) { g.error.sdump('D_ERROR',E); } } + g.common_ancestor_ou_ids = util.fm_utils.find_common_aou_ancestors( g.ou_ids ).reverse(); + + /***********************************************************************************************************/ + /* For the remainder batch drop downs */ + + g.list_prefixes(); + g.list_suffixes(); + + /************/ g.load_prefs(); + if (g.existing_copies.length > 0) { + g.gather_copies_soon(); + } + + try { + $('main').parentNode.scrollLeft = 9999; + } catch(E) { + dump('Error in volume_copy_creator.js, my_init(), trying to auto-scroll to the far right: ' + E + '\n'); + } + } catch(E) { var err_msg = $("commonStrings").getFormattedString('common.exception', ['cat/volume_copy_creator.js', E]); try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); } @@ -147,8 +223,12 @@ g.render_library_label = function(row,ou_id) { g.render_volume_count_entry = function(row,ou_id) { var hb = document.createElement('vbox'); row.appendChild(hb); var tb = document.createElement('textbox'); hb.appendChild(tb); + if (g.use_defaults) { + tb.value = 1; // default to 1 volume per org + tb.select(); + } tb.setAttribute('ou_id',ou_id); tb.setAttribute('size','3'); tb.setAttribute('cols','3'); - tb.setAttribute('rel_vert_pos','1'); + tb.setAttribute('rel_vert_pos',rel_vert_pos_volume_count); if ( (!g.copy_shortcut) && (!g.last_focus) ) { tb.focus(); g.last_focus = tb; } var node; function render_copy_count_entry(ev) { @@ -161,15 +241,16 @@ g.render_volume_count_entry = function(row,ou_id) { return; } if (node) { row.removeChild(node); node = null; } - //ev.target.disabled = true; node = g.render_callnumber_copy_count_entry(row,ou_id,ev.target.value); } } - util.widgets.apply_vertical_tab_on_enter_handler( - tb, - function() { render_copy_count_entry({'target':tb}); setTimeout(function(){util.widgets.vertical_tab(tb);},0); } + util.widgets.apply_vertical_tab_on_enter_handler( + tb, + function() { render_copy_count_entry({'target':tb}); setTimeout(function(){util.widgets.vertical_tab(tb);},0); }, + g.delay_gather_copies_soon ); tb.addEventListener( 'change', render_copy_count_entry, false); + tb.addEventListener( 'change', g.gather_copies_soon, false); tb.addEventListener( 'focus', function(ev) { g.last_focus = ev.target; }, false ); setTimeout( function() { @@ -184,6 +265,14 @@ g.render_volume_count_entry = function(row,ou_id) { ).length; render_copy_count_entry({'target':tb}); tb.disabled = true; + } else if (tb.value) { + // since we're now supplying a default + render_copy_count_entry({'target':tb}); + setTimeout( + function() { + util.widgets.vertical_tab(tb); + }, 0 + ); } } catch(E) { alert(E); @@ -199,14 +288,35 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) { var rows = grid.lastChild; var r = document.createElement('row'); rows.appendChild( r ); var x = document.createElement('label'); r.appendChild(x); - x.setAttribute('value', $("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.call_nums')); x.setAttribute('style','font-weight: bold'); + x.setAttribute('value', $("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.classification')); + x.setAttribute('style','font-weight: bold'); x = document.createElement('label'); r.appendChild(x); - x.setAttribute('value',$("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.num_of_copies')); x.setAttribute('style','font-weight: bold'); - x.setAttribute('size','3'); x.setAttribute('cols','3'); - - function handle_change(call_number_column_textbox,number_of_copies_column_textbox,barcode_column_box) { - if (call_number_column_textbox.value == '') return; - if (isNaN( Number( number_of_copies_column_textbox.value ) )) return; + x.setAttribute('value', $("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.prefix')); + x.setAttribute('style','font-weight: bold'); + x = document.createElement('label'); r.appendChild(x); + x.setAttribute('value', $("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.call_nums')); + x.setAttribute('style','font-weight: bold'); + x = document.createElement('label'); r.appendChild(x); + x.setAttribute('value', $("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.suffix')); + x.setAttribute('style','font-weight: bold'); + x = document.createElement('label'); r.appendChild(x); + x.setAttribute('value',$("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.num_of_copies')); + x.setAttribute('style','font-weight: bold'); + x = document.createElement('label'); r.appendChild(x); + x.setAttribute('value',$("catStrings").getString('staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.barcodes_and_parts')); + x.setAttribute('style','font-weight: bold'); + + function handle_change_precipitating_barcode_rendering( + callnumber_composite_key, + number_of_copies_column_textbox, + barcode_column_box + ) { + dump('handle_change_precipitating_barcode_rendering\n'); + + if (isNaN( Number( number_of_copies_column_textbox.value ) )) { + dump('1:handle_change_precipitating_barcode_rendering early return\n'); + return; + } if ( Number( number_of_copies_column_textbox.value ) > g_max_copies_that_can_be_added_at_a_time_per_volume ) { g.error.yns_alert($("catStrings").getFormattedString('staff.cat.volume_copy_creator.render_volume_count_entry.message', [g_max_copies_that_can_be_added_at_a_time_per_volume]), $("catStrings").getString('staff.cat.volume_copy_creator.render_volume_count_entry.title'), @@ -219,59 +329,177 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) { } g.render_barcode_entry( barcode_column_box, - call_number_column_textbox.value, + callnumber_composite_key, Number(number_of_copies_column_textbox.value), ou_id ); - document.getElementById("EditThenCreate").disabled = false; - document.getElementById("CreateWithDefaults").disabled = false; + if (! xulG.unified_interface) { + document.getElementById("EditThenCreate").disabled = false; + document.getElementById("CreateWithDefaults").disabled = false; + } else { + document.getElementById("Create").disabled = false; + } } - function handle_change_call_number_column_textbox(ev) { - var _call_number_column_textbox = ev.target; + function handle_change_to_callnumber_data(ev) { + dump('handle_change_to_callnumber_data\n'); + var _call_number_column_textbox = ev.target; var _call_number_column_box = _call_number_column_textbox.parentNode; - var _number_of_copies_column_box = _call_number_column_box.nextSibling; + + var _classification_column_box = _call_number_column_box.previousSibling.previousSibling; /* two over to the left */ + var _classification_column_menulist = _classification_column_box.firstChild; + + var _prefix_column_box = _call_number_column_box.previousSibling; /* one over to the left */ + var _prefix_column_menulist = _prefix_column_box.firstChild; + + var _suffix_column_box = _call_number_column_box.nextSibling; /* one over to the right */ + var _suffix_column_menulist = _suffix_column_box.firstChild; + + var _number_of_copies_column_box = _call_number_column_box.nextSibling.nextSibling; /* two over to the right */ var _number_of_copies_column_textbox = _number_of_copies_column_box.firstChild; + var _barcode_column_box = _number_of_copies_column_box.nextSibling; - handle_change(_call_number_column_textbox,_number_of_copies_column_textbox,_barcode_column_box); + + var acn_label = _call_number_column_textbox.value; + var acnc_id = _classification_column_menulist.value; + var acnp_id = _prefix_column_menulist.value; + var acns_id = _suffix_column_menulist.value; + var callnumber_composite_key = acnc_id + ':' + acnp_id + ':' + acn_label + ':' + acns_id; + dump('\tcomposite_key = ' + callnumber_composite_key + '\n'); + + _call_number_column_textbox.setAttribute('callkey',callnumber_composite_key); + _call_number_column_textbox.setAttribute('acnc_id',acnc_id); + _call_number_column_textbox.setAttribute('acnp_id',acnp_id); + _call_number_column_textbox.setAttribute('acns_id',acns_id); + + handle_change_precipitating_barcode_rendering( + callnumber_composite_key, + _number_of_copies_column_textbox, + _barcode_column_box + ); } function handle_change_number_of_copies_column_textbox(ev) { - var _number_of_copies_column_textbox = ev.target; + dump('handle_change_number_of_copies_column_textbox\n'); + var _number_of_copies_column_textbox = ev.target; var _number_of_copies_column_box = _number_of_copies_column_textbox.parentNode; - var _call_number_column_box = _number_of_copies_column_box.previousSibling; + var _call_number_column_box = _number_of_copies_column_box.previousSibling.previousSibling; /* two over */ var _call_number_column_textbox = _call_number_column_box.firstChild; - var _barcode_column_box = _number_of_copies_column_box.nextSibling; - handle_change(_call_number_column_textbox,_number_of_copies_column_textbox,_barcode_column_box); + handle_change_to_callnumber_data({'target':_call_number_column_textbox}); // let this guy do the work } for (var i = 0; i < count; i++) { var r = document.createElement('row'); rows.appendChild(r); - var call_number_column_box = document.createElement('vbox'); r.appendChild(call_number_column_box); - var number_of_copies_column_box = document.createElement('vbox'); r.appendChild(number_of_copies_column_box); - var barcode_column_box = document.createElement('vbox'); r.appendChild(barcode_column_box); - var call_number_column_textbox = document.createElement('textbox'); call_number_column_box.appendChild(call_number_column_textbox); - call_number_column_textbox.setAttribute('rel_vert_pos','2'); - call_number_column_textbox.setAttribute('ou_id',ou_id); - util.widgets.apply_vertical_tab_on_enter_handler( - call_number_column_textbox, - function() { handle_change_call_number_column_textbox({'target':call_number_column_textbox}); setTimeout(function(){util.widgets.vertical_tab(call_number_column_textbox);},0); } - ); - var number_of_copies_column_textbox = document.createElement('textbox'); number_of_copies_column_box.appendChild(number_of_copies_column_textbox); - number_of_copies_column_textbox.setAttribute('size','3'); number_of_copies_column_textbox.setAttribute('cols','3'); - number_of_copies_column_textbox.setAttribute('rel_vert_pos','3'); - number_of_copies_column_textbox.setAttribute('ou_id',ou_id); - util.widgets.apply_vertical_tab_on_enter_handler( - number_of_copies_column_textbox, - function() { handle_change_number_of_copies_column_textbox({'target':number_of_copies_column_textbox}); setTimeout(function(){util.widgets.vertical_tab(number_of_copies_column_textbox);},0); } - ); - call_number_column_textbox.addEventListener( 'change', handle_change_call_number_column_textbox, false); - call_number_column_textbox.addEventListener( 'focus', function(ev) { g.last_focus = ev.target; }, false ); - number_of_copies_column_textbox.addEventListener( 'change', handle_change_number_of_copies_column_textbox, false); - number_of_copies_column_textbox.addEventListener( 'focus', function(ev) { g.last_focus = ev.target; }, false ); - if ( !g.last_focus ) { number_of_copies_column_textbox.focus(); g.last_focus = number_of_copies_column_textbox; } + /**** CLASSIFICATION COLUMN ****/ + var classification_column_box = document.createElement('vbox'); + r.appendChild(classification_column_box); + + /**** PREFIX COLUMN ****/ + var prefix_column_box = document.createElement('vbox'); + r.appendChild(prefix_column_box); + + /**** CALLNUMBER COLUMN ****/ + var call_number_column_box = document.createElement('vbox'); + r.appendChild(call_number_column_box); + var call_number_column_textbox = document.createElement('textbox'); + call_number_column_box.appendChild(call_number_column_textbox); + if (g.use_defaults && $('marc_cn').firstChild) { + // default to first real value from batch callnumber menu + var menupopup = $('marc_cn').firstChild.firstChild; + if (menupopup.childNodes.length > 1) { + call_number_column_textbox.value = menupopup.childNodes[1].getAttribute('label'); + call_number_column_textbox.select(); + } + } + call_number_column_textbox.setAttribute('rel_vert_pos',rel_vert_pos_call_number); + call_number_column_textbox.setAttribute('ou_id',ou_id); + util.widgets.apply_vertical_tab_on_enter_handler( + call_number_column_textbox, + function() { + handle_change_to_callnumber_data({'target':call_number_column_textbox}); + setTimeout( + function(){ + util.widgets.vertical_tab(call_number_column_textbox); + },0 + ); + }, + g.delay_gather_copies_soon + ); + call_number_column_textbox.addEventListener( 'change', handle_change_to_callnumber_data, false); + call_number_column_textbox.addEventListener( 'change', g.gather_copies_soon, false); + call_number_column_textbox.addEventListener( 'focus', function(ev) { g.last_focus = ev.target; }, false ); + + /**** CLASSIFICATION COLUMN revisited ****/ + var classification_column_menulist = g.render_class_menu(call_number_column_textbox); + classification_column_menulist.addEventListener( + 'command', + function() { + handle_change_to_callnumber_data({'target':call_number_column_textbox}); + } + ,false + ); + classification_column_box.appendChild(classification_column_menulist); + + /**** PREFIX COLUMN revisited ****/ + var prefix_column_menulist = g.render_prefix_menu(call_number_column_textbox); + prefix_column_menulist.addEventListener( + 'command', + function() { + handle_change_to_callnumber_data({'target':call_number_column_textbox}); + } + ,false + ); + + prefix_column_box.appendChild(prefix_column_menulist); + + /**** SUFFIX COLUMN ****/ + var suffix_column_box = document.createElement('vbox'); + r.appendChild(suffix_column_box); + var suffix_column_menulist = g.render_suffix_menu(call_number_column_textbox); + suffix_column_menulist.addEventListener( + 'command', + function() { + handle_change_to_callnumber_data({'target':call_number_column_textbox}); + } + ,false + ); + suffix_column_box.appendChild(suffix_column_menulist); + + /**** NUMBER OF COPIES COLUMN ****/ + var number_of_copies_column_box = document.createElement('vbox'); + r.appendChild(number_of_copies_column_box); + var number_of_copies_column_textbox = document.createElement('textbox'); + number_of_copies_column_box.appendChild(number_of_copies_column_textbox); + if (g.use_defaults) { + // default to one copy per call number + number_of_copies_column_textbox.value = 1; + number_of_copies_column_textbox.select(); + } + number_of_copies_column_textbox.setAttribute('size','3'); number_of_copies_column_textbox.setAttribute('cols','3'); + number_of_copies_column_textbox.setAttribute('rel_vert_pos',rel_vert_pos_copy_count); + number_of_copies_column_textbox.setAttribute('ou_id',ou_id); + util.widgets.apply_vertical_tab_on_enter_handler( + number_of_copies_column_textbox, + function() { + handle_change_number_of_copies_column_textbox({'target':number_of_copies_column_textbox}); + setTimeout( + function(){ + util.widgets.vertical_tab(number_of_copies_column_textbox); + },0 + ); + }, + g.delay_gather_copies_soon + ); + number_of_copies_column_textbox.addEventListener( 'change', handle_change_number_of_copies_column_textbox, false); + number_of_copies_column_textbox.addEventListener( 'change', g.gather_copies_soon, false); + number_of_copies_column_textbox.addEventListener( 'focus', function(ev) { g.last_focus = ev.target; }, false ); + if ( !g.last_focus ) { number_of_copies_column_textbox.focus(); g.last_focus = number_of_copies_column_textbox; } + + /**** BARCODE COLUMN ****/ + var barcode_column_box = document.createElement('vbox'); + r.appendChild(barcode_column_box); setTimeout( function(idx,call_number_column_textbox,number_of_copies_column_textbox){ @@ -279,23 +507,36 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) { try { JSAN.use('util.functional'); if (g.copy_shortcut) { - var label = util.functional.map_object_to_list( + var callnumber_composite_key = util.functional.map_object_to_list( g.copy_shortcut[ou_id], function(o,i) { return i; } )[idx]; if (g.org_label_existing_copy_map[ou_id]) { - var num_of_copies = g.org_label_existing_copy_map[ou_id][label].length; + var num_of_copies = g.org_label_existing_copy_map[ou_id][callnumber_composite_key].length; if (num_of_copies>0) { number_of_copies_column_textbox.value = num_of_copies; number_of_copies_column_textbox.disabled = true; } } - call_number_column_textbox.value = label; - handle_change_call_number_column_textbox({'target':call_number_column_textbox}); - if (g.existing_copies.length < 1) { - call_number_column_textbox.disabled = true; + var acn_label = callnumber_composite_key.split(/:/).slice(2,-1).join(':'); + var acnc_id = callnumber_composite_key.split(/:/)[0]; + var acnp_id = callnumber_composite_key.split(/:/)[1]; + var acns_id = callnumber_composite_key.split(/:/).slice(-1)[0]; + call_number_column_textbox.value = acn_label; + classification_column_menulist.value = acnc_id; + prefix_column_menulist.value = acnp_id; + suffix_column_menulist.value = acns_id; + handle_change_to_callnumber_data({'target':call_number_column_textbox}); + } else { + + // if we're providing defaults, keep on rendering + if (call_number_column_textbox.value) { + util.widgets.dispatch('change',call_number_column_textbox); + } + if (number_of_copies_column_textbox.value) { + util.widgets.dispatch('change',number_of_copies_column_textbox); } } } catch(E) { @@ -309,36 +550,150 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) { return grid; } -g.render_barcode_entry = function(node,callnumber,count,ou_id) { +g.render_part_menu = function(barcode_tb) { + var hbox = document.createElement('hbox'); + var menulist = document.createElement('menulist'); + menulist.setAttribute('editable','true'); + hbox.appendChild(menulist); + var button = document.createElement('button'); + button.setAttribute('label',$('catStrings').getString('staff.cat.volume_copy_creator.create_part.btn.label')); + button.hidden = true; + hbox.appendChild(button); + + var menupopup = document.createElement('menupopup'); + menulist.appendChild(menupopup); + g.render_part_menuitems(menupopup); + + button.addEventListener( + 'command', + function(ev) { + var new_part = new bmp(); + new_part.isnew(1); + new_part.label(menulist.value); + new_part.record(g.doc_id); + g.pcrud.create(new_part, { + "oncomplete": function (r, objs) { + var db_part = objs[0]; + if (!db_part) { return; } + g.parts.push( db_part ); + g.parts_hash[ db_part.id() ] = db_part; + g.render_part_menuitems(menupopup); + if (menulist.selectedItem) { + barcode_tb.setAttribute('bmp_id',menulist.selectedItem.value); + button.hidden = true; + } + g.gather_copies_soon(); + } + }); + }, + false + ); + + menulist.addEventListener( + 'change', + function(ev) { + if (! ev.target.selectedItem) { + button.hidden = false; + } + }, + false + ); + menulist.addEventListener('change',g.gather_copies_soon,false); + menulist.addEventListener( + 'command', + function(ev) { + barcode_tb.setAttribute('bmp_id',menulist.selectedItem.value); + button.hidden = true; + }, + false + ); + menulist.addEventListener('command',g.gather_copies_soon,false); + + return hbox; +} + +g.render_part_menuitems = function(menupopup) { + util.widgets.remove_children(menupopup); + var menuitem = document.createElement('menuitem'); + menuitem.setAttribute('label',''); + menuitem.setAttribute('value',''); + menupopup.appendChild(menuitem); + for (var i = 0; i < g.parts.length; i++) { + var menuitem = document.createElement('menuitem'); + menuitem.setAttribute('label',g.parts[i].label()); + menuitem.setAttribute('value',g.parts[i].id()); + menupopup.appendChild(menuitem); + } + +} + +g.render_barcode_entry = function(node,callnumber_composite_key,count,ou_id) { try { + dump('g.render_barcode_entry(node,'+callnumber_composite_key+','+count+','+ou_id+'\n'); function ready_to_create(ev) { - document.getElementById("EditThenCreate").disabled = false; - document.getElementById("CreateWithDefaults").disabled = false; + if (! xulG.unified_interface) { + document.getElementById("EditThenCreate").disabled = false; + document.getElementById("CreateWithDefaults").disabled = false; + } else { + document.getElementById("Create").disabled = false; + } } - JSAN.use('util.barcode'); + JSAN.use('util.barcode'); for (var i = 0; i < count; i++) { - var tb; var set_handlers = false; + var tb_part_box; + var tb; + var part_menu; + var set_handlers = false; if (typeof node.childNodes[i] == 'undefined') { - tb = document.createElement('textbox'); node.appendChild(tb); + tb_part_box = document.createElement('hbox'); + node.appendChild(tb_part_box); + tb = document.createElement('textbox'); + tb_part_box.appendChild(tb); + part_menu = g.render_part_menu(tb); + tb_part_box.appendChild(part_menu); set_handlers = true; } else { - tb = node.childNodes[i]; + tb_part_box = node.childNodes[i]; + tb = tb_part_box.firstChild; + part_menu = tb_part_box.lastChild; } tb.setAttribute('ou_id',ou_id); - tb.setAttribute('callnumber',callnumber); - tb.setAttribute('rel_vert_pos','4'); + tb.setAttribute('callkey',callnumber_composite_key); + tb.setAttribute('rel_vert_pos',rel_vert_pos_barcode); + part_menu.firstChild.setAttribute('rel_vert_pos',rel_vert_pos_part); if (!tb.value && g.org_label_existing_copy_map[ ou_id ]) { - tb.value = g.org_label_existing_copy_map[ ou_id ][ callnumber ][i].barcode(); - tb.setAttribute('acp_id', g.org_label_existing_copy_map[ ou_id ][ callnumber ][i].id()); + tb.value = g.org_label_existing_copy_map[ ou_id ][ callnumber_composite_key ][i].barcode(); + tb.setAttribute('acp_id', g.org_label_existing_copy_map[ ou_id ][ callnumber_composite_key ][i].id()); + var temp_parts = g.org_label_existing_copy_map[ ou_id ][ callnumber_composite_key ][i].parts(); + temp_parts = util.functional.filter_list( + temp_parts, + function(p) { + return p.record() == g.doc_id; // filter out foreign parts + } + ); + if (temp_parts.length > 0) { + tb.setAttribute('bmp_id',temp_parts[0].id()); + part_menu.firstChild.value = g.parts_hash[ temp_parts[0].id() ].label(); + } tb.select(); if (! g.first_focus) { g.first_focus = tb; } } + if (g.use_defaults && ! g.first_focus) { + g.first_focus = tb; + tb.focus(); + } if (set_handlers) { - util.widgets.apply_vertical_tab_on_enter_handler( - tb, - function() { ready_to_create({'target':tb}); setTimeout(function(){util.widgets.vertical_tab(tb);},0); } + util.widgets.apply_vertical_tab_on_enter_handler( + tb, + function() { ready_to_create({'target':tb}); setTimeout(function(){util.widgets.vertical_tab(tb);},0); }, + g.delay_gather_copies_soon + ); + util.widgets.apply_vertical_tab_on_enter_handler( + part_menu.firstChild, + function() { setTimeout(function(){util.widgets.vertical_tab(part_menu.firstChild);},0); }, + g.delay_gather_copies_soon ); tb.addEventListener('change', function(ev) { var barcode = String( ev.target.value ).replace(/\s/g,''); @@ -351,11 +706,13 @@ g.render_barcode_entry = function(node,callnumber,count,ou_id) { setTimeout( function() { ev.target.select(); ev.target.focus(); }, 0); } }, false); + tb.addEventListener('change', g.gather_copies_soon, false); tb.addEventListener( 'focus', function(ev) { g.last_focus = ev.target; }, false ); } } - - setTimeout( function() { if (g.first_focus) { g.first_focus.focus(); } }, 0 ); + + g.gather_copies_soon(); + setTimeout( function() { if (g.first_focus) { g.first_focus.focus(); } }, 0 ); } catch(E) { g.error.sdump('D_ERROR','g.render_barcode_entry: ' + E); @@ -364,7 +721,7 @@ g.render_barcode_entry = function(node,callnumber,count,ou_id) { g.generate_barcodes = function() { try { - var nodes = document.getElementsByAttribute('rel_vert_pos','4'); + var nodes = document.getElementsByAttribute('rel_vert_pos',rel_vert_pos_barcode); if (nodes.length < 1) { return; } var first_barcode = nodes[0].value; @@ -387,50 +744,167 @@ g.generate_barcodes = function() { for (var i = 0; i < barcodes.length; i++) { nodes[i+1].value = barcodes[i]; nodes[i+1].select(); + util.widgets.dispatch('change',nodes[i+1]); } + setTimeout( + function() { + g.gather_copies_soon(); + },0 + ); + } catch(E) { g.error.sdump('D_ERROR','g.generate_barcodes: ' + E); } } -g.new_node_id = -1; - -g.stash_and_close = function(param) { +g.delay_gather_copies_soon = function() { + if (xulG.unified_interface) { + dump('g.delay_gather_copies_soon()\n'); + g.gather_copies_soon(); + } +} +g.gather_copies_soon = function() { try { + if (!xulG.unified_interface) { return; } + dump('g.gather_copies_soon()\n'); + document.getElementById("Create").disabled = true; + if (g.update_copy_editor_timeoutID) { + clearTimeout(g.update_copy_editor_timeoutID); + } + // This function is expensive when it comes to keeping the UI responsive, so let's give it a delay + // that quick entry of consecutive fields can override + g.update_copy_editor_timeoutID = setTimeout( + function() { + try { + g.gather_copies(); + xulG.refresh_copy_editor(); + document.getElementById("Create").disabled = false; + } catch(E) { + alert('Error in volume_copy_editor.js with g.gather_copies_soon setTimeout func(): ' + E); + } + }, update_timer + ); + } catch(E) { + alert('Error in volume_copy_creator.js, g.gather_copies_soon(): ' + E); + } +} +g.new_acp_id = -1; +g.new_acn_id = -1; + +g.gather_copies = function() { + dump('g.gather_copies()\n'); + try { var nl = document.getElementsByTagName('textbox'); - var volumes_hash = {}; + g.volumes_scaffold = {}; + /* + g.volumes_scaffold = { + '#ou_id' : { + '#class_id:#prefix_id:#callnumber label:#suffix_id' : { + 'callnumber_data' : { + 'acn_id' : '#callnumber id', + 'acn_label' : '#callnumber label', + 'acnc_id' : '#classification_id', + 'acnp_id' : '#prefix_id', + 'acns_id' : '#suffix_id' + }, + 'barcode_data' : + [ + { + 'barcode' : '#barcode', + 'acp_id' : '#copy_id', + 'bmp_id' : '#part_id' + }, ... + ] + } + }, ... + } + */ var barcodes = []; - + var v_count = 0; for (var i = 0; i < nl.length; i++) { - if ( nl[i].getAttribute('rel_vert_pos') == 4 ) barcodes.push( nl[i] ); - if ( nl[i].getAttribute('rel_vert_pos') == 2 ) { + if ( nl[i].getAttribute('rel_vert_pos') == rel_vert_pos_barcode ) barcodes.push( nl[i] ); + if ( nl[i].getAttribute('rel_vert_pos') == rel_vert_pos_call_number ) { + v_count++; var ou_id = nl[i].getAttribute('ou_id'); + var acn_id = nl[i].getAttribute('acn_id'); + if (!acn_id) { + acn_id = g.new_acn_id--; + nl[i].setAttribute('acn_id',acn_id); + } + var acnc_id = nl[i].getAttribute('acnc_id') || g.label_class; + var acnp_id = nl[i].getAttribute('acnp_id') || -1; + var acns_id = nl[i].getAttribute('acns_id') || -1; var callnumber = nl[i].value; - if (typeof volumes_hash[ou_id] == 'undefined') { volumes_hash[ou_id] = {} } - if (typeof volumes_hash[ou_id][callnumber] == 'undefined') { volumes_hash[ou_id][callnumber] = [] } + if (typeof g.volumes_scaffold[ou_id] == 'undefined') { + g.volumes_scaffold[ou_id] = {} + } + var composite_key = acnc_id + ':' + acnp_id + ':' + callnumber + ':' + acns_id; + if (typeof g.volumes_scaffold[ou_id][composite_key] == 'undefined') { + g.volumes_scaffold[ou_id][composite_key] = { + //'node' : nl[i], + 'callnumber_data' : { + 'acn_id' : acn_id, + 'acn_label' : callnumber, + 'acnc_id' : acnc_id, + 'acnp_id' : acnp_id, + 'acns_id' : acns_id + }, + 'barcode_data' : [] + } + dump('fleshing volumes scaffold with ou_id = ' + ou_id + ' composite_key = ' + composite_key + ' acn_id = ' + acn_id + '\n'); + } } }; - + dump('volume_copy_creator: processed ' + nl.length + ' textbox nodes, consisting of ' + barcodes.length + ' barcodes and ' + v_count + 'volumes\n'); + dump('volume scaffold = ' + js2JSON(g.volumes_scaffold) + '\n'); + for (var i = 0; i < barcodes.length; i++) { - var acp_id = barcodes[i].getAttribute('acp_id') || g.new_node_id--; + var acp_id = barcodes[i].getAttribute('acp_id') || g.new_acp_id--; var ou_id = barcodes[i].getAttribute('ou_id'); - var callnumber = barcodes[i].getAttribute('callnumber'); + var callnumber_composite_key = barcodes[i].getAttribute('callkey'); var barcode = barcodes[i].value; + var bmp_id = barcodes[i].getAttribute('bmp_id'); - if (typeof volumes_hash[ou_id] == 'undefined') { volumes_hash[ou_id] = {} } - if (typeof volumes_hash[ou_id][callnumber] == 'undefined') { volumes_hash[ou_id][callnumber] = [] } + dump('placing ' + barcode + ' for ou = ' + ou_id + ' into composite_key bin ' + callnumber_composite_key + '\n'); - if (barcode != '') volumes_hash[ou_id][callnumber].push( { 'barcode' : barcode, 'acp_id' : acp_id } ); + if (typeof g.volumes_scaffold[ou_id] == 'undefined') { + dump('1: I want to remove this soon, so alert me if it is getting used, ou_id = ' + ou_id + '\n'); + g.volumes_scaffold[ou_id] = {} + } + if (typeof g.volumes_scaffold[ou_id][callnumber_composite_key] == 'undefined') { + dump('2: when does this happen, and why? ou_id = ' + ou_id + ' callnumber_composite_key = ' + callnumber_composite_key + '\n'); + // one way this can happen, race condition between this function and editing a widget + g.volumes_scaffold[ou_id][callnumber_composite_key] = { + 'callnumber_data' : { + // not ideal, but hey... + 'acn_label' : callnumber_composite_key.split(/:/).slice(2,-1).join(':'), + 'acnc_id' : callnumber_composite_key.split(/:/)[0], + 'acnp_id' : callnumber_composite_key.split(/:/)[1], + 'acns_id' : callnumber_composite_key.split(/:/).slice(-1)[0] + }, + 'barcode_data' : [] + } + } + + if (barcode != '') { + g.volumes_scaffold[ou_id][callnumber_composite_key].barcode_data.push( + { + 'barcode' : barcode, + 'acp_id' : acp_id, + 'bmp_id' : bmp_id + } + ); + } } var volumes = []; var copies = []; - var volume_labels = {}; + var volume_data = {}; function new_copy(acp_id,ou_id,acn_id,barcode) { var copy = new acp(); @@ -454,43 +928,163 @@ g.stash_and_close = function(param) { return copy; } - for (var ou_id in volumes_hash) { - for (var cn_label in volumes_hash[ou_id]) { + for (var ou_id in g.volumes_scaffold) { + for (var composite_key in g.volumes_scaffold[ou_id]) { + for (var i = 0; i < g.volumes_scaffold[ou_id][composite_key].barcode_data.length; i++) { + var barcode = g.volumes_scaffold[ou_id][composite_key].barcode_data[i].barcode; + var acp_id = g.volumes_scaffold[ou_id][composite_key].barcode_data[i].acp_id; + var bmp_id = g.volumes_scaffold[ou_id][composite_key].barcode_data[i].bmp_id; + var acn_id = g.volumes_scaffold[ou_id][composite_key].callnumber_data.acn_id; + dump('gather_copies(): barcode = ' + barcode + ' acp_id = ' + acp_id + ' bmp_id = ' + bmp_id + ' acn_id = ' + acn_id + ' composite_key = ' + composite_key + '\n'); + var copy = g.id_copy_map[ acp_id ]; + if (!copy) { + copy = new_copy(acp_id,ou_id,acn_id,barcode); + g.id_copy_map[ acp_id ] = copy; + } else { + copy.ischanged( get_db_true() ); + } + copy.barcode( barcode ); + copy.call_number( acn_id ); + var temp_parts = util.functional.filter_list( + copy.parts() || [], + function(p) { + return (p.record() != g.doc_id); // filter out parts for this bib + } + ); + if (bmp_id) { + temp_parts.push( g.parts_hash[ bmp_id ] ); + } + copy.parts( temp_parts ); + copies.push( copy ); + } + } + } + + xulG.copies = copies; + return copies; - var acn_id = g.network.simple_request( - 'FM_ACN_FIND_OR_CREATE', - [ ses(), cn_label, g.doc_id, ou_id ] - ); + } catch(E) { + alert('Error in volume_copy_creator.js, g.gather_copies():' + E); + } +} - if (typeof acn_id.ilsevent != 'undefined') { - g.error.standard_unexpected_error_alert($("catStrings").getFormattedString('staff.cat.volume_copy_creator.stash_and_close.problem_with_volume', [cn]), acn_id); - continue; - } +g.vivicate_update_volumes = function() { + try { + var volumes = []; + for (var ou_id in g.volumes_scaffold) { + for (var composite_key in g.volumes_scaffold[ou_id]) { - volume_labels[ acn_id ] = { 'label' : cn_label, 'owning_lib' : ou_id }; + var callnumber_data = g.volumes_scaffold[ou_id][composite_key].callnumber_data; + var acn_id = callnumber_data.acn_id; + var acnp_id = callnumber_data.acnp_id; + var acns_id = callnumber_data.acns_id; + var acnc_id = callnumber_data.acnc_id; - for (var i = 0; i < volumes_hash[ou_id][cn_label].length; i++) { - var barcode = volumes_hash[ou_id][cn_label][i].barcode; - var acp_id = volumes_hash[ou_id][cn_label][i].acp_id; - var copy; - if (acp_id < 0) { - copy = new_copy(acp_id,ou_id,acn_id,barcode); - } else { - copy = g.id_copy_map[ acp_id ]; - copy.barcode( barcode ); - copy.call_number( acn_id ); - copy.ischanged('1'); + if (acn_id < 0) { + + var acn_blob = g.network.simple_request( + 'FM_ACN_FIND_OR_CREATE', + [ ses(), callnumber_data.acn_label, g.doc_id, ou_id, acnp_id, acns_id, acnc_id ] + ); + dump('FM_ACN_FIND_OR_CREATE: label = ' + callnumber_data.acn_label + + ' doc = ' + g.doc_id + ' ou = ' + ou_id + ' acnp = ' + acnp_id + ' acns = ' + acns_id + ' acnc = ' + acnc_id + '\n'); + + if (typeof acn_blob.ilsevent != 'undefined') { + alert('Error in g.vivicate_update_volumes, acn_id = ' + acn_id + ' acn_blob = ' + js2JSON(acn_blob)); + continue; } - copies.push( copy ); + + acn_id = acn_blob.acn_id; + + if (typeof g.acn_map[ acn_id ] == 'undefined') { + var temp_acn = g.network.simple_request( + 'FM_ACN_RETRIEVE.authoritative', + [ acn_id ] + ); + if (typeof temp_acn.ilsevent != 'undefined') { + alert('Error in g.vivicate_update_volumes, acn_id = ' + acn_id + ' temp_acn = ' + js2JSON(temp_acn)); + continue; + } + g.acn_map[ acn_id ] = temp_acn; + if (callnumber_data.acn_id < 0) { + g.acn_map[ callnumber_data.acn_id ] = temp_acn; + } + } + + } +/* + var my_acn = g.acn_map[ acn_id ]; + + var node = g.volumes_scaffold[ou_id][composite_key].node; + var class_menulist = node.parentNode.previousSibling.previousSibling.firstChild; + var prefix_menulist = node.parentNode.previousSibling.firstChild; + var suffix_menulist = node.parentNode.nextSibling.firstChild; + + if ( String(class_menulist.value) != String(my_acn.label_class()) { + my_acn.label_class( class_menulist.value ); + my_acn.ischanged( get_db_true() ); + } + if ( String(prefix_menulist.value) != String(my_acn.prefix()) { + my_acn.prefix( prefix_menulist.value ); + my_acn.ischanged( get_db_true() ); + } + if ( String(suffix_menulist.value) != String(my_acn.suffix()) { + my_acn.suffix( suffix_menulist.value ); + my_acn.ischanged( get_db_true() ); + } + + if (get_bool( my_acn.ischanged() )) { + volumes.push( my_acn ); } +*/ } } + if (volumes.length > 0) { + if (typeof xul_param('update_volume') == 'function') { + xul_param('update_volume')(volumes); + } else { + var r = g.network.simple_request( + 'FM_ACN_TREE_UPDATE', + [ ses(),volumes, false, { 'auto_merge_vols' : false } ] + ); + if (typeof r.ilsevent != 'undefined') { + alert('error with volume update: ' + js2JSON(r)); + } + } + } + } catch(E) { + alert('Error in volume_copy_creator.js, vivicate_volumes(): ' + E); + } +} + +g.stash_and_close = function(param) { + + try { + + var copies; + if (xulG.unified_interface) { + copies = xulG.copies; + } else { + copies = g.gather_copies(); + copies = blob.copies; + } var dont_close = false; - JSAN.use('util.window'); var win = new util.window(); + + g.vivicate_update_volumes(); + for (var i = 0; i < copies.length; i++) { + var acn_id = copies[i].call_number(); + if (typeof g.acn_map[acn_id] != 'undefined') { + // handle vivicated-callnumbers + copies[i].call_number( g.acn_map[acn_id].id() ); + } else { + alert('error in stash and close, acn_id = ' + acn_id); + } + } + if (copies.length > 0) { - JSAN.use('cat.util'); if (param == 'edit') { + JSAN.use('cat.util'); copies = cat.util.spawn_copy_editor( { 'edit' : true, 'docid' : g.doc_id, 'copies' : copies, 'caller_handles_update' : true }); } if (typeof xul_param('update_copy') == 'function') { @@ -501,7 +1095,7 @@ g.stash_and_close = function(param) { [ ses(),copies, true ] ); if (typeof r.ilsevent != 'undefined') { - g.error.standard_unexpected_error_alert('copy update',r); + alert('error with copy update:' + js2JSON(r)); } } try { @@ -513,22 +1107,26 @@ g.stash_and_close = function(param) { urls.XUL_SPINE_LABEL, { 'tab_name' : $("catStrings").getString('staff.cat.util.spine_editor.tab_name') }, { - 'barcodes' : util.functional.map_list( copies, function(o){return o.barcode();}) + 'barcodes' : util.functional.map_list( copies, function(o){return o.barcode();}) } ); } } catch(E) { - g.error.standard_unexpected_error_alert($(catStrings).getString('staff.cat.volume_copy_creator.stash_and_close.tree_err2'),E); + alert('2: Error in volume_copy_creator.js with g.stash_and_close(): ' + E); } } try { if (typeof window.refresh == 'function') { window.refresh(); } } catch(E) { dump(E+'\n'); } try { if (typeof g.refresh == 'function') { g.refresh(); } } catch(E) { dump(E+'\n'); } + if (typeof xulG.unlock_copy_editor == 'function') { + xulG.unlock_copy_editor(); + } + if (! dont_close) { xulG.close_tab(); } } catch(E) { - g.error.standard_unexpected_error_alert($(catStrings).getString('staff.cat.volume_copy_creator.stash_and_close.tree_err3'),E); + alert('3: Error in volume_copy_creator.js with g.stash_and_close(): ' + E); } } @@ -559,8 +1157,7 @@ g.load_prefs = function() { } } catch(E) { - g.error.standard_unexpected_error_alert($(catStrings).getString('staff.cat.volume_copy_creator.load_prefs.err_retrieving_prefs'),E); - + alert('Error in volume_copy_creator.js with g.load_prefs(): ' + E); } } @@ -576,10 +1173,143 @@ g.save_prefs = function () { ); file.close(); } catch(E) { - g.error.standard_unexpected_error_alert($(catStrings).getString('staff.cat.volume_copy_creator.save_prefs.err_storing_prefs'),E); + alert('Error in volume_copy_creator.js with g.save_prefs(): ' + E); + } +} + +g.render_class_menu = function(call_number_tb) { + var ml = util.widgets.make_menulist( + util.functional.map_list( + g.data.list.acnc, + function(o) { + return [ o.name(), o.id() ]; + } + ) + ); + ml.setAttribute('rel_vert_pos',rel_vert_pos_call_number_classification); + ml.addEventListener( + 'command', + function() { + call_number_tb.setAttribute('acnc_id',ml.value); + }, + false + ); + return ml; +} + +g.render_prefix_menu = function(call_number_tb) { + var ou_id = call_number_tb.getAttribute('ou_id'); + var org = g.data.hash.aou[ ou_id ]; + var menulist = document.createElement('menulist'); + var menupopup = document.createElement('menupopup'); + menulist.appendChild(menupopup); + var org_list = []; // order from top of consortium to owning lib + while(org) { + org_list.unshift(org.id()); + org = org.parent_ou(); + if (org && typeof org != 'object') { + org = g.data.hash.aou[ org ]; + } + } + for (var i = 0; i < org_list.length; i++) { + g.render_prefix_menu_items(menupopup,org_list[i]); + } + + menulist.setAttribute('rel_vert_pos',rel_vert_pos_call_number_prefix); + menulist.addEventListener( + 'command', + function() { + call_number_tb.setAttribute('acnp_id',menulist.value); + }, + false + ); + return menulist; +} + +g.render_prefix_menu_items = function(menupopup,ou_id) { + if (typeof g.data.list['acnp_for_lib_'+ou_id] == 'undefined') { + g.data.list['acnp_for_lib_'+ou_id] = g.network.simple_request( + 'FM_ACNP_RETRIEVE_VIA_PCRUD', + [ ses(), {"owning_lib":{"=":ou_id}}, {"order_by":{"acnp":"label_sortkey"}} ] + ); + g.data.stash('list'); + } + for (var i = 0; i < g.data.list['acnp_for_lib_'+ou_id].length; i++) { + var my_acnp = g.data.list['acnp_for_lib_'+ou_id][i]; + var menuitem = document.createElement('menuitem'); + menupopup.appendChild(menuitem); + menuitem.setAttribute( + 'label', + my_acnp.id() == -1 ? '' : + $('catStrings').getFormattedString( + 'staff.cat.volume_copy_creator.call_number_prefix.menuitem_label', + [ + my_acnp.label(), + g.data.hash.aou[ ou_id ].shortname() + ] + ) + ); + menuitem.setAttribute('value',my_acnp.id()); } } +g.render_suffix_menu = function(call_number_tb) { + var ou_id = call_number_tb.getAttribute('ou_id'); + var org = g.data.hash.aou[ ou_id ]; + var menulist = document.createElement('menulist'); + var menupopup = document.createElement('menupopup'); + menulist.appendChild(menupopup); + var org_list = []; // order from top of consortium to owning lib + while(org) { + org_list.unshift(org.id()); + org = org.parent_ou(); + if (org && typeof org != 'object') { + org = g.data.hash.aou[ org ]; + } + } + for (var i = 0; i < org_list.length; i++) { + g.render_suffix_menu_items(menupopup,org_list[i]); + } + + menulist.setAttribute('rel_vert_pos',rel_vert_pos_call_number_suffix); + menulist.addEventListener( + 'command', + function() { + call_number_tb.setAttribute('acns_id',menulist.value); + }, + false + ); + return menulist; +} + +g.render_suffix_menu_items = function(menupopup,ou_id) { + if (typeof g.data.list['acns_for_lib_'+ou_id] == 'undefined') { + g.data.list['acns_for_lib_'+ou_id] = g.network.simple_request( + 'FM_ACNS_RETRIEVE_VIA_PCRUD', + [ ses(), {"owning_lib":{"=":ou_id}}, {"order_by":{"acns":"label_sortkey"}} ] + ); + g.data.stash('list'); + } + for (var i = 0; i < g.data.list['acns_for_lib_'+ou_id].length; i++) { + var my_acns = g.data.list['acns_for_lib_'+ou_id][i]; + var menuitem = document.createElement('menuitem'); + menupopup.appendChild(menuitem); + menuitem.setAttribute( + 'label', + my_acns.id() == -1 ? '' : + $('catStrings').getFormattedString( + 'staff.cat.volume_copy_creator.call_number_suffix.menuitem_label', + [ + my_acns.label(), + g.data.hash.aou[ ou_id ].shortname() + ] + ) + ); + menuitem.setAttribute('value',my_acns.id()); + } +} + + g.list_callnumbers = function(doc_id, label_class) { var cn_blob; try { @@ -589,18 +1319,113 @@ g.list_callnumbers = function(doc_id, label_class) { } var hbox = document.getElementById('marc_cn'); var ml = util.widgets.make_menulist( - util.functional.map_list( - cn_blob, - function(o) { - for (var i in o) { - return [ o[i], i ]; + [ + [ '', '' ] + ].concat( + util.functional.map_list( + cn_blob, + function(o) { + for (var i in o) { + return [ o[i], i ]; + } } - } + ) ) ); hbox.appendChild(ml); ml.setAttribute('editable','true'); ml.setAttribute('width', '200'); + ml.setAttribute('id', 'marc_cn_menulist'); +} + +g.list_classes = function() { + var hbox = $('batch_class'); + var ml = util.widgets.make_menulist( + [ + [ '<No Change>', false ] + ].concat( + util.functional.map_list( + g.data.list.acnc, + function(o) { + return [ o.name(), o.id() ]; + } + ) + ) + ); hbox.appendChild(ml); + ml.setAttribute('id','batch_class_menulist'); + ml.addEventListener( + 'command', + function() { + if (!isNaN(Number(ml.value))) { + addCSSClass(hbox,'copy_editor_field_changed'); + if (xulG.unified_interface) { + xulG.notify_of_templatable_field_change('batch_class_menulist',ml.value); + } + } else { + removeCSSClass(hbox,'copy_editor_field_changed'); + } + }, + false + ); +} + +g.list_prefixes = function() { + var hbox = $('batch_prefix'); + var ml = util.widgets.make_menulist( + [ + [ '<No Change>', false ] + ] + ); hbox.appendChild(ml); + for (var i = 0; i < g.common_ancestor_ou_ids.length; i++) { + g.render_prefix_menu_items(ml.firstChild,g.common_ancestor_ou_ids[i]); + } + ml.setAttribute('id','batch_prefix_menulist'); + ml.addEventListener( + 'command', + function() { + if (!isNaN(Number(ml.value))) { + addCSSClass(hbox,'copy_editor_field_changed'); + if (xulG.unified_interface) { + xulG.notify_of_templatable_field_change('batch_prefix_menulist',ml.value); + } + } else { + removeCSSClass(hbox,'copy_editor_field_changed'); + } + }, + false + ); +} + +g.list_suffixes = function() { + var hbox = $('batch_suffix'); + var ml = util.widgets.make_menulist( + [ + [ '<No Change>', false ] + ] + ); hbox.appendChild(ml); + for (var i = 0; i < g.common_ancestor_ou_ids.length; i++) { + g.render_suffix_menu_items(ml.firstChild,g.common_ancestor_ou_ids[i]); + } + ml.setAttribute('id','batch_suffix_menulist'); + ml.addEventListener( + 'command', + function() { + if (!isNaN(Number(ml.value))) { + addCSSClass(hbox,'copy_editor_field_changed'); + if (xulG.unified_interface) { + xulG.notify_of_templatable_field_change('batch_suffix_menulist',ml.value); + } + } else { + removeCSSClass(hbox,'copy_editor_field_changed'); + } + }, + false + ); +} + +g.render_batch_button = function() { + var hbox = $('batch_button_box'); var btn = document.createElement('button'); + btn.setAttribute('id','batch_button'); btn.setAttribute('label',$('catStrings').getString('staff.cat.volume_copy_creator.my_init.btn.label')); btn.setAttribute('accesskey',$('catStrings').getString('staff.cat.volume_copy_creator.my_init.btn.accesskey')); btn.setAttribute('image','/xul/server/skin/media/images/down_arrow.gif'); @@ -610,15 +1435,49 @@ g.list_callnumbers = function(doc_id, label_class) { function() { var nl = document.getElementsByTagName('textbox'); for (var i = 0; i < nl.length; i++) { - if (nl[i].getAttribute('rel_vert_pos')==2 - && !nl[i].disabled) - { - nl[i].value = ml.value; - util.widgets.dispatch('change',nl[i]); + /* label */ + if (nl[i].getAttribute('rel_vert_pos')==rel_vert_pos_call_number && !nl[i].disabled) { + var label = $('marc_cn').firstChild.value; + if (label != '') { + nl[i].value = label; + util.widgets.dispatch('change',nl[i]); + } + } + } + nl = document.getElementsByTagName('menulist'); + for (var i = 0; i < nl.length; i++) { + /* classification */ + if (nl[i].getAttribute('rel_vert_pos')==rel_vert_pos_call_number_classification && !nl[i].disabled) { + var value = $('batch_class_menulist').value; + if (!isNaN( Number(value) )) { + nl[i].value = value; + util.widgets.dispatch('command',nl[i]); + } + } + /* prefix */ + if (nl[i].getAttribute('rel_vert_pos')==rel_vert_pos_call_number_prefix && !nl[i].disabled) { + var value = $('batch_prefix_menulist').value; + if (!isNaN( Number(value) )) { + nl[i].value = value; + util.widgets.dispatch('command',nl[i]); + } + } + /* suffix */ + if (nl[i].getAttribute('rel_vert_pos')==rel_vert_pos_call_number_suffix && !nl[i].disabled) { + var value = $('batch_suffix_menulist').value; + if (!isNaN( Number(value) )) { + nl[i].value = value; + util.widgets.dispatch('command',nl[i]); + } } } + setTimeout( + function() { + g.gather_copies_soon(); + },0 + ); if (g.last_focus) setTimeout( function() { g.last_focus.focus(); }, 0 ); - }, + }, false ); } diff --git a/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul b/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul index 0c5058752b..9e88c1ae68 100644 --- a/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul +++ b/Open-ILS/xul/staff_client/server/cat/volume_copy_creator.xul @@ -6,6 +6,7 @@ <!-- STYLESHEETS --> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?> +<?xml-stylesheet href="/xul/server/skin/cat.css" type="text/css"?> <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> <!-- LOCALIZATION --> @@ -19,7 +20,6 @@ <window id="cat_volume_copy_creator_win" onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" - width="800" height="580" oils_persist="height width sizemode" title="&staff.cat.volume_copy_creator.title;" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> @@ -36,31 +36,69 @@ <messagecatalog id="catStrings" src="/xul/server/locale/<!--#echo var='locale'-->/cat.properties" /> <messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties" /> - <vbox id="summary_box"/> - <hbox> - <hbox id="marc_cn"/> - <spacer flex="1" /> - <button id="generate_barcodes" label="&staff.cat.volume_copy_creator.generate_barcodes.label;" oncommand="g.generate_barcodes();" accesskey="&staff.cat.volume_copy_creator.generate_barcodes.accesskey;"/> - <checkbox id="check_barcodes" label="&staff.cat.volume_copy_creator.check_barcodes.label;" oncommand="g.save_prefs();" accesskey="&staff.cat.volume_copy_creator.check_barcodes.accesskey;"/> - <checkbox id="print_labels" label="&staff.cat.volume_copy_creator.print_labels.label;" oncommand="g.save_prefs();" accesskey="&staff.cat.volume_copy_creator.print_labels.accesskey;"/> - </hbox> - <groupbox flex="1" class="my_overflow"> - <caption id="caption" label="&staff.cat.volume_copy_creator.label;"/> - <grid flex="1"> - <columns> <column flex="0"/> <column flex="0"/> <column flex="1"/> </columns> - <rows id="rows"> - <row> - <label value="&staff.cat.volume_copy_creator.library_label.value;" style="font-weight: bold"/> - <label value="&staff.cat.volume_copy_creator.num_of_volumes_label.value;" style="font-weight: bold"/> - </row> - </rows> - </grid> - </groupbox> - <hbox style="border-bottom: solid black thin"> - <spacer flex="1" /> - <button id="CreateWithDefaults" disabled="true" oncommand="g.stash_and_close('noedit');"/> - <button id="EditThenCreate" disabled="true" oncommand="g.stash_and_close('edit');"/> - </hbox> +<vbox flex="1" class="my_overflow"> + <vbox id="summary_box" oils_persist="height"/> + <splitter + collapse="before" + resize_before="flex" + resize_after="flex" + oils_persist="state hidden" + oils_persist_peers="summary_box main"> + <grippy/> + </splitter> + <vbox id="main" oils_persist="height" flex="1"> + <hbox flex="0"> + <hbox id="batch_bar"> + <label value="&staff.cat.volume_copy_creator.batch_bar;"/> + <label value="&staff.cat.volume_copy_creator.batch_bar.call_number.classification;"/> + <hbox id="batch_class"/> + <label value="&staff.cat.volume_copy_creator.batch_bar.call_number.prefix;"/> + <hbox id="batch_prefix"/> + <label value="&staff.cat.volume_copy_creator.batch_bar.call_number.label.label;" + accesskey="&staff.cat.volume_copy_creator.batch_bar.call_number.label.accesskey;" control="marc_cn_menulist"/> + <hbox id="marc_cn"/> + <label value="&staff.cat.volume_copy_creator.batch_bar.call_number.suffix;"/> + <hbox id="batch_suffix"/> + <hbox id="batch_button_box"/> + </hbox> + <spacer flex="1" /> + </hbox> + <groupbox flex="1" class="my_overflow"> + <caption id="caption" label="&staff.cat.volume_copy_creator.label;"/> + <grid flex="1"> + <columns> <column flex="0"/> <column flex="0"/> <column flex="1"/> </columns> + <rows id="rows"> + <row> + <label value="&staff.cat.volume_copy_creator.library_label.value;" style="font-weight: bold"/> + <label value="&staff.cat.volume_copy_creator.num_of_volumes_label.value;" style="font-weight: bold"/> + </row> + </rows> + </grid> + </groupbox> + <hbox style="border-bottom: solid black thin" flex="0"> + <hbox id="misc_control_bar"> + <button id="generate_barcodes" + label="&staff.cat.volume_copy_creator.generate_barcodes.label;" + oncommand="g.generate_barcodes();" + accesskey="&staff.cat.volume_copy_creator.generate_barcodes.accesskey;"/> + <checkbox id="check_barcodes" + label="&staff.cat.volume_copy_creator.check_barcodes.label;" + oncommand="g.save_prefs();" + accesskey="&staff.cat.volume_copy_creator.check_barcodes.accesskey;"/> + <checkbox id="print_labels" + label="&staff.cat.volume_copy_creator.print_labels.label;" + oncommand="g.save_prefs();" + accesskey="&staff.cat.volume_copy_creator.print_labels.accesskey;"/> + </hbox> + <spacer flex="1"/> + <hbox id="non_unified_buttons"> + <button id="CreateWithDefaults" disabled="true" oncommand="g.stash_and_close('noedit');"/> + <button id="EditThenCreate" disabled="true" oncommand="g.stash_and_close('edit');"/> + </hbox> + <button id="Create" disabled="true" oncommand="g.stash_and_close('unified_interface');"/> + </hbox> + </vbox> +</vbox> </window> diff --git a/Open-ILS/xul/staff_client/server/cat/volume_copy_editor.js b/Open-ILS/xul/staff_client/server/cat/volume_copy_editor.js new file mode 100644 index 0000000000..e15c3951fc --- /dev/null +++ b/Open-ILS/xul/staff_client/server/cat/volume_copy_editor.js @@ -0,0 +1,111 @@ +var error; +var g = {}; + +function my_init() { + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if (typeof JSAN == 'undefined') { throw( "The JSAN library object is missing."); } + JSAN.errorLevel = "die"; // none, warn, or die + JSAN.addRepository('/xul/server/'); + JSAN.use('util.error'); error = new util.error(); + error.sdump('D_TRACE','my_init() for main_test.xul'); + + /*if (typeof window.xulG == 'object' && typeof window.xulG.set_tab_name == 'function') { + try { window.xulG.set_tab_name('Test'); } catch(E) { alert(E); } + }*/ + + // Both interfaces look for this + xulG.unified_interface = true; + + // Item Attribute Editor looks for these + xulG.not_modal = true; + xulG.edit = true; + + // Spawn the volume/copy creator + JSAN.use('util.browser'); + var volume_pane = new util.browser(); + volume_pane.init( + { + 'url' : urls.XUL_VOLUME_COPY_CREATOR_ORIGINAL, + 'push_xulG' : true, + 'alt_print' : false, + 'browser_id' : 'volume_pane', + 'passthru_content_params' : xulG + } + ); + + setup_templates(); + + // Spawn the item attribute editor + var item_pane = new util.browser(); + item_pane.init( + { + 'url' : urls.XUL_COPY_EDITOR, + 'push_xulG' : true, + 'alt_print' : false, + 'browser_id' : 'item_pane', + 'passthru_content_params' : xulG, + 'on_url_load' : g.clone_template_bar // from setup_templates() + } + ); + + } catch(E) { + alert('Error in volume_copy_editor.js, my_init(): ' + E); + } +} + +function setup_templates() { + try { + JSAN.use('util.widgets'); JSAN.use('util.functional'); + + // Once the item attribute editor is loaded, clone and import its template menu to this window with this callback + g.clone_template_bar = function() { + var item_editor_template_bar = get_contentWindow( $('item_pane') ).document.getElementById('template_bar'); + $('template_bar_holder').appendChild( + document.importNode( + item_editor_template_bar, + true // children + ) + ); + item_editor_template_bar.hidden = true; + g.apply_template = function() { + xulG.update_item_editor_template_selection( $('template_menu').value ); + xulG.item_editor_apply_template(); + }; + g.delete_template = function() { + xulG.update_item_editor_template_selection( $('template_menu').value ); + xulG.item_editor_delete_template(); + }; + g.save_template = function() { xulG.item_editor_save_template(); }; + g.import_templates = function() { xulG.item_editor_import_templates(); }; + g.export_templates = function() { xulG.item_editor_apply_templates(); }; + g.reset = function() { xulG.item_editor_reset(); }; + + // just do this once; not sure if on_url_load could fire multiple times + g.clone_template_bar = function() {}; + } + + // callback for populating the list of templates + xulG.update_unified_template_list = function(list) { + try { + util.widgets.remove_children('template_placeholder'); + g.template_menu = util.widgets.make_menulist( list ); + g.template_menu.setAttribute('id','template_menu'); + $('template_placeholder').appendChild(g.template_menu); + g.template_menu.addEventListener( + 'command', + function() { + xulG.update_item_editor_template_selection( g.template_menu.value ); + }, + false + ); + } catch(E) { + alert('Error in volume_copy_editor.js, xulG.update_unified_template_list(): ' + E); + } + }; + + } catch(E) { + alert('Error in volume_copy_editor.js, setup_templates(): ' + E); + } +} + diff --git a/Open-ILS/xul/staff_client/server/cat/volume_copy_editor.xul b/Open-ILS/xul/staff_client/server/cat/volume_copy_editor.xul new file mode 100644 index 0000000000..b35c077c04 --- /dev/null +++ b/Open-ILS/xul/staff_client/server/cat/volume_copy_editor.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<!-- Application: Evergreen Staff Client --> +<!-- Screen: Unified Call Number / Item Editor/Creator --> + +<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> +<!-- STYLESHEETS --> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?> + +<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> +<!-- LOCALIZATION --> +<!DOCTYPE window PUBLIC "" ""[ + <!--#include virtual="/opac/locale/${locale}/lang.dtd"--> +]> + +<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> +<!-- OVERLAYS --> +<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?> + +<window id="volume_item_win" + onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> + <!-- BEHAVIOR --> + <script type="text/javascript"> + var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true; + </script> + <scripts id="openils_util_scripts"/> + + <script type="text/javascript" src="/xul/server/main/JSAN.js"/> + <script type="text/javascript" src="volume_copy_editor.js"/> + + <vbox flex="1"> + <vbox id="top_pane" flex="1" oils_persist="height"> + <hbox id="template_bar_holder"/> + <browser id="volume_pane" flex="1" /> + </vbox> + <splitter + collapse="after" + resizeafter="flex" + resizebefore="flex" + oils_persist="state hidden" + oils_persist_peers="top_pane bottom_pane"> + <grippy/> + </splitter> + <vbox id="bottom_pane" flex="1" oils_persist="height"> + <browser id="item_pane" flex="1" /> + <hbox id="bottom_bar"/> + </vbox> + </vbox> + +</window> + diff --git a/Open-ILS/xul/staff_client/server/cat/volume_copy_editor_horiz.xul b/Open-ILS/xul/staff_client/server/cat/volume_copy_editor_horiz.xul new file mode 100644 index 0000000000..df1b45994f --- /dev/null +++ b/Open-ILS/xul/staff_client/server/cat/volume_copy_editor_horiz.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<!-- Application: Evergreen Staff Client --> +<!-- Screen: Unified Call Number / Item Editor/Creator --> + +<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> +<!-- STYLESHEETS --> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="/xul/server/skin/global.css" type="text/css"?> + +<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> +<!-- LOCALIZATION --> +<!DOCTYPE window PUBLIC "" ""[ + <!--#include virtual="/opac/locale/${locale}/lang.dtd"--> +]> + +<!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> +<!-- OVERLAYS --> +<?xul-overlay href="/xul/server/OpenILS/util_overlay.xul"?> + +<window id="volume_item_win" + onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> + <!-- BEHAVIOR --> + <script type="text/javascript"> + var myPackageDir = 'open_ils_staff_client'; var IAMXUL = true; + </script> + <scripts id="openils_util_scripts"/> + + <script type="text/javascript" src="/xul/server/main/JSAN.js"/> + <script type="text/javascript" src="volume_copy_editor.js"/> + + <hbox flex="1"> + <vbox id="top_pane" flex="1" oils_persist="width"> + <hbox id="template_bar_holder"/> + <browser id="volume_pane" flex="1" /> + </vbox> + <splitter + collapse="after" + resizeafter="flex" + resizebefore="flex" + oils_persist="state hidden" + oils_persist_peers="top_pane bottom_pane"> + <grippy/> + </splitter> + <vbox id="bottom_pane" flex="1" oils_persist="width"> + <browser id="item_pane" flex="1" /> + <hbox id="bottom_bar"/> + </vbox> + </hbox> + +</window> + diff --git a/Open-ILS/xul/staff_client/server/cat/volume_editor.js b/Open-ILS/xul/staff_client/server/cat/volume_editor.js new file mode 100644 index 0000000000..92aeb214cc --- /dev/null +++ b/Open-ILS/xul/staff_client/server/cat/volume_editor.js @@ -0,0 +1,217 @@ +var xulG = {}; + +function my_init() { + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if (typeof JSAN == 'undefined') { throw( $("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 cat/volume_editor.xul'); + + JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'}); + JSAN.use('util.network'); g.network = new util.network(); + + JSAN.use('util.functional'); + + g.volumes = xul_param('volumes',{'stash_name':'volumes_temp','clear_xpcom':true,'modal_xulG':true}); //JSON2js( g.data.volumes_temp ); + //g.data.volumes_temp = ''; g.data.stash('volumes_temp'); + + var rows = document.getElementById('rows'); + + var first_tb; + + for (var i = 0; i < g.volumes.length; i++) { + var row = document.createElement('row'); rows.appendChild(row); + var lib_label = document.createElement('label'); row.appendChild(lib_label); + var class_ml = g.render_class_menu(i); row.appendChild(class_ml); + var prefix_ml = g.render_prefix_menu(i); row.appendChild(prefix_ml); + var label_tb = document.createElement('textbox'); row.appendChild(label_tb); + var suffix_ml = g.render_suffix_menu(i); row.appendChild(suffix_ml); + if (!first_tb) { first_tb = label_tb; } + + var lib_id = g.volumes[i].owning_lib(); + var last_lib_seen; + + if (last_lib_seen != lib_id ) { + lib_label.setAttribute('value',g.data.hash.aou[ lib_id ].shortname() ); + last_lib_seen = lib_id; + } + + label_tb.setAttribute('value',g.volumes[i].label()); + label_tb.setAttribute('onchange','try { var v = g.volumes['+i+']; v.ischanged("1"); v.label( this.value ); } catch(E) { alert(E); }'); + } + + first_tb.select(); first_tb.focus(); + + } catch(E) { + var err_msg = $("commonStrings").getFormattedString('common.exception', ['cat/volume_editor.xul', E]); + try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); } + alert(err_msg); + } +} + +g.stash_and_close = function() { + try { + //g.data.volumes_temp = js2JSON( g.volumes ); + //g.error.sdump('D_CAT','in modal window, g.data.volumes_temp = \n' + g.data.volumes_temp + '\n'); + //g.data.stash('volumes_temp'); + xulG.volumes = g.volumes; + xulG.update_these_volumes = 1; + xulG.auto_merge = document.getElementById('auto_merge').checked; + update_modal_xulG(xulG); + window.close(); + } catch(E) { + alert('FIXME: volume editor -> ' + E); + } +} + +g.render_class_menu = function(vol_idx) { + var ml = util.widgets.make_menulist( + util.functional.map_list( + g.data.list.acnc, + function(o) { + return [ o.name(), o.id() ]; + } + ), + typeof g.volumes[vol_idx].label_class() == 'object' + ? g.volumes[vol_idx].label_class().id() + : g.volumes[vol_idx].label_class() + ); + ml.addEventListener( + 'command', + function(ev) { + g.volumes[vol_idx].ischanged(1); + g.volumes[vol_idx].label_class(ml.value); + }, + false + ); + return ml; +} + +g.render_prefix_menu = function(vol_idx) { + var org = typeof g.volumes[vol_idx].owning_lib() == 'object' + ? g.volumes[vol_idx].owning_lib() + : g.data.hash.aou[ g.volumes[vol_idx].owning_lib() ]; + var menulist = document.createElement('menulist'); + var menupopup = document.createElement('menupopup'); + menulist.appendChild(menupopup); + var org_list = []; // order from top of consortium to owning lib + while(org) { + org_list.unshift(org.id()); + org = org.parent_ou(); + if (org && typeof org != 'object') { + org = g.data.hash.aou[ org ]; + } + } + for (var i = 0; i < org_list.length; i++) { + g.render_prefix_menu_items(menupopup,org_list[i]); + } + menulist.setAttribute('value', + typeof g.volumes[vol_idx].prefix() == 'object' + ? g.volumes[vol_idx].prefix().id() + : g.volumes[vol_idx].prefix() + ); + + menulist.addEventListener( + 'command', + function() { + g.volumes[vol_idx].ischanged(1); + g.volumes[vol_idx].prefix(menulist.value); + }, + false + ); + return menulist; +} + +g.render_prefix_menu_items = function(menupopup,ou_id) { + if (typeof g.data.list['acnp_for_lib_'+ou_id] == 'undefined') { + g.data.list['acnp_for_lib_'+ou_id] = g.network.simple_request( + 'FM_ACNP_RETRIEVE_VIA_PCRUD', + [ ses(), {"owning_lib":{"=":ou_id}}, {"order_by":{"acnp":"label_sortkey"}} ] + ); + g.data.stash('list'); + } + for (var i = 0; i < g.data.list['acnp_for_lib_'+ou_id].length; i++) { + var my_acnp = g.data.list['acnp_for_lib_'+ou_id][i]; + var menuitem = document.createElement('menuitem'); + menupopup.appendChild(menuitem); + menuitem.setAttribute( + 'label', + my_acnp.id() == -1 ? '' : + $('catStrings').getFormattedString( + 'staff.cat.volume_copy_creator.call_number_prefix.menuitem_label', + [ + my_acnp.label(), + g.data.hash.aou[ ou_id ].shortname() + ] + ) + ); + menuitem.setAttribute('value',my_acnp.id()); + } +} + + +g.render_suffix_menu = function(vol_idx) { + var org = typeof g.volumes[vol_idx].owning_lib() == 'object' + ? g.volumes[vol_idx].owning_lib() + : g.data.hash.aou[ g.volumes[vol_idx].owning_lib() ]; + var menulist = document.createElement('menulist'); + var menupopup = document.createElement('menupopup'); + menulist.appendChild(menupopup); + var org_list = []; // order from top of consortium to owning lib + while(org) { + org_list.unshift(org.id()); + org = org.parent_ou(); + if (org && typeof org != 'object') { + org = g.data.hash.aou[ org ]; + } + } + for (var i = 0; i < org_list.length; i++) { + g.render_suffix_menu_items(menupopup,org_list[i]); + } + menulist.setAttribute('value', + typeof g.volumes[vol_idx].suffix() == 'object' + ? g.volumes[vol_idx].suffix().id() + : g.volumes[vol_idx].suffix() + ); + + menulist.addEventListener( + 'command', + function() { + g.volumes[vol_idx].ischanged(1); + g.volumes[vol_idx].suffix(menulist.value); + }, + false + ); + return menulist; +} + +g.render_suffix_menu_items = function(menupopup,ou_id) { + if (typeof g.data.list['acns_for_lib_'+ou_id] == 'undefined') { + g.data.list['acns_for_lib_'+ou_id] = g.network.simple_request( + 'FM_ACNS_RETRIEVE_VIA_PCRUD', + [ ses(), {"owning_lib":{"=":ou_id}}, {"order_by":{"acns":"label_sortkey"}} ] + ); + g.data.stash('list'); + } + for (var i = 0; i < g.data.list['acns_for_lib_'+ou_id].length; i++) { + var my_acns = g.data.list['acns_for_lib_'+ou_id][i]; + var menuitem = document.createElement('menuitem'); + menupopup.appendChild(menuitem); + menuitem.setAttribute( + 'label', + my_acns.id() == -1 ? '' : + $('catStrings').getFormattedString( + 'staff.cat.volume_copy_creator.call_number_suffix.menuitem_label', + [ + my_acns.label(), + g.data.hash.aou[ ou_id ].shortname() + ] + ) + ); + menuitem.setAttribute('value',my_acns.id()); + } +} + + diff --git a/Open-ILS/xul/staff_client/server/cat/volume_editor.xul b/Open-ILS/xul/staff_client/server/cat/volume_editor.xul index b8f57783e7..5a4e5c634b 100644 --- a/Open-ILS/xul/staff_client/server/cat/volume_editor.xul +++ b/Open-ILS/xul/staff_client/server/cat/volume_editor.xul @@ -19,7 +19,7 @@ <window id="cat_volume_editor_win" onload="try { my_init(); font_helper(); persist_helper(); } catch(E) { alert(E); }" - title="&staff.cat.volume_editor.title;" height="400" width="300" oils_persist="height width" + title="&staff.cat.volume_editor.title;" height="800" width="300" oils_persist="height width" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <!-- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// --> @@ -30,76 +30,8 @@ <scripts id="openils_util_scripts"/> <script type="text/javascript" src="/xul/server/main/JSAN.js"/> - <script> - <![CDATA[ + <script type="text/javascript" src="volume_editor.js"/> - var xulG = {}; - - function my_init() { - try { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - if (typeof JSAN == 'undefined') { throw( $("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 cat/volume_editor.xul'); - - JSAN.use('OpenILS.data'); g.data = new OpenILS.data(); g.data.init({'via':'stash'}); - - JSAN.use('util.functional'); - - g.volumes = xul_param('volumes',{'stash_name':'volumes_temp','clear_xpcom':true,'modal_xulG':true}); //JSON2js( g.data.volumes_temp ); - //g.data.volumes_temp = ''; g.data.stash('volumes_temp'); - - var rows = document.getElementById('rows'); - - var first_tb; - - for (var i = 0; i < g.volumes.length; i++) { - var row = document.createElement('row'); rows.appendChild(row); - var lib_label = document.createElement('label'); row.appendChild(lib_label); - var tb = document.createElement('textbox'); row.appendChild(tb); - if (!first_tb) { first_tb = tb; } - - var lib_id = g.volumes[i].owning_lib(); - var last_lib_seen; - - if (last_lib_seen != lib_id ) { - lib_label.setAttribute('value',g.data.hash.aou[ lib_id ].shortname() ); - last_lib_seen = lib_id; - } - - tb.setAttribute('value',g.volumes[i].label()); - tb.setAttribute('onchange','try { var v = g.volumes['+i+']; v.ischanged("1"); v.label( this.value ); } catch(E) { alert(E); }'); - } - - first_tb.select(); first_tb.focus(); - - } catch(E) { - var err_msg = $("commonStrings").getFormattedString('common.exception', ['cat/volume_editor.xul', E]); - try { g.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); } - alert(err_msg); - } - } - - g.stash_and_close = function() { - try { - //g.data.volumes_temp = js2JSON( g.volumes ); - //g.error.sdump('D_CAT','in modal window, g.data.volumes_temp = \n' + g.data.volumes_temp + '\n'); - //g.data.stash('volumes_temp'); - xulG.volumes = g.volumes; - xulG.update_these_volumes = 1; - xulG.auto_merge = document.getElementById('auto_merge').checked; - update_modal_xulG(xulG); - window.close(); - } catch(E) { - alert('FIXME: volume editor -> ' + E); - } - } - - ]]> - </script> - <messagecatalog id="catStrings" src="/xul/server/locale/<!--#echo var='locale'-->/cat.properties" /> <messagecatalog id="circStrings" src="/xul/server/locale/<!--#echo var='locale'-->/circ.properties" /> @@ -115,8 +47,22 @@ <checkbox id="auto_merge" label="&staff.cat.volume_editor.automerge.label;" accesskey="&staff.cat.volume_editor.automerge.accesskey;" oils_persist="checked"/> </hbox> <grid flex="1"> - <columns> <column /> <column /> <column flex="1"/> </columns> - <rows id="rows" /> + <columns> + <column /> + <column /> + <column /> + <column flex="1"/> + <column /> + </columns> + <rows id="rows"> + <row> + <label value="&staff.cat.volume_editor.owning_lib;" class="header"/> + <label value="&staff.cat.volume_editor.classification;" class="header"/> + <label value="&staff.cat.volume_editor.prefix;" class="header"/> + <label value="&staff.cat.volume_editor.label;" class="header"/> + <label value="&staff.cat.volume_editor.suffix;" class="header"/> + </row> + </rows> </grid> </groupbox> diff --git a/Open-ILS/xul/staff_client/server/circ/copy_status.js b/Open-ILS/xul/staff_client/server/circ/copy_status.js index 62a56a31fd..632e77dba2 100644 --- a/Open-ILS/xul/staff_client/server/circ/copy_status.js +++ b/Open-ILS/xul/staff_client/server/circ/copy_status.js @@ -532,8 +532,10 @@ circ.copy_status.prototype = { var title = document.getElementById('circStrings').getFormattedString('staff.circ.copy_status.add_items.title', [r]); + var horizontal_interface = String( obj.data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true'; + var url = window.xulG.url_prefix( horizontal_interface ? urls.XUL_VOLUME_COPY_CREATOR_HORIZONTAL : urls.XUL_VOLUME_COPY_CREATOR ); var w = xulG.new_tab( - window.xulG.url_prefix(urls.XUL_VOLUME_COPY_CREATOR), + url, { 'tab_name' : title }, { 'doc_id' : r, 'ou_ids' : list, 'copy_shortcut' : copy_shortcut[r] } ); @@ -682,8 +684,10 @@ circ.copy_status.prototype = { var title = document.getElementById('circStrings').getFormattedString('staff.circ.copy_status.add_volumes.title', [r]); + var horizontal_interface = String( obj.data.hash.aous['ui.cat.volume_copy_editor.horizontal'] ) == 'true'; + var url = window.xulG.url_prefix( horizontal_interface ? urls.XUL_VOLUME_COPY_CREATOR_HORIZONTAL : urls.XUL_VOLUME_COPY_CREATOR ); var w = xulG.new_tab( - window.xulG.url_prefix(urls.XUL_VOLUME_COPY_CREATOR), + url, { 'tab_name' : title }, { 'doc_id' : r, 'ou_ids' : list } ); diff --git a/Open-ILS/xul/staff_client/server/circ/util.js b/Open-ILS/xul/staff_client/server/circ/util.js index e8a7086b5c..231132fe07 100644 --- a/Open-ILS/xul/staff_client/server/circ/util.js +++ b/Open-ILS/xul/staff_client/server/circ/util.js @@ -571,22 +571,45 @@ circ.util.columns = function(modify,params) { 'flex' : 1, 'primary' : false, 'hidden' : true, - 'editable' : false, 'render' : function(my) { - if (my.acp && my.acp.call_number() == -1) { + 'editable' : false, 'render' : function(my,scratch_data) { + var acn_id; + if (my.acn) { + if (typeof my.acn == 'object') { + acn_id = my.acn.id(); + } else { + acn_id = my.acn; + } + } else if (my.acp) { + if (typeof my.acp.call_number() == 'object') { + acn_id = my.acp.call_number().id(); + } else { + acn_id = my.acp.call_number(); + } + } + if (!acn_id && acn_id != 0) { + return ''; + } else if (acn_id == -1) { return document.getElementById('circStrings').getString('staff.circ.utils.not_cataloged'); - } else if (my.acp && my.acp.call_number() == -2) { + } else if (acn_id == -2) { return document.getElementById('circStrings').getString('staff.circ.utils.retrieving'); } else { if (!my.acn) { - var x = network.simple_request("FM_ACN_RETRIEVE.authoritative",[ my.acp.call_number() ]); - if (x.ilsevent) { - return document.getElementById('circStrings').getString('staff.circ.utils.not_cataloged'); + if (typeof scratch_data['acn_map'] == 'undefined') { + scratch_data['acn_map'] = {}; + } + if (typeof scratch_data['acn_map'][ acn_id ] == 'undefined') { + var x = network.simple_request("FM_ACN_RETRIEVE.authoritative",[ acn_id ]); + if (x.ilsevent) { + return document.getElementById('circStrings').getString('staff.circ.utils.not_cataloged'); + } else { + my.acn = x; + scratch_data['acn_map'][ acn_id ] = my.acn; + } } else { - my.acn = x; return x.label(); + my.acn = scratch_data['acn_map'][ acn_id ]; } - } else { - return my.acn.label(); } + return my.acn.label(); } }, 'persist' : 'hidden width ordinal' @@ -608,6 +631,71 @@ circ.util.columns = function(modify,params) { 'persist' : 'hidden width ordinal' }, { + 'id' : 'prefix', + 'fm_class' : 'acn', + 'label' : document.getElementById('circStrings').getString('staff.circ.utils.prefix'), + 'flex' : 1, + 'primary' : false, + 'hidden' : true, + 'editable' : false, 'render' : function(my) { + if (typeof my.acn == 'undefined') return ''; + return (typeof my.acn.prefix() == 'object') ? my.acn.prefix().label() : my.acn.prefix(); + }, + 'persist' : 'hidden width ordinal' + }, + { + 'id' : 'suffix', + 'fm_class' : 'acn', + 'label' : document.getElementById('circStrings').getString('staff.circ.utils.suffix'), + 'flex' : 1, + 'primary' : false, + 'hidden' : true, + 'editable' : false, 'render' : function(my) { + if (typeof my.acn == 'undefined') return ''; + return (typeof my.acn.suffix() == 'object') ? my.acn.suffix().label() : my.acn.suffix(); + }, + 'persist' : 'hidden width ordinal' + }, + { + 'id' : 'label_class', + 'fm_class' : 'acn', + 'label' : document.getElementById('circStrings').getString('staff.circ.utils.label_class'), + 'flex' : 1, + 'primary' : false, + 'hidden' : true, + 'editable' : false, 'render' : function(my) { + if (typeof my.acn == 'undefined') return ''; + return (typeof my.acn.label_class() == 'object') ? my.acn.label_class().name() : my.acn.label_class(); + }, + 'persist' : 'hidden width ordinal' + }, + { + 'id' : 'parts', + 'fm_class' : 'acp', + 'label' : document.getElementById('commonStrings').getString('staff.acp_label_parts'), + 'flex' : 1, + 'sort_type' : 'number', + 'primary' : false, + 'hidden' : true, + 'editable' : false, 'render' : function(my) { + if (! my.acp.parts()) return ''; + var parts = my.acp.parts(); + var display_string = ''; + for (var i = 0; i < parts.length; i++) { + if (my.doc_id) { + if (my.doc_id == parts[i].record()) { + return parts[i].label(); + } + } else { + if (i != 0) display_string += ' : '; + display_string += parts[i].label(); + } + } + return display_string; + }, + 'persist' : 'hidden width ordinal' + }, + { 'id' : 'copy_number', 'fm_class' : 'acp', 'label' : document.getElementById('commonStrings').getString('staff.acp_label_copy_number'), @@ -2233,9 +2321,11 @@ circ.util.std_map_row_to_column = function(error_value) { }; */ circ.util.std_map_row_to_columns = function(error_value) { - return function(row,cols) { + return function(row,cols,scratch) { // row contains { 'my' : { 'acp' : {}, 'circ' : {}, 'mvr' : {} } } // cols contains all of the objects listed above in columns + // scratch is a temporary space shared by all cells/rows (or just per row if not explicitly passed in) + if (!scratch) { scratch = {}; } var obj = {}; JSAN.use('util.error'); obj.error = new util.error(); @@ -2249,7 +2339,7 @@ circ.util.std_map_row_to_columns = function(error_value) { try { for (var i = 0; i < cols.length; i++) { switch (typeof cols[i].render) { - case 'function': try { values[i] = cols[i].render(my); } catch(E) { values[i] = error_value; obj.error.sdump('D_COLUMN_RENDER_ERROR',E); } break; + case 'function': try { values[i] = cols[i].render(my,scratch); } catch(E) { values[i] = error_value; obj.error.sdump('D_COLUMN_RENDER_ERROR',E); } break; case 'string' : cmd += 'try { ' + cols[i].render + '; values['+i+'] = v; } catch(E) { values['+i+'] = error_value; }'; break; default: cmd += 'values['+i+'] = "??? '+(typeof cols[i].render)+'"; '; } diff --git a/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties b/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties index 79dd237094..b71cdb59bd 100644 --- a/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties +++ b/Open-ILS/xul/staff_client/server/locale/en-US/cat.properties @@ -44,7 +44,6 @@ staff.cat.copy_browser.delete_volume.delete=Delete staff.cat.copy_browser.delete_volume.cancel=Cancel staff.cat.copy_browser.delete_volume.override=Override Delete Failure? staff.cat.copy_browser.delete_volume.copies_remain=You must delete all the copies on the volume before you may delete the volume itself. -staff.cat.copy_browser.delete_volume.success=Volumes deleted. staff.cat.copy_browser.delete_volume.exception=copy browser -> delete volumes staff.cat.copy_browser.mark_library.alert=Library + Record marked as Volume Transfer Destination staff.cat.copy_browser.mark_library.prompt=Choose just one Library to mark as Volume Transfer Destination @@ -392,19 +391,26 @@ staff.cat.util.mark_item_missing_pieces.circ_not_found=No circulation found for staff.cat.volume_buckets.window_tab_name=Volume Buckets staff.cat.volume_copy_creator.my_init.btn.label=Apply staff.cat.volume_copy_creator.my_init.btn.accesskey=A +staff.cat.volume_copy_creator.create_part.btn.label=Create Part Designator staff.cat.volume_copy_creator.edit_then_create.btn.label=Edit then Create staff.cat.volume_copy_creator.edit_then_create.btn.accesskey=C staff.cat.volume_copy_creator.create_with_defaults.btn.label=Create with Defaults staff.cat.volume_copy_creator.create_with_defaults.btn.accesskey=D +staff.cat.volume_copy_creator.create.btn.label=Create Volumes/Items +staff.cat.volume_copy_creator.create.btn.accesskey=C staff.cat.volume_copy_creator.edit_then_rebarcode.btn.label=Edit then Re-barcode staff.cat.volume_copy_creator.edit_then_rebarcode.btn.accesskey=E -staff.cat.volume_copy_creator.rebarcode.btn.label=Re-barcode +staff.cat.volume_copy_creator.rebarcode.btn.label=Re-barcode / Update Items staff.cat.volume_copy_creator.rebarcode.btn.accesskey=R staff.cat.volume_copy_creator.render_volume_count_entry.message=You may not add more than %1$s items at a time for a given volume in this interface. staff.cat.volume_copy_creator.render_volume_count_entry.title=Maximum items exceeded. staff.cat.volume_copy_creator.render_volume_count_entry.ok_label=Ok +staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.classification=Classification +staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.prefix=Prefix staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.call_nums=Call Numbers +staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.suffix=Suffix staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.num_of_copies=# of Copies +staff.cat.volume_copy_creator.render_callnumber_copy_count_entry.barcodes_and_parts=Barcodes / Part Designation staff.cat.volume_copy_creator.render_barcode_entry.alert_message="%1$s" is an invalid barcode. staff.cat.volume_copy_creator.render_barcode_entry.alert_title=Invalid Barcode staff.cat.volume_copy_creator.render_barcode_entry.alert_ok_button=OK @@ -414,6 +420,10 @@ staff.cat.volume_copy_creator.stash_and_close.tree_err2=volume tree update 2 staff.cat.volume_copy_creator.stash_and_close.tree_err3=volume tree update 3 staff.cat.volume_copy_creator.load_prefs.err_retrieving_prefs=Error retrieving stored preferences staff.cat.volume_copy_creator.save_prefs.err_storing_prefs=Error storing preferences +# %1$s = Call Number Prefix Label, %2$s = Call Number Prefix Owning Lib Shortname +staff.cat.volume_copy_creator.call_number_prefix.menuitem_label=%2$s : %1$s +# %1$s = Call Number Suffix Label, %2$s = Call Number Suffix Owning Lib Shortname +staff.cat.volume_copy_creator.call_number_suffix.menuitem_label=%2$s : %1$s staff.cat.z3950.native_catalog=Native Catalog staff.cat.z3950.obj_list_init.list_construction_error=Failure during list construction. staff.cat.z3950.results_view.label=Results View diff --git a/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties b/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties index c6f718d171..508e4041e9 100644 --- a/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties +++ b/Open-ILS/xul/staff_client/server/locale/en-US/circ.properties @@ -238,6 +238,9 @@ staff.circ.utils.offline.use_time=Use Time staff.circ.utils.not_cataloged=Not Cataloged staff.circ.utils.retrieving=Retrieving... staff.circ.utils.owning_lib=Owning Library +staff.circ.utils.prefix=Prefix +staff.circ.utils.suffix=Suffix +staff.circ.utils.label_class=Classification staff.circ.utils.loan_duration.short=Short staff.circ.utils.loan_duration.normal=Normal staff.circ.utils.loan_duration.long=Long diff --git a/Open-ILS/xul/staff_client/server/locale/en-US/common.properties b/Open-ILS/xul/staff_client/server/locale/en-US/common.properties index d5b1ff57c9..14fd00c1ff 100644 --- a/Open-ILS/xul/staff_client/server/locale/en-US/common.properties +++ b/Open-ILS/xul/staff_client/server/locale/en-US/common.properties @@ -37,6 +37,7 @@ staff.acp_label_circ_modifier=Circulation Modifier # %1$s = circ modifier code, %2$s = circ modifier name, %3$s = circ modifier description staff.circ_modifier.display=%1$s : %2$s : %3$s staff.acp_label_copy_number=Copy Number +staff.acp_label_parts=Part staff.acp_label_deposit_amount=Deposit Amount staff.acp_label_fine_level=Fine Level staff.acp_label_id=Copy ID diff --git a/Open-ILS/xul/staff_client/server/patron/util.js b/Open-ILS/xul/staff_client/server/patron/util.js index 0f235efb45..742b6b4995 100644 --- a/Open-ILS/xul/staff_client/server/patron/util.js +++ b/Open-ILS/xul/staff_client/server/patron/util.js @@ -540,9 +540,10 @@ patron.util.columns = function(modify,params) { } patron.util.std_map_row_to_columns = function(error_value) { - return function(row,cols) { + return function(row,cols,scratch) { // row contains { 'my' : { 'au' : {} } } // cols contains all of the objects listed above in columns + // scratch is a temporary space shared by all cells/rows (or just per row if not explicitly passed in) var obj = {}; obj.OpenILS = {}; JSAN.use('util.error'); obj.error = new util.error();