LP1904036 Stat cats and surveys
authorBill Erickson <berickxx@gmail.com>
Mon, 22 Mar 2021 16:11:10 +0000 (12:11 -0400)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:28 +0000 (20:13 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts
Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts

index 86d522d..2412f67 100644 (file)
 
   <button class="btn btn-success" (click)="newAddr()" i18n>New Address</button>
 
+  <div class="alert alert-success p-2 m-3" i18n>Statistical Categories</div>
+
+  <div class="row pt-1 pb-1 mt-1" *ngFor="let stat of statCats">
+   <div class="col-lg-3 field-label">
+    <label for="asc-{{stat.cat.id()}}-input">{{stat.cat.name()}}</label>
+   </div>
+   <div class="col-lg-3">
+    <eg-combobox
+      domId="asc-{{stat.cat.id()}}-input"
+      name="asc-{{stat.cat.id()}}-input"
+      [entries]="stat.entries"
+      [allowFreeText]="stat.cat.allow_freetext() == 1"
+      [(ngModel)]="userStatCats[stat.cat.id()]"
+      (onChange)="userStatCatChange(stat.cat, $event)">
+    </eg-combobox>
+   </div>
+  </div>
+
+  <div class="alert alert-success p-2 m-3" i18n>Surveys</div>
+
+  <div class="row pt-1 pb-1 mt-1" *ngFor="let survey of surveys">
+   <div class="col-lg-3 field-label">
+    <label for="asv-{{survey.id()}}-input">{{survey.name()}}</label>
+   </div>
+   <div class="col-lg-3">
+  </div>
+
 </div>
 
index 0d26483..4d5a52c 100644 (file)
@@ -47,13 +47,6 @@ const PERMS_NEEDED = [
     'UPDATE_PATRON_PRIMARY_CARD'
 ];
 
-const FLESH_PATRON_FIELDS = {
-  flesh: 1,
-  flesh_fields: {
-    au: ['card', 'mailing_address', 'billing_address', 'addresses', 'settings']
-  }
-};
-
 interface StatCat {
     cat: IdlObject;
     entries: ComboboxEntry[];
@@ -75,20 +68,23 @@ export class EditComponent implements OnInit {
     @ViewChild('secondaryGroupsDialog')
         private secondaryGroupsDialog: SecondaryGroupsDialogComponent;
 
+    autoId = -1;
     patron: IdlObject;
     changeHandlerNeeded = false;
     nameTab = 'primary';
     loading = false;
 
+    surveys: IdlObject[];
     smsCarriers: ComboboxEntry[];
     identTypes: ComboboxEntry[];
     inetLevels: ComboboxEntry[];
     orgSettings: {[name: string]: any} = {};
+    statCats: StatCat[] = [];
+    userStatCats: {[statId: number]: ComboboxEntry} = {};
     userSettings: {[name: string]: any} = {};
     userSettingTypes: {[name: string]: IdlObject} = {};
     optInSettingTypes: {[name: string]: IdlObject} = {};
     secondaryGroups: IdlObject[];
-    statCats: StatCat[] = [];
     expireDate: Date;
 
     // All locations we have the specified permissions
@@ -121,25 +117,38 @@ export class EditComponent implements OnInit {
 
     load(): Promise<any> {
         this.loading = true;
-        return this.loadPatron()
+        return this.setStatCats()
+        .then(_ => this.setSurveys())
+        .then(_ => this.loadPatron())
         .then(_ => this.getSecondaryGroups())
         .then(_ => this.applyPerms())
         .then(_ => this.setIdentTypes())
         .then(_ => this.setInetLevels())
         .then(_ => this.setOptInSettings())
         .then(_ => this.setOrgSettings())
-        .then(_ => this.setStatCats())
         .then(_ => this.setSmsCarriers())
-        .finally(() => this.loading = false);
+        .then(_ => this.loading = false);
+    }
+
+    setSurveys(): Promise<any> {
+        return this.patronService.getSurveys()
+        .then(surveys => this.surveys = surveys);
+    }
+
+    surveyQuestionAnswers(question: IdlObject): ComboboxEntry[] {
+        return question.answers().map(
+            a => ({id: a.id(), label: a.answer(), fm: a}));
     }
 
     setStatCats(): Promise<any> {
         this.statCats = [];
         return this.patronService.getStatCats().then(cats => {
             cats.forEach(cat => {
-                const entries = cat.entries.map(entry => {
-                    return {id: entry.id(), label: entry.value()};
-                });
+                cat.id(Number(cat.id()));
+                cat.entries().forEach(entry => entry.id(Number(entry.id())));
+
+                const entries = cat.entries().map(entry =>
+                    ({id: entry.id(), label: entry.value()}));
 
                 this.statCats.push({
                     cat: cat,
@@ -211,7 +220,6 @@ export class EditComponent implements OnInit {
     }
 
     setOptInSettings(): Promise<any> {
-
         const orgIds = this.org.ancestors(this.auth.user().ws_ou(), true);
 
         const query = {
@@ -243,7 +251,7 @@ export class EditComponent implements OnInit {
 
     loadPatron(): Promise<any> {
         if (this.patronId) {
-            return this.patronService.getById(this.patronId, FLESH_PATRON_FIELDS)
+            return this.patronService.getFleshedById(this.patronId)
             .then(patron => {
                 this.patron = patron;
                 this.absorbPatronData();
@@ -279,6 +287,28 @@ export class EditComponent implements OnInit {
         }
 
         this.expireDate = new Date(this.patron.expire_date());
+
+        // stat_cat_entries() are entry maps under the covers.
+        this.patron.stat_cat_entries().forEach(map => {
+            const stat: StatCat = this.statCats.filter(
+                stat => stat.cat.id() === map.stat_cat())[0];
+            let cboxEntry: ComboboxEntry =
+                stat.entries.filter(e => e.label === map.stat_cat_entry())[0];
+
+            if (!cboxEntry) {
+                // If the applied value is not in the list of entries,
+                // create a freetext combobox entry for it.
+                cboxEntry = {
+                    id: null,
+                    freetext: true,
+                    label: map.stat_cat_entry()
+                };
+
+                stat.entries.unshift(cboxEntry);
+            }
+
+            this.userStatCats[map.stat_cat()] = cboxEntry;
+        });
     }
 
     createNewPatron() {
@@ -320,6 +350,12 @@ export class EditComponent implements OnInit {
         return this.objectFromPath(path, index)[field]();
     }
 
+
+    userStatCatChange(cat: IdlObject, entry: ComboboxEntry) {
+        // TODO: set dirty
+        // 2-way binding at work, no need to set the value
+    }
+
     userSettingChange(name: string, value: any) {
         // TODO: set dirty
         this.userSettings[name] = value;
index ea1fb63..385c9b9 100644 (file)
@@ -18,6 +18,7 @@ export class PatronService {
     profileGroups: IdlObject[];
     smsCarriers: IdlObject[];
     statCats: IdlObject[];
+    surveys: IdlObject[];
 
     constructor(
         private net: NetService,
@@ -64,6 +65,16 @@ export class PatronService {
         return this.pcrud.retrieve('au', id, pcrudOps).toPromise();
     }
 
+
+    // Alternate retrieval method that uses the fleshed user API,
+    // which performs some additional data munging on the back end.
+    getFleshedById(id: number, fleshFields?: string[]): Promise<IdlObject> {
+        return this.net.request(
+            'open-ils.actor',
+            'open-ils.actor.user.fleshed.retrieve',
+            this.auth.token(), id, fleshFields).toPromise();
+    }
+
     // Returns a name part (e.g. family_name) with preference for
     // preferred name value where available.
     namePart(patron: IdlObject, part: string): string {
@@ -148,7 +159,7 @@ export class PatronService {
             'open-ils.circ.stat_cat.actor.retrieve.all',
             this.auth.token(), this.auth.user().ws_ou()
         ).toPromise().then(cats => {
-            cats = cats.sort((a, b) => a.name() < b.name() ? -1 : 1);
+            cats.sort((a, b) => a.name() < b.name() ? -1 : 1);
             cats.forEach(cat => {
                 cat.entries(
                     cat.entries().sort((a,b) => a.value() < b.value() ? -1 : 1)
@@ -157,5 +168,30 @@ export class PatronService {
             return cats;
         });
     }
+
+    getSurveys(): Promise<IdlObject[]> {
+        if (this.surveys) {
+            return Promise.resolve(this.surveys);
+        }
+
+        const orgIds = this.org.fullPath(this.auth.user().ws_ou(), true);
+
+        return this.pcrud.search('asv', {
+                owner: orgIds,
+                start_date: {'<=': 'now'},
+                end_date: {'>=': 'now'}
+            }, {
+                flesh: 2,
+                flesh_fields: {
+                    asv: ['questions'],
+                    asvq: ['answers']
+                }
+            },
+            {atomic : true}
+        ).toPromise().then(surveys => {
+            return this.surveys =
+                surveys.sort((s1, s2) => s1.name() < s2.name() ? -1 : 1);
+        });
+    }
 }