From e4d4ae3854122aa87a15325b7cc9dfce3aa0edd7 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 24 Jun 2020 15:23:27 -0400 Subject: [PATCH] LPXXX Angular Volcopy Signed-off-by: Bill Erickson --- .../staff/cat/volcopy/copy-attrs.component.html | 12 +++- .../app/staff/cat/volcopy/copy-attrs.component.ts | 76 ++++++++++++++++++---- .../src/app/staff/cat/volcopy/volcopy.service.ts | 36 ++++++++-- 3 files changed, 104 insertions(+), 20 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html index 2f5811b76d..5128eda2dc 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.html @@ -40,9 +40,19 @@
+ - + + diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.ts index 2eb0ef2153..dff203371c 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/copy-attrs.component.ts @@ -303,7 +303,7 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { this.store.setLocalItem('cat.copy.last_template', entry.id); - // TODO: handle owning_lib and statcats differently. + // TODO: handle owning_lib and statcats differently, location const template = this.volcopy.templates[entry.id]; @@ -312,12 +312,27 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { if (value === null || value === undefined) { return; } - this.applyCopyValue(field, value); + // In some cases, we may have to fetch the data since + // the local code assumes copy field is fleshed. + let promise = Promise.resolve(value); - // Indicate in the form these values have changed - this.batchAttrs - .filter(ba => ba.name === field) - .forEach(attr => attr.hasChanged = true); + // TODO: promises in loops are dangerous becuase they + // can lead to blasts of duplicate requests for non-local + // data. Consider an alternative approach. + + if (field === 'location') { + // May be a 'remote' location. Fetch as needed. + promise = this.volcopy.getLocation(value); + } + + promise.then(val => { + this.applyCopyValue(field, val); + + // Indicate in the form these values have changed + this.batchAttrs + .filter(ba => ba.name === field) + .forEach(attr => attr.hasChanged = true); + }); }); } @@ -330,6 +345,8 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { if (entry.freetext) { name = entry.label; + // freetext entries don't have an ID, but we may need one later. + entry.id = entry.label; template = {}; } else { name = entry.id; @@ -344,14 +361,17 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { const value = copy[comp.name](); if (value === null) { delete template[comp.name]; + } else { - template[comp.name] = value; + // some values are fleshed. + // this assumes fleshed objects have an 'id' value, + // which is true so far. + template[comp.name] = + typeof value === 'object' ? value.id() : value; } } }); - console.debug('Saving template', template); - this.volcopy.templates[name] = template; this.volcopy.saveTemplates(); } @@ -359,13 +379,33 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { exportTemplate($event) { if (this.fileExport.inProgress()) { return; } - const entry: ComboboxEntry = this.copyTemplateCbox.selected; - if (!entry) { return; } + this.fileExport.exportFile( + $event, JSON.stringify(this.volcopy.templates), 'text/json'); + } - const template = this.volcopy.templates[entry.id]; + importTemplate($event) { + const file: File = $event.target.files[0]; + if (!file) { return; } - this.fileExport.exportFile( - $event, JSON.stringify(template), 'text/json'); + const reader = new FileReader(); + + reader.addEventListener('load', () => { + + try { + const template = JSON.parse(reader.result as string); + const name = Object.keys(template)[0]; + this.volcopy.templates[name] = template[name]; + } catch (E) { + console.error('Invalid Item Attribute template', E); + return; + } + + this.volcopy.saveTemplates(); + // Adds the new one to the list and re-sorts the labels. + this.volcopy.fetchTemplates(); + }); + + reader.readAsText(file); } // Returns null when no export is in progress. @@ -373,6 +413,14 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { return this.fileExport.safeUrl; } + deleteTemplate() { + const entry: ComboboxEntry = this.copyTemplateCbox.selected; + if (!entry) { return; } + delete this.volcopy.templates[entry.id]; + this.volcopy.saveTemplates(); + this.copyTemplateCbox.selected = null; + } + displayAttr(field: string): boolean { return this.volcopy.defaults.hidden[field] !== true; } diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts index b411fd13a0..63081e381e 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.service.ts @@ -27,10 +27,15 @@ export class VolCopyService { autoId = -1; + localOrgs: number[]; defaults: VolCopyDefaults = null; defaultLocation: IdlObject; copyStatuses: {[id: number]: IdlObject} = null; + // This will be all 'local' copy locations plus any remote + // locations that we are required to interact with. + copyLocationMap: {[id: number]: IdlObject} = {}; + // Track this here so it can survive route changes. currentContext: VolCopyContext; @@ -65,9 +70,10 @@ export class VolCopyService { if (this.itemTypeMaps.length > 0) { return Promise.resolve(); } - const myOrgs = this.org.fullPath(this.auth.user().ws_ou(), true); + this.localOrgs = this.org.fullPath(this.auth.user().ws_ou(), true); return this.fetchDefaults() + .then(_ => this.getLocations()) .then(_ => this.holdings.fetchCallNumberClasses()) .then(cls => this.volClasses = cls) @@ -75,7 +81,7 @@ export class VolCopyService { return this.holdings.fetchCallNumberPrefixes().then(prefixes => this.volPrefixes = prefixes .filter(sfx => sfx.id() !== -1) - .filter(pfx => myOrgs.includes(pfx.owning_lib())) + .filter(pfx => this.localOrgs.includes(pfx.owning_lib())) ); }) @@ -83,7 +89,7 @@ export class VolCopyService { return this.holdings.fetchCallNumberSuffixes().then(suffixes => this.volSuffixes = suffixes .filter(sfx => sfx.id() !== -1) - .filter(sfx => myOrgs.includes(sfx.owning_lib())) + .filter(sfx => this.localOrgs.includes(sfx.owning_lib())) ); }) @@ -159,6 +165,26 @@ export class VolCopyService { }); } + getLocation(id: number): Promise { + if (this.copyLocationMap[id]) { + return Promise.resolve(this.copyLocationMap[id]); + } + + return this.pcrud.retrieve('acpl', id) + .pipe(tap(loc => this.copyLocationMap[loc.id()] = loc)) + .toPromise(); + } + + getLocations(): Promise { + this.localOrgs = this.org.fullPath(this.auth.user().ws_ou(), true); + + return this.pcrud.search('acpl', + {deleted: 'f', owning_lib: this.localOrgs}, + {order_by: {acpl: 'name'}} + ).pipe(tap(loc => this.copyLocationMap[loc.id()] = loc) + ).toPromise(); + } + fetchTemplates(): Promise { // TODO: copy templates should be server settings @@ -194,9 +220,9 @@ export class VolCopyService { // Use the first non-deleted copy location within org unit // range as the default. Typically "Stacks". - const myOrgs = this.org.fullPath(this.auth.user().ws_ou(), true); + this.localOrgs = this.org.fullPath(this.auth.user().ws_ou(), true); return this.pcrud.search('acpl', - {deleted: 'f', owning_lib: myOrgs}, + {deleted: 'f', owning_lib: this.localOrgs}, {order_by: {acpl: 'id'}, limit: 1} ).toPromise().then(loc => this.defaultLocation = loc); }); -- 2.11.0