add support for search conjunction ("all" or "any")
authorGalen Charlton <gmc@equinoxinitiative.org>
Wed, 22 Jan 2020 17:23:51 +0000 (12:23 -0500)
committerGalen Charlton <gmc@equinoxinitiative.org>
Wed, 22 Jan 2020 17:23:51 +0000 (12:23 -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/eg2/src/app/staff/acq/search/acq-search-form.component.ts
Open-ILS/src/eg2/src/app/staff/acq/search/acq-search.service.ts
Open-ILS/src/eg2/src/app/staff/acq/search/invoice-results.component.ts
Open-ILS/src/eg2/src/app/staff/acq/search/lineitem-results.component.ts
Open-ILS/src/eg2/src/app/staff/acq/search/picklist-results.component.ts
Open-ILS/src/eg2/src/app/staff/acq/search/purchase-order-results.component.ts

index 72143a0..c375352 100644 (file)
@@ -1,4 +1,14 @@
 <div id="acq-search-form" class="pl-3 pr-3 pt-3 pb-3">
+<div class="row mb-1">
+  <div class="col-lg-5 form-group form-inline">
+    <label i18n>Search for records matching
+    <select class="form-inline ml-1 mr-1" id="acq-search-conjunction" [ngModelOptions]="{standalone: true}" [(ngModel)]="searchConjunction">
+      <option i18n select value="all">all</option>
+      <option i18n select value="any">any</option>
+    </select>
+    of the following terms:</label>
+  </div>
+</div>
 <div class="row mb-1" *ngFor="let t of searchTerms; let idx=index">
   <div class="col-lg-3">
     <select class="form-control" id="selected-search-term" [ngModelOptions]="{standalone: true}" [(ngModel)]="t.field"
index 2ef2da4..1c2474b 100644 (file)
@@ -4,7 +4,7 @@ import {Router, ActivatedRoute} from '@angular/router';
 import {StaffCommonModule} from '@eg/staff/common.module';
 import {IdlService, IdlObject} from '@eg/core/idl.service';
 import {PcrudService} from '@eg/core/pcrud.service';
-import {AcqSearchTerm} from './acq-search.service';
+import {AcqSearchTerm, AcqSearch} from './acq-search.service';
 import {ServerStoreService} from '@eg/core/server-store.service';
 
 @Component({
@@ -18,7 +18,7 @@ export class AcqSearchFormComponent implements OnInit, AfterViewInit {
     @Input() initialSearchTerms: AcqSearchTerm[] = [];
     @Input() defaultSearchSetting = '';
 
-    @Output() searchSubmitted = new EventEmitter<AcqSearchTerm[]>();
+    @Output() searchSubmitted = new EventEmitter<AcqSearch>();
 
     hints = ['jub', 'acqpl', 'acqpo', 'acqinv', 'acqlid'];
     availableSearchFields = {};
@@ -26,6 +26,7 @@ export class AcqSearchFormComponent implements OnInit, AfterViewInit {
     searchFieldLinkedClasses = {};
     validSearchTypes = ['lineitems', 'purchaseorders', 'invoices', 'selectionlists'];
     defaultSearchType = 'lineitems';
+    searchConjunction = 'all';
 
     searchTerms: AcqSearchTerm[] = [];
 
@@ -83,7 +84,8 @@ export class AcqSearchFormComponent implements OnInit, AfterViewInit {
             this.store.getItem(this.defaultSearchSetting).then(
                 defaultSearch => {
                     if (defaultSearch) {
-                        this.searchTerms = JSON.parse(JSON.stringify(defaultSearch));
+                        this.searchTerms = JSON.parse(JSON.stringify(defaultSearch.terms));
+                        this.searchConjunction = defaultSearch.conjunction;
                         this.submitSearch();
                     } else {
                         this.addSearchTerm();
@@ -128,10 +130,16 @@ export class AcqSearchFormComponent implements OnInit, AfterViewInit {
     }
 
     submitSearch() {
-        this.searchSubmitted.emit(this.searchTerms);
+        this.searchSubmitted.emit({
+            terms: this.searchTerms,
+            conjunction: this.searchConjunction
+        });
     }
 
     saveSearchAsDefault() {
-        return this.store.setItem(this.defaultSearchSetting, this.searchTerms);
+        return this.store.setItem(this.defaultSearchSetting, {
+            terms: this.searchTerms,
+            conjunction: this.searchConjunction
+        });
     }
 }
index 47233c8..35a0e1f 100644 (file)
@@ -80,10 +80,16 @@ export interface AcqSearchTerm {
     is_date?: boolean;
 }
 
+export interface AcqSearch {
+    terms: AcqSearchTerm[];
+    conjunction: string;
+}
+
 @Injectable()
 export class AcqSearchService {
 
     _terms: AcqSearchTerm[] = [];
+    _conjunction = 'all';
 
     constructor(
         private net: NetService,
@@ -91,12 +97,14 @@ export class AcqSearchService {
     ) {
     }
 
-    setSearchTerms(terms: AcqSearchTerm[]) {
-        this._terms = terms;
+    setSearch(search: AcqSearch) {
+        this._terms = search.terms;
+        this._conjunction = search.conjunction;
     }
 
     generateAcqSearch(searchType, filters): any {
-        const baseSearch = JSON.parse(JSON.stringify(defaultSearch[searchType])); // deep copy
+        const andTerms = JSON.parse(JSON.stringify(defaultSearch[searchType])); // deep copy
+        const orTerms = {};
         const coreRecType = Object.keys(defaultSearch[searchType])[0];
 
         // handle supplied search terms
@@ -115,13 +123,20 @@ export class AcqSearchService {
             if (term.op !== '') {
                 searchTerm[term.op] = true;
             }
-            if (!(recType in baseSearch)) {
-                baseSearch[recType] = [];
-            }
             if (term.is_date) {
                 searchTerm['__castdate'] = true;
             }
-            baseSearch[recType].push(searchTerm);
+            if (this._conjunction === 'any') {
+                if (!(recType in orTerms)) {
+                    orTerms[recType] = [];
+                }
+                orTerms[recType].push(searchTerm);
+            } else {
+                if (!(recType in andTerms)) {
+                    andTerms[recType] = [];
+                }
+                andTerms[recType].push(searchTerm);
+            }
         });
 
         // handle grid filters
@@ -157,11 +172,11 @@ export class AcqSearchService {
                 if (filterOp in operatorMap) {
                     searchTerm[operatorMap[filterOp]] = true;
                 }
-                baseSearch[coreRecType].push(searchTerm);
+                andTerms[coreRecType].push(searchTerm);
             });
         });
-        console.debug(baseSearch);
-        return baseSearch;
+        console.debug(andTerms);
+        return { andTerms: andTerms, orTerms: orTerms };
     }
 
     getAcqSearchDataSource(searchType: string): GridDataSource {
@@ -178,8 +193,8 @@ export class AcqSearchService {
                 'open-ils.acq',
                 'open-ils.acq.' + searchType + '.unified_search',
                     this.auth.token(),
-                    currentSearch,
-                    null,
+                    currentSearch.andTerms,
+                    currentSearch.orTerms,
                     null,
                     opts
             );
index 30359a8..31c40fe 100644 (file)
@@ -11,7 +11,7 @@ import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {GridComponent} from '@eg/share/grid/grid.component';
 import {GridDataSource} from '@eg/share/grid/grid';
-import {AcqSearchService, AcqSearchTerm} from './acq-search.service';
+import {AcqSearchService, AcqSearchTerm, AcqSearch} from './acq-search.service';
 import {AcqSearchFormComponent} from './acq-search-form.component';
 
 @Component({
@@ -73,8 +73,8 @@ export class InvoiceResultsComponent implements OnInit {
       );
     }
 
-    doSearch(terms: AcqSearchTerm[]) {
-        this.acqSearch.setSearchTerms(terms);
+    doSearch(search: AcqSearch) {
+        this.acqSearch.setSearch(search);
         this.invoiceResultsGrid.reload();
     }
 }
index 1518f9b..c504139 100644 (file)
@@ -8,7 +8,7 @@ import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {GridComponent} from '@eg/share/grid/grid.component';
 import {GridDataSource} from '@eg/share/grid/grid';
-import {AcqSearchService, AcqSearchTerm} from './acq-search.service';
+import {AcqSearchService, AcqSearchTerm, AcqSearch} from './acq-search.service';
 import {AcqSearchFormComponent} from './acq-search-form.component';
 
 @Component({
@@ -35,8 +35,8 @@ export class LineitemResultsComponent implements OnInit {
         this.gridSource = this.acqSearch.getAcqSearchDataSource('lineitem');
     }
 
-    doSearch(terms: AcqSearchTerm[]) {
-        this.acqSearch.setSearchTerms(terms);
+    doSearch(search: AcqSearch) {
+        this.acqSearch.setSearch(search);
         this.lineitemResultsGrid.reload();
     }
 }
index 606eff8..da21498 100644 (file)
@@ -11,7 +11,7 @@ import {AuthService} from '@eg/core/auth.service';
 import {PermService} from '@eg/core/perm.service';
 import {GridComponent} from '@eg/share/grid/grid.component';
 import {GridDataSource} from '@eg/share/grid/grid';
-import {AcqSearchService, AcqSearchTerm} from './acq-search.service';
+import {AcqSearchService, AcqSearchTerm, AcqSearch} from './acq-search.service';
 import {PicklistCreateDialogComponent} from './picklist-create-dialog.component';
 import {PicklistCloneDialogComponent} from './picklist-clone-dialog.component';
 import {PicklistDeleteDialogComponent} from './picklist-delete-dialog.component';
@@ -112,8 +112,8 @@ export class PicklistResultsComponent implements OnInit {
         this.picklistMergeDialog.update(); // update the dialog UI with selections
     }
 
-    doSearch(terms: AcqSearchTerm[]) {
-        this.acqSearch.setSearchTerms(terms);
+    doSearch(search: AcqSearch) {
+        this.acqSearch.setSearch(search);
         this.picklistResultsGrid.reload();
     }
 }
index bb605e6..17e99b4 100644 (file)
@@ -8,7 +8,7 @@ import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {GridComponent} from '@eg/share/grid/grid.component';
 import {GridDataSource} from '@eg/share/grid/grid';
-import {AcqSearchService, AcqSearchTerm} from './acq-search.service';
+import {AcqSearchService, AcqSearchTerm, AcqSearch} from './acq-search.service';
 import {AcqSearchFormComponent} from './acq-search-form.component';
 
 @Component({
@@ -35,8 +35,8 @@ export class PurchaseOrderResultsComponent implements OnInit {
         this.gridSource = this.acqSearch.getAcqSearchDataSource('purchase_order');
     }
 
-    doSearch(terms: AcqSearchTerm[]) {
-        this.acqSearch.setSearchTerms(terms);
+    doSearch(search: AcqSearch) {
+        this.acqSearch.setSearch(search);
         this.purchaseOrderResultsGrid.reload();
     }
 }