UNFINISHED Acq: If deleting on-order copies, remove fund debits and (if confirmed... acq-li-delete-debits-and-copies
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 11 Jun 2013 21:28:17 +0000 (17:28 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 11 Jun 2013 21:41:03 +0000 (17:41 -0400)
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Lineitem.pm
Open-ILS/web/js/dojo/openils/Util.js
Open-ILS/web/js/ui/default/acq/common/li_table.js

index 35d4648..cd5d0af 100644 (file)
@@ -213,6 +213,53 @@ __PACKAGE__->register_method(
     }
 );
 
+
+# Deletes one fund debit given its ID ($debit_id) *iff* it's an orphan
+# (i.e., it's not related to any of the tables that can point to a fund_debit,
+# not including the lineitem detail with ID $lid_id).
+#
+# Returns nothing on success, event on failure.
+sub delete_orphan_fund_debit {
+    my ($e, $debit_id, $lid_id) = @_;
+
+    # Make sure the debit is orphaned first.
+    my $rows = $e->json_query({
+        select => {acqfdeb => ["id"]},
+        from => {
+            acqfdeb => {
+                acqii => {
+                    type => "left", field => "fund_debit", fkey => "id"
+                },
+                acqpoi => {
+                    type => "left", field => "fund_debit", fkey => "id"
+                },
+                acqlid => {
+                    type => "left", field => "fund_debit", fkey => "id"
+                },
+                acqda => {
+                    type => "left", field => "fund_debit", fkey => "id"
+                }
+            }
+        },
+        where => {
+            "+acqfdeb" => {id => $debit_id},
+            "+acqii" => {id => undef},
+            "+acqpoi" => {id => undef},
+            "+acqda" => {id => undef},
+            "+acqlid" => {id => $lid_id},
+        }
+    }) or return $e->die_event;
+
+    if (@$rows) {
+        $e->delete_acq_fund_debit(
+            $e->retrieve_acq_fund_debit($rows->[0]->{id})
+        ) or return $e->die_event;
+    }
+
+    return; # success
+}
+
+
 __PACKAGE__->register_method(
     method    => 'delete_lineitem',
     api_name  => 'open-ils.acq.picklist.lineitem.delete',
@@ -221,13 +268,14 @@ __PACKAGE__->register_method(
         params => [
             {desc => 'Authentication token',  type => 'string'},
             {desc => 'lineitem ID to delete', type => 'number'},
+            {desc => 'Even delete associated copies', type => 'boolean'}
         ],
         return => {desc => '1 on success, Event on error'}
     }
 );
 
 sub delete_lineitem {
-    my($self, $conn, $auth, $li_id) = @_;
+    my($self, $conn, $auth, $li_id, $even_copies) = @_;
     my $e = new_editor(xact=>1, authtoken=>$auth);
     return $e->die_event unless $e->checkauth;
 
@@ -260,9 +308,43 @@ sub delete_lineitem {
         {lineitem => $li_id}, {idlist=>1});
 
     for my $lid_id (@$lid_ids) {
-        $e->delete_acq_lineitem_detail(
-            $e->retrieve_acq_lineitem_detail($lid_id))
-            or return $e->die_event;
+        # As of 2.4 (specifically as of commit 76d2e15b), it shouldn't be
+        # possible in normal workflows to delete lineitems attached to
+        # activated purchase orders.  But it's probably possible through some
+        # side-door or other, and it's definitely possible if you go straight
+        # to the middle layer (with suitable authentication, of course).
+        #
+        # Until/unless we decide that should be tightened, we need to be
+        # careful not to leave orphaned fund debits hanging around when
+        # lineitems are deleted.
+
+        my $lid = $e->retrieve_acq_lineitem_detail($lid_id);
+
+        if ($lid->fund_debit) {
+            my $evt = delete_orphan_fund_debit($e, $lid->fund_debit, $lid->id);
+            return $evt if $evt;
+        }
+
+        if ($lid->eg_copy_id) {
+            if ($even_copies) {
+                my $copy = $e->retrieve_asset_copy([
+                    $lid->eg_copy_id,
+                    {flesh => 1, flesh_fields  => {"acp" => ["call_number"]}}
+                ]) or return $e->die_event;
+
+                $e->allowed("DELETE_COPY", $copy->call_number->owning_lib) or
+                    return $e->die_event;
+
+                $e->delete_asset_copy($copy) or return $e->die_event;
+            } else {
+                $e->rollback;
+                return new OpenILS::Event(
+                    "NO_CHANGE", payload => $lid->eg_copy_id
+                );
+            }
+        }
+
+        $e->delete_acq_lineitem_detail($lid) or return $e->die_event;
     }
 
     $e->delete_acq_lineitem($li) or return $e->die_event;
index 804d13c..c8099b5 100644 (file)
@@ -163,23 +163,30 @@ if(!dojo._hasResource["openils.Util"]) {
      * false, the response content will be null when an event is encountered.
      * @param isList If true, assume the response will be a list of data and
      * check the 1st item in the list for event-ness instead of the list itself.
+     * @param eventDispatch optional dispatch table indexed by event textcode,
+     *  with methods taking 1) event and 2) raw value as args.
      */
     openils.Util.alertEvent = true;
-    openils.Util.readResponse = function(r, eventOk, isList) {
+    openils.Util.readResponse = function(r, eventOk, isList, eventDispatch) {
         var msg = r.recv();
-        if(msg == null) return msg;
+        if (msg == null) return msg;
         var val = msg.content();
         var testval = val;
-        if(isList && dojo.isArray(val))
+        if (isList && dojo.isArray(val))
             testval = val[0];
-        if(e = openils.Event.parse(testval)) {
-            if(eventOk || e.textcode == 'SUCCESS') return e;
-            console.log(e.toString());
+        if (e = openils.Event.parse(testval)) {
+            if (e.textcode != "NO_SESSION") {
+                if (eventOk || e.textcode == 'SUCCESS') {
+                    return e;
+                } else if (eventDispatch && e.textcode in eventDispatch) {
+                    return eventDispatch[e.textcode](e, val);
+                }
+                console.log(e.toString());
+            } else {
 
             // session timed out.  Stop propagation of requests queued by Util.onload 
             // and launch the XUL login dialog if possible
-            var retryLogin = false;
-            if(e.textcode == 'NO_SESSION') {
+                var retryLogin = false;
                 openils.User.authtoken = null; 
                 if(openils.XUL.isXUL()) {
                     retryLogin = true;
@@ -190,7 +197,7 @@ if(!dojo._hasResource["openils.Util"]) {
                 }
             }
 
-            if(openils.Util.alertEvent && !retryLogin)
+            if (openils.Util.alertEvent && !retryLogin)
                 alert(e);
             return null;
         }
index 7a804da..de56fa0 100644 (file)
@@ -3179,7 +3179,7 @@ function AcqLiTable() {
         );
     }
 
-    this._deleteLiList = function(list, idx) {
+    this._deleteLiList = function(list, idx, even_copies) {
         if(idx == null) idx = 0;
         if(idx >= list.length) return;
 
@@ -3214,10 +3214,22 @@ function AcqLiTable() {
             ['open-ils.acq',
              this.isPO ? 'open-ils.acq.purchase_order.lineitem.delete' : 'open-ils.acq.picklist.lineitem.delete'],
             {   async: true,
-                params: [openils.User.authtoken, liId],
+                params: [openils.User.authtoken, liId, even_copies],
                 oncomplete: function(r) {
-                    self.removeLineitem(liId);
-                    self._deleteLiList(list, ++idx);
+                    r = openils.Util.readResponse(
+                        r, false, false, {
+                            "NO_CHANGE": function(e, val) {
+                                /* XXX replace with dijit.Dialog before publishing */ if (confirm(localeStrings.DELETE_LID_REAL_COPY)) {
+                                    self._deleteLiList(list, idx, true);
+                                }
+                                return 0;
+                            }
+                        }
+                    );
+                    if (r) {    /* worked fine, didn't get NO_CHANGE */
+                        self.removeLineitem(liId);
+                        self._deleteLiList(list, ++idx, even_copies);
+                    }
                 }
             }
         );