Acq: batch update api - target perm checks
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 12 Feb 2013 19:40:27 +0000 (14:40 -0500)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Mon, 11 Mar 2013 18:00:48 +0000 (14:00 -0400)
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Lineitem.pm

index 9fe3ae6..e96f30e 100644 (file)
@@ -954,4 +954,148 @@ sub retrieve_lineitem_by_copy_id {
     return $li;
 }
 
+# lineitem_batch_update_perm_test()
+#
+# Returns undef on success, event on perm failure.
+# Responsible for calling $e->die_event.
+# Also sanitizes values in $target.
+#
+sub lineitem_batch_update_perm_test {
+    my ($e, $target) = @_;
+
+    return $e->die_event(new OpenILS::Event("BAD_PARAMS", note => "target"))
+        unless ref $target eq "HASH";
+
+    my $perm_for = {
+        ordering_agency => "CREATE_PURCHASE_ORDER",
+        org_unit => "UPDATE_PICKLIST"
+    };
+
+    if (ref $target->{lineitems} eq "ARRAY") {
+        # Sanitization
+        $target->{lineitems} = [ map { int $_ } @{$target->{lineitems}} ];
+
+        return $e->die_event(
+            new OpenILS::Event(
+                "BAD_PARAMS", note => "target (lineitems list empty)"
+            )
+        ) unless @{$target->{lineitems}};
+
+        # Get all PO & picklist linkings from lineitems in question.
+        my $li_rows = $e->json_query({
+            select => {
+                jub => ["id"],
+                acqpo => ["ordering_agency"],
+                acqpl => ["org_unit"]
+            },
+            from => {
+                jub => {acqpl => {type => "left"}, acqpo => {type => "left"}}
+            },
+            where => {
+                "+jub" => {id => $target->{lineitems}}
+            }
+        }) or return $e->die_event;
+
+        # Fail loudly rather than giving user any surprises if they asked to
+        # update lineitems that don't exist.  This is an asymmetric difference
+        # calculation.
+        my %present = map { $_->{id} => 1 } @$li_rows;
+        my @missing = grep { not exists $present{$_} } @{$target->{lineitems}};
+        return $e->die_event(
+            new OpenILS::Event(
+                "ACQ_LINEITEM_NOT_FOUND", payload => \@missing
+            )
+        ) if @missing;
+
+        # To avoid repetition of perm tests, track them here.
+        my $already_done = {
+            ordering_agency => {},
+            org_unit => {}
+        };
+
+        # Test all lineitems based on the context OU of all linked POs AND PLs.
+        foreach my $row (@$li_rows) {
+            foreach my $field (keys %$already_done) {
+                if ($row->{$field}) {
+                    if (not $already_done->{$row}{$field}) {
+                        my $perm = $perm_for->{$field};
+                        my $context = $row->{$field};
+
+                        if (not $e->allowed($perm, $context)) {
+                            my $evt = $e->die_event;
+
+                            # Take the PERM_FAILURE event and annotate it with
+                            # a list of the targeted lineitems that would fail
+                            # the same permission check (i.e. that have the
+                            # same context).
+                            $evt->{payload} = [
+                                map { $_->{id} } (
+                                    grep { $_->{$field} == $context } @$li_rows
+                                )
+                            ];
+                            return $evt;
+                        } else {
+                            $already_done->{$row}{$field} = 1;
+                        }
+                    }
+                }
+            }
+        }
+    } elsif ($target->{purchase_order}) {
+        $target->{purchase_order} = int($target->{purchase_order});
+
+        my $po = $e->retrieve_acq_purchase_order($target->{purchase_order}) or
+            return $e->die_event;
+
+        return $e->die_event unless
+            $e->allowed($perm_for->{ordering_agency}, $po->ordering_agency);
+    } elsif ($target->{picklist}) {
+        $target->{picklist} = int($target->{picklist});
+
+        my $pl = $e->retrieve_acq_picklist($target->{picklist}) or
+            return $e->die_event;
+
+        return $e->die_event unless
+            $e->allowed($perm_for->{org_unit}, $pl->org_unit);
+    } else {
+        return $e->die_event(
+            new OpenILS::Event("BAD_PARAMS", note => "target")
+        );
+    }
+
+    return; # perm check pass
+}
+
+__PACKAGE__->register_method(
+    method => "lineitem_batch_update_api",
+    api_name => "open-ils.acq.lineitem.batch_update",
+    signature => {
+        desc => "Apply changes to lineitems in batch",
+        params => [
+            {desc => "Authentication token", type => "string"},
+            {desc => "Target. Object key must be one of lineitems, purchase_order or picklist.  The value for 'lineitems' must be an array of IDs, and the values for either of the other two must be single IDs.", type => "object"},
+            {desc => "Changes (optional).  If these changes conflict with distribution formula, these changes win.", type => "object"},
+            {desc => "Distribution formula ID (optional)", type => "number"}
+        ],
+        return => {
+            # XXX TODO
+        }
+    }
+);
+
+sub lineitem_batch_update_api {
+    my ($self, $conn, $auth, $target, $changes, $dist_formula) = @_;
+
+    my $e = new_editor("authtoken" => $auth, xact => 1);
+    return $e->die_event unless $e->checkauth;
+
+    # lineitem_batch_update_perm_test() will call die_event() for us if needed.
+    my $evt = lineitem_batch_update_perm_test($e, $target);
+    return $evt if $U->event_code($evt);
+
+    $e->rollback;
+
+    return 1;
+}
+
 1;