implement provider contains searching
authorGalen Charlton <gmc@equinoxinitiative.org>
Sun, 26 Jan 2020 07:31:45 +0000 (02:31 -0500)
committerGalen Charlton <gmc@equinoxinitiative.org>
Sun, 26 Jan 2020 07:31:45 +0000 (02:31 -0500)
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/eg2/src/app/staff/acq/search/acq-search-form.component.html
Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Search.pm

index 0858c8c..87c8769 100644 (file)
@@ -40,7 +40,7 @@
         (change)="clearSearchTermValueAfterOpChange(t)">
         <option i18n value="">is</option>
         <option i18n value="__not">is NOT</option>
-        <option i18n value="__fuzzy" [disabled]="searchTermDatatypes[t.field] != 'text'">contains</option>
+        <option i18n value="__fuzzy" [disabled]="searchTermDatatypes[t.field] != 'text' && searchFieldLinkedClasses[t.field] !== 'acqpro'">contains</option>
         <option i18n value="__not,__fuzzy" [disabled]="searchTermDatatypes[t.field] != 'text'">does NOT contain</option>
         <option i18n value="__starts" [disabled]="searchTermDatatypes[t.field] != 'text'">STARTS with</option>
         <option i18n value="__ends" [disabled]="searchTermDatatypes[t.field] != 'text'">ENDS with</option>
           [initialOrgId]="t.value1"
           (onChange)="setOrgUnitSearchValue($event, t)">
         </eg-org-select>
-        <eg-combobox *ngIf="searchTermDatatypes[t.field] == 'link'"
-          [idlClass]="searchFieldLinkedClasses[t.field]"
-          [selectedId]="t.value1"
-          (onChange)="t.value1 = $event ? $event.id : ''">
-        </eg-combobox>
+        <ng-container *ngIf="searchTermDatatypes[t.field] == 'link'">
+          <ng-container *ngIf="searchFieldLinkedClasses[t.field] === 'acqpro'">
+            <eg-combobox *ngIf="t.op != '__fuzzy'"
+              [idlClass]="searchFieldLinkedClasses[t.field]"
+              [selectedId]="t.value1"
+              (onChange)="t.value1 = $event ? $event.id : ''">
+            </eg-combobox>
+            <input [ngModelOptions]="{standalone: true}" [(ngModel)]="t.value1" type="text" *ngIf="t.op == '__fuzzy'" class="form-control" />
+          </ng-container>
+          <ng-container *ngIf="searchFieldLinkedClasses[t.field] !== 'acqpro'">
+            <eg-combobox
+              [idlClass]="searchFieldLinkedClasses[t.field]"
+              [selectedId]="t.value1"
+              (onChange)="t.value1 = $event ? $event.id : ''">
+            </eg-combobox>
+          </ng-container>
+        </ng-container>
         <eg-date-select *ngIf="searchTermDatatypes[t.field] == 'timestamp' && t.op != '__age'"
           (onChangeAsIso)="t.value1 = $event ? $event : ''; t.is_date = true">
         </eg-date-select>
index f6fa48d..83d23a8 100644 (file)
@@ -248,6 +248,74 @@ sub prepare_au_terms {
     @joins;
 }
 
+sub gen_acqpro_term {
+    my ($value, $n) = @_;
+    my $lc_value = {
+        "=" => { transform => "lowercase", value => lc($value) }
+    };
+
+    +{
+        "-or" => [
+            {"+acqpro$n" => {"name" => $value}},
+            {"+acqpro$n" => {"code" => $lc_value}},
+        ]
+    };
+}
+
+# go through the terms hash, find keys that correspond to fields links
+# to actor.usr, and rewrite the search as one that searches not by
+# actor.usr.id but by any of these user properties: card barcode, username,
+# given names and family name.
+sub prepare_acqpro_terms {
+    my ($terms, $join_num) = @_;
+
+    my @joins = ();
+    my $nots = 0;
+    $join_num ||= 0;
+
+    foreach my $conj (qw/-and -or/) {
+        next unless exists $terms->{$conj};
+
+        my @new_outer_terms = ();
+        HINT_UNIT: foreach my $hint_unit (@{$terms->{$conj}}) {
+            my $hint = (keys %$hint_unit)[0];
+            (my $plain_hint = $hint) =~ y/+//d;
+            if ($hint eq "-not") {
+                $hint_unit = $hint_unit->{$hint};
+                $nots++;
+                redo HINT_UNIT;
+            }
+
+            if (my $links = get_fm_links_by_hint($plain_hint) and
+                $plain_hint ne "acqlia") {
+                my @new_terms = ();
+                my ($attr, $value) = breakdown_term($hint_unit->{$hint});
+                my $is_fuzzy = ref($value) eq 'HASH' && exists($value->{'ilike'});
+                if ($links->{$attr} and
+                    $is_fuzzy and
+                    $links->{$attr}->{"class"} eq "acqpro") {
+                    push @joins, [$plain_hint, $attr, $join_num];
+                    my $acqpro_term = gen_acqpro_term($value, $join_num);
+                    if ($nots > 0) {
+                        $acqpro_term = {"-not" => $acqpro_term};
+                        $nots--;
+                    }
+                    push @new_outer_terms, $acqpro_term;
+                    $join_num++;
+                    delete $hint_unit->{$hint};
+                }
+            }
+            if ($nots > 0) {
+                $hint_unit = {"-not" => $hint_unit};
+                $nots--;
+            }
+            push @new_outer_terms, $hint_unit if scalar keys %$hint_unit;
+        }
+        $terms->{$conj} = [ @new_outer_terms ];
+    }
+    @joins;
+}
+
 sub prepare_terms {
     my ($terms, $is_and) = @_;
 
@@ -367,6 +435,33 @@ sub add_au_joins {
     $n;
 }
 
+sub add_acqpro_joins {
+    my $graft_map = shift;
+    my $core_hint = shift;
+
+    my $n = 0;
+    foreach my $join (@_) {
+        my ($hint, $attr, $num) = @$join;
+        my $start = $graft_map->{$hint};
+        my $clause = {
+            "class" => "acqpro",
+            "type" => "left",
+            "field" => "id",
+            "fkey" => $attr,
+        };
+
+        if ($hint eq $core_hint) {
+            $start->{"acqpro$num"} = $clause;
+        } else {
+            $start->{"join"} ||= {};
+            $start->{"join"}->{"acqpro$num"} = $clause;
+        }
+
+        $n++;
+    }
+    $n;
+}
+
 sub build_from_clause_and_joins {
     my ($query, $core, $and_terms, $or_terms) = @_;
 
@@ -552,6 +647,9 @@ q/order_by clause must be of the long form, like:
         add_au_joins($graft_map, $hint, prepare_au_terms($or_terms, $offset));
     }
 
+    my $offset = add_acqpro_joins($graft_map, $hint, prepare_acqpro_terms($and_terms));
+    add_acqpro_joins($graft_map, $hint, prepare_acqpro_terms($or_terms, $offset));
+
     # The join to acqmapinv needs to be a left join when present.
     if ($query->{from}{$hint}{acqmapinv}) {
         $query->{from}{$hint}{acqmapinv}{type} = "left";