Serials: add "clone subscription" functionality to alt serials control
authorsenator <senator@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Wed, 26 Jan 2011 23:04:56 +0000 (23:04 +0000)
committersenator <senator@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Wed, 26 Jan 2011 23:04:56 +0000 (23:04 +0000)
This feature clones subscriptions and all the objects related to them
that don't describe holdings.  Should help speed users' workflow when setting
up subscriptions.

git-svn-id: svn://svn.open-ils.org/ILS/trunk@19311 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/OpenILS/Application/Serial.pm
Open-ILS/web/js/ui/default/serial/subscription.js
Open-ILS/web/templates/default/serial/subscription.tt2

index 0ad3a7a..4c551a7 100644 (file)
@@ -3359,10 +3359,12 @@ SELECT  usr,
                        <field reporter:label="Distribution" name="distribution" reporter:datatype="link"/>
                        <field reporter:label="Routing Label" name="routing_label" reporter:datatype="text"/>
                        <field reporter:label="Items" name="items" oils_persist:virtual="true" reporter:datatype="link" />
+                       <field reporter:label="Routing List Users" name="routing_list_users" oils_persist:virtual="true" reporter:datatype="link"/>
                </fields>
                <links>
                        <link field="distribution" reltype="has_a" key="id" map="" class="sdist"/>
                        <link field="items" reltype="has_many" key="id" map="" class="sitem"/>
+                       <link field="routing_list_users" reltype="has_many" key="stream" map="" class="srlu"/>
                </links>
                <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
                        <actions>
index 9612a7b..c70aefe 100644 (file)
@@ -2791,7 +2791,7 @@ __PACKAGE__->register_method(
         "params" => [
             {"desc" => "Search token", "type" => "string"},
             {"desc" => "Options: require_subscriptions, add_mvr, is_actual_id" .
-                " (all boolean)", "type" => "object"}
+                ", id_list (all boolean)", "type" => "object"}
         ],
         "return" => {
             "desc" => "Any matching BREs, or if the add_mvr option is true, " .
@@ -2835,6 +2835,12 @@ sub bre_by_identifier {
             $e->disconnect;
             return undef;
         }
+
+        if ($options->{"id_list"}) {
+            $e->disconnect;
+            $client->respond($_) foreach (@ids);
+            return undef;
+        }
     }
 
     my $bre = $e->search_biblio_record_entry([
@@ -3274,4 +3280,187 @@ sub create_scaps_from_marcxml {
     undef;
 }
 
+# All these _clone_foo() functions could possibly have been consolidated into
+# one clever function, but it's faster to get things working this way.
+sub _clone_subscription {
+    my ($sub, $bib_id, $e) = @_;
+
+    # clone sub itself
+    my $new_sub = $sub->clone;
+    $new_sub->record_entry(int $bib_id) if $bib_id;
+    $new_sub->clear_id;
+    $new_sub->clear_distributions;
+    $new_sub->clear_notes;
+    $new_sub->clear_scaps;
+
+    $e->create_serial_subscription($new_sub) or return $e->die_event;
+
+    my $new_sub_id = $e->data->id;
+    # clone dists
+    foreach my $dist (@{$sub->distributions}) {
+        my $r = _clone_distribution($dist, $new_sub_id, $e);
+        return $r if $U->event_code($r);
+    }
+
+    # clone sub notes
+    foreach my $note (@{$sub->notes}) {
+        my $r = _clone_subscription_note($note, $new_sub_id, $e);
+        return $r if $U->event_code($r);
+    }
+
+    # clone scaps
+    foreach my $scap (@{$sub->scaps}) {
+        my $r = _clone_caption_and_pattern($scap, $new_sub_id, $e);
+        return $r if $U->event_code($r);
+    }
+
+    return $new_sub_id;
+}
+
+sub _clone_distribution {
+    my ($dist, $sub_id, $e) = @_;
+
+    my $new_dist = $dist->clone;
+    $new_dist->clear_id;
+    $new_dist->clear_notes;
+    $new_dist->clear_streams;
+    $new_dist->subscription($sub_id);
+
+    $e->create_serial_distribution($new_dist) or return $e->die_event;
+    my $new_dist_id = $e->data->id;
+
+    # clone streams
+    foreach my $stream (@{$dist->streams}) {
+        my $r = _clone_stream($stream, $new_dist_id, $e);
+        return $r if $U->event_code($r);
+    }
+
+    # clone distribution notes
+    foreach my $note (@{$dist->notes}) {
+        my $r = _clone_distribution_note($note, $new_dist_id, $e);
+        return $r if $U->event_code($r);
+    }
+
+    return $new_dist_id;
+}
+
+sub _clone_subscription_note {
+    my ($note, $sub_id, $e) = @_;
+
+    my $new_note = $note->clone;
+    $new_note->clear_id;
+    $new_note->creator($e->requestor->id);
+    $new_note->create_date("now");
+    $new_note->subscription($sub_id);
+
+    $e->create_serial_subscription_note($new_note) or return $e->die_event;
+    return $e->data->id;
+}
+
+sub _clone_caption_and_pattern {
+    my ($scap, $sub_id, $e) = @_;
+
+    my $new_scap = $scap->clone;
+    $new_scap->clear_id;
+    $new_scap->subscription($sub_id);
+
+    $e->create_serial_caption_and_pattern($new_scap) or return $e->die_event;
+    return $e->data->id;
+}
+
+sub _clone_distribution_note {
+    my ($note, $dist_id, $e) = @_;
+
+    my $new_note = $note->clone;
+    $new_note->clear_id;
+    $new_note->creator($e->requestor->id);
+    $new_note->create_date("now");
+    $new_note->distribution($dist_id);
+
+    $e->create_serial_distribution_note($new_note) or return $e->die_event;
+    return $e->data->id;
+}
+
+sub _clone_stream {
+    my ($stream, $dist_id, $e) = @_;
+
+    my $new_stream = $stream->clone;
+    $new_stream->clear_id;
+    $new_stream->clear_routing_list_users;
+    $new_stream->distribution($dist_id);
+
+    $e->create_serial_stream($new_stream) or return $e->die_event;
+    my $new_stream_id = $e->data->id;
+
+    # clone routing list users
+    foreach my $user (@{$stream->routing_list_users}) {
+        my $r = _clone_routing_list_user($user, $new_stream_id, $e);
+        return $r if $U->event_code($r);
+    }
+
+    return $new_stream_id;
+}
+
+sub _clone_routing_list_user {
+    my ($user, $stream_id, $e) = @_;
+
+    my $new_user = $user->clone;
+    $new_user->clear_id;
+    $new_user->stream($stream_id);
+
+    $e->create_serial_routing_list_user($new_user) or return $e->die_event;
+    return $e->data->id;
+}
+
+__PACKAGE__->register_method(
+    "method" => "clone_subscription",
+    "api_name" => "open-ils.serial.subscription.clone",
+    "signature" => {
+        "desc" => q{Clone a subscription, including its attending distributions,
+            streams, captions and patterns, routing list users, distribution
+            notes and subscription notes. Do not include holdings-specific
+            things, like issuances, items, units, summaries. Attach the
+            clone either to the same bib record as the original, or to one
+            specified by ID.},
+        "params" => [
+            {"desc" => "Authtoken", "type" => "string"},
+            {"desc" => "Subscription ID", "type" => "number"},
+            {"desc" => "Bib Record ID (optional)", "type" => "number"}
+        ],
+        "return" => {
+            "desc" => "ID of the new subscription", "type" => "number"
+        }
+    }
+);
+
+sub clone_subscription {
+    my ($self, $client, $auth, $sub_id, $bib_id) = @_;
+
+    my $e = new_editor("authtoken" => $auth, "xact" => 1);
+    return $e->die_event unless $e->checkauth;
+
+    my $sub = $e->retrieve_serial_subscription([
+        int $sub_id, {
+            "flesh" => 3,
+            "flesh_fields" => {
+                "ssub" => [qw/distributions notes scaps/],
+                "sdist" => [qw/streams notes/],
+                "sstr" => ["routing_list_users"]
+            }
+        }
+    ]) or return $e->die_event;
+
+    # ADMIN_SERIAL_SUBSCRIPTION will have to be good enough as a
+    # catch-all permisison for this operation.
+    return $e->die_event unless
+        $e->allowed("ADMIN_SERIAL_SUBSCRIPTION", $sub->owning_lib);
+
+    my $result = _clone_subscription($sub, $bib_id, $e);
+
+    return $e->die_event($result) if $U->event_code($result);
+
+    $e->commit or return $e->die_event;
+    return $result;
+}
+
 1;
index a37609b..2429836 100644 (file)
@@ -167,6 +167,61 @@ function open_batch_receive() {
     }
 }
 
+function toggle_clone_ident_field(dij) {
+    setTimeout(
+        function() {
+            var disabled = !dij.attr("checked");
+            clone_ident.attr("disabled", disabled);
+            if (!disabled) clone_ident.focus();
+        }, 175
+    );
+}
+
+function clone_subscription(form) {
+    if (form.use_ident == "yes") {
+        fieldmapper.standardRequest(
+            ["open-ils.serial",
+                "open-ils.serial.biblio.record_entry.by_identifier.atomic"], {
+                "params": [form.ident, {"id_list": true}],
+                "async": true,
+                "oncomplete": function(r) {
+                    r = openils.Util.readResponse(r);
+                    if (!r || !r.length) {
+                        alert("No matches for that indentifier."); /*XXX i18n*/
+                    } else if (r.length != 1) {
+                        alert("Too many matches for that identifier. Use a " +
+                            "unique identifier."); /* XXX i18n */
+                    } else {
+                        _clone_subscription(r[0]);
+                    }
+                }
+            }
+        );
+    } else {
+        _clone_subscription();
+    }
+}
+
+function _clone_subscription(bre_id) {
+    progress_dialog.show(true);
+
+    fieldmapper.standardRequest(
+        ["open-ils.serial", "open-ils.serial.subscription.clone"], {
+            "params": [openils.User.authtoken, sub_id, bre_id],
+            "async": false,
+            "oncomplete": function(r) {
+                progress_dialog.hide();
+                if (!(r = openils.Util.readResponse(r))) {
+                    alert("Error cloning subscription."); /* XXX i18n */
+                } else {
+                    location.href =
+                        oilsBasePath + "/serial/subscription?id=" + r;
+                }
+            }
+        }
+    );
+}
+
 openils.Util.addOnLoad(
     function() {
         var tab_dispatch = {
index 6c40ed0..95a5f8d 100644 (file)
@@ -13,6 +13,9 @@
 <div dojoType="dijit.layout.ContentPane" layout="top" class="oils-header-panel">
     <div>Subscription Details</div>
     <div>
+        <span dojoType="dijit.form.Button" onClick="clone_dialog.show();">
+            Clone Subscription
+        </span>
         <span dojoType="dijit.form.Button" onClick="open_batch_receive();">
             Batch Item Receive
         </span>
 </div>
 <div class="hidden">
     <div dojoType="openils.widget.ProgressDialog" jsId="progress_dialog"></div>
+    <div dojoType="dijit.Dialog" jsId="clone_dialog" title="Clone Subscription"
+        style="width: 460px;" execute="clone_subscription(arguments[0]);">
+        <div style="margin-bottom: 0.5em;">
+            This feature will clone a subscription and all of its subscription
+            notes, distributions, distribution notes, captions and patterns,
+            streams, and routing list users.</div>
+        <div style="margin-bottom: 0.5em;">
+            Holdings-related objects, like issuances, items, units, and
+            summaries will <em>not</em> be cloned.
+        </div>
+        <div style="margin-bottom: 0.5em;">
+            To what bibliographic record should the new subscription be
+            attached?
+        </div>
+        <table width="100%">
+            <tr>
+                <td style="padding-right: 0.5em; vertical-align: top;">
+                    <input dojoType="dijit.form.RadioButton"
+                        id="use_ident_no" name="use_ident" value="no"
+                        checked="checked" />
+                </td>
+                <td>
+                    <label for="use_ident_no">
+                        Same record as the existing subscription
+                    </label>
+                </td>
+            </tr>
+            <tr>
+                <td style="padding-right: 0.5em; vertical-align: top;">
+                    <input dojoType="dijit.form.RadioButton"
+                        onChange="toggle_clone_ident_field(this);"
+                        id="use_ident_yes" name="use_ident" value="yes" />
+                </td>
+                <td>
+                    <label for="use_ident_yes">
+                        Record specified by this unique identifier:
+                    </label>
+                    <input dojoType="dijit.form.TextBox" name="ident"
+                        jsId="clone_ident" disabled="true"
+                        style="margin-left: 0.5em; width: 10em;" />
+                </td>
+            </tr>
+            <tr>
+                <td colspan="2" align="center">
+                    <span dojoType="dijit.form.Button" type="submit">
+                        Clone!
+                    </span>
+                </td>
+            </tr>
+        </table>
+    </div>
 </div>
 [% END %]