From a4cf356f8bfa3832aaaeb0659fb99a7d5363af0c Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 17 Jun 2020 16:36:02 -0400 Subject: [PATCH] LPXXX Angular Volcopy Signed-off-by: Bill Erickson --- .../src/app/share/combobox/combobox.component.html | 1 + .../src/app/share/combobox/combobox.component.ts | 3 + .../item-location-select.component.html | 1 + .../item-location-select.component.ts | 4 + .../app/share/org-select/org-select.component.ts | 3 +- .../staff/cat/volcopy/copy-attrs.component.html | 266 +++++++++++++-------- .../app/staff/cat/volcopy/copy-attrs.component.ts | 175 ++++++++++---- .../src/app/staff/cat/volcopy/routing.module.ts | 11 +- .../app/staff/cat/volcopy/vol-edit.component.css | 5 +- .../app/staff/cat/volcopy/vol-edit.component.html | 14 +- .../app/staff/cat/volcopy/vol-edit.component.ts | 13 +- .../app/staff/cat/volcopy/volcopy.component.html | 69 +++--- .../src/app/staff/cat/volcopy/volcopy.component.ts | 94 ++++++-- .../src/app/staff/cat/volcopy/volcopy.service.ts | 122 +++++++++- .../src/eg2/src/app/staff/cat/volcopy/volcopy.ts | 15 +- .../app/staff/catalog/record/actions.component.ts | 3 +- .../share/holdings/copy-alerts-dialog.component.ts | 15 +- .../app/staff/share/holdings/holdings.service.ts | 3 +- Open-ILS/src/eg2/src/styles.css | 12 + 19 files changed, 610 insertions(+), 219 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html index c456f80043..88e636f74d 100644 --- a/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html +++ b/Open-ILS/src/eg2/src/app/share/combobox/combobox.component.html @@ -7,6 +7,7 @@
" i18n-text> + + + + + + + + + + -
+
+
Templates:
+
+ +
+
+ + + + +
+ + +
@@ -63,11 +90,11 @@
+ domId='location-input' [required]="true" permFilter="UPDATE_COPY"> - @@ -77,12 +104,13 @@
- @@ -92,12 +120,13 @@
- @@ -107,10 +136,10 @@
+ id="copy-number-input" [(ngModel)]="values['copy_number']"/> -

Circulation

- - - + + + + + (changesSaved)="applyCopyValue('circulate')">
- - - + + + + + (changesSaved)="applyCopyValue('holdable')">
- - - - - + +
- - - - - + + - - - - + + - - - - + +
- - - - - + +
- - - - - + +
- - - - - + +
-

Miscellaneous

+ + +
+ +
Add Item Alerts
+
+ +
+
- - - + + + + + (changesSaved)="applyCopyValue('deposit')">
- - - + + + @@ -329,12 +373,13 @@
- - - + + + @@ -342,45 +387,42 @@
- - - + + + + + (changesSaved)="applyCopyValue('opac_visible')">
- - - + + + + + (changesSaved)="applyCopyValue('ref')">
- - - + + + @@ -391,13 +433,15 @@ - - - - + +

Statistics

+ +
+ + + + + + + + +
+
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 ccfe284ff1..723638cbbf 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 @@ -4,6 +4,7 @@ import {tap} from 'rxjs/operators'; import {IdlObject, IdlService} from '@eg/core/idl.service'; import {EventService} from '@eg/core/event.service'; import {OrgService} from '@eg/core/org.service'; +import {StoreService} from '@eg/core/store.service'; import {NetService} from '@eg/core/net.service'; import {AuthService} from '@eg/core/auth.service'; import {PcrudService} from '@eg/core/pcrud.service'; @@ -12,10 +13,19 @@ import {VolCopyContext} from './volcopy'; import {VolCopyService} from './volcopy.service'; import {FormatService} from '@eg/core/format.service'; import {StringComponent} from '@eg/share/string/string.component'; +import {CopyAlertsDialogComponent + } from '@eg/staff/share/holdings/copy-alerts-dialog.component'; +import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component'; @Component({ selector: 'eg-copy-attrs', - templateUrl: 'copy-attrs.component.html' + templateUrl: 'copy-attrs.component.html', + + // Match the header of the batch attrs component + styles: [ + `.batch-header {background-color: #EBF4FA;}`, + `.template-row {background-color: #EBF4FA;}` + ] }) export class CopyAttrsComponent implements OnInit, AfterViewInit { @@ -25,10 +35,8 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { // Some values are scalar, some IdlObjects depending on copy fleshyness. values: {[field: string]: any} = {}; - ageProtectRules: IdlObject[] = []; - floatingGroups: IdlObject[] = []; - itemTypeMaps: IdlObject[] = []; - circModifiers: IdlObject[] = []; + // Map of stat ID to entry ID. + statCatValues: {[statId: number]: number} = {}; loanDurationLabelMap: {[level: number]: string} = {}; fineLevelLabelMap: {[level: number]: string} = {}; @@ -52,6 +60,12 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { @ViewChild('mintConditionNo', {static: false}) mintConditionNo: StringComponent; + @ViewChild('copyAlertsDialog', {static: false}) + private copyAlertsDialog: CopyAlertsDialogComponent; + + @ViewChild('copyTemplateCbox', {static: false}) + copyTemplateCbox: ComboboxComponent; + constructor( private router: Router, private route: ActivatedRoute, @@ -64,15 +78,18 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { private pcrud: PcrudService, private holdings: HoldingsService, private volcopy: VolCopyService, - private format: FormatService + private format: FormatService, + private store: StoreService ) { } ngOnInit() { - this.load(); } ngAfterViewInit() { + const tmpl = this.store.getLocalItem('cat.copy.last_template'); + if (tmpl) { this.copyTemplateCbox.selectedId = tmpl; } + this.loanDurationLabelMap[1] = this.loanDurationShort.text; this.loanDurationLabelMap[2] = this.loanDurationNormal.text; this.loanDurationLabelMap[3] = this.loanDurationLong.text; @@ -82,45 +99,40 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { this.fineLevelLabelMap[3] = this.fineLevelHigh.text; } - load() { - - this.pcrud.retrieveAll('crahp') - .pipe(tap(rule => this.ageProtectRules.push(rule))).toPromise() - .then(_ => { - - this.ageProtectRules = this.ageProtectRules.sort( - (a, b) => a.name() < b.name() ? -1 : 1); - - }).then(_ => { - - return this.pcrud.retrieveAll('cfg') - .pipe(tap(rule => this.floatingGroups.push(rule))).toPromise() - - }).then(_ => { - - this.floatingGroups = this.floatingGroups.sort( - (a, b) => a.name() < b.name() ? -1 : 1); - - }).then(_ => { - - return this.pcrud.retrieveAll('ccm') - .pipe(tap(rule => this.circModifiers.push(rule))).toPromise() - - }).then(_ => { + statCats(): IdlObject[] { + return this.volcopy.statCats; + } - this.circModifiers = this.circModifiers.sort( - (a, b) => a.name() < b.name() ? -1 : 1); - }).then(_ => { + orgSn(orgId: number): string { + return orgId ? this.org.get(orgId).shortname() : ''; + } - return this.pcrud.retrieveAll('citm') - .pipe(tap(itemType => this.itemTypeMaps.push(itemType))).toPromise() + statCatCounts(catId: number): {[value: string]: number} { + catId = Number(catId); + const counts = {}; - }).then(_ => { + this.context.copyList().forEach(copy => { + const entry = copy.stat_cat_entries() + .filter(e => e.stat_cat() === catId)[0]; + + let value = ''; + if (entry) { + if (this.volcopy.statCatEntryMap[entry.id()]) { + value = this.volcopy.statCatEntryMap[entry.id()].value(); + } else { + // Map to a remote stat cat. Ignore. + return; + } + } - this.itemTypeMaps = this.itemTypeMaps.sort( - (a, b) => a.value() < b.value() ? -1 : 1); + if (counts[value] === undefined) { + counts[value] = 0; + } + counts[value]++; }); + + return counts; } itemAttrCounts(field: string): {[value: string]: number} { @@ -131,7 +143,7 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { if (counts[value] === undefined) { counts[value] = 0; - }; + } counts[value]++; }); @@ -147,11 +159,11 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { ' : ' + copy.call_number().label(); } - let value = copy[field](); + const value = copy[field](); if (!value && value !== 0) { return ''; } - switch(field) { + switch (field) { case 'status': return this.volcopy.copyStatuses[value].name(); @@ -174,12 +186,12 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { return this.org.get(value).shortname(); case 'age_protect': - const rule = this.ageProtectRules.filter( + const rule = this.volcopy.ageProtectRules.filter( r => r.id() === Number(value))[0]; return rule ? rule.name() : ''; case 'floating': - const grp = this.floatingGroups.filter( + const grp = this.volcopy.floatingGroups.filter( g => g.id() === Number(value))[0]; return grp ? grp.name() : ''; @@ -190,12 +202,12 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { return this.fineLevelLabelMap[value]; case 'circ_as_type': - const map = this.itemTypeMaps.filter( + const map = this.volcopy.itemTypeMaps.filter( m => m.code() === value)[0]; return map ? map.value() : ''; case 'circ_modifier': - const mod = this.circModifiers.filter( + const mod = this.volcopy.circModifiers.filter( m => m.code() === value)[0]; return mod ? mod.name() : ''; @@ -228,8 +240,77 @@ export class CopyAttrsComponent implements OnInit, AfterViewInit { owningLibChanged() { // TODO + // copies.ischanged(true); console.log('OWNING LIB ', this.values['owning_lib']); } + + + // Create or modify a stat cat entry for each copy that does not + // already match the new value. + statCatChanged(catId: number) { + catId = Number(catId); + + const entryId = this.statCatValues[catId]; + this.context.copyList().forEach(copy => { + + let entry = copy.stat_cat_entries() + .filter(e => e.stat_cat() === catId)[0]; + + if (entry) { + if (entry.id() === entryId) { + // Requested mapping already exists. + return; + } + } else { + + // Copy has no entry for this stat cat yet. + entry = this.idl.create('asce'); + entry.stat_cat(catId); + copy.stat_cat_entries().push(entry); + } + + entry.id(entryId); + entry.value(this.volcopy.statCatEntryMap[entryId].value()); + + copy.ischanged(true); + }); + } + + openCopyAlerts() { + this.copyAlertsDialog.inPlaceMode = true; + this.copyAlertsDialog.mode = 'create'; + this.copyAlertsDialog.open({size: 'lg'}).subscribe( + newAlert => { + if (newAlert) { + this.context.copyList().forEach(copy => { + const a = this.idl.clone(newAlert); + a.isnew(true); + a.copy(copy.id()); + if (!copy.copy_alerts()) { copy.copy_alerts([]); } + copy.copy_alerts().push(a); + copy.ischanged(true); + }); + } + } + ); + } + + applyTemplate() { + const entry = this.copyTemplateCbox.selected; + if (!entry) { return; } + + this.store.setLocalItem('cat.copy.last_template', entry.id); + + const template = this.volcopy.templates[entry.id]; + + Object.keys(template).forEach(field => { + const value = template[field]; + + if (value === null || value === undefined) { return; } + + this.applyCopyValue(field, value); + }); + } } diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts index fb4f44d7c5..c5d3f67d2d 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/routing.module.ts @@ -3,16 +3,7 @@ import {RouterModule, Routes} from '@angular/router'; import {VolCopyComponent} from './volcopy.component'; const routes: Routes = [{ - path: 'edit/item/:copy_id', - component: VolCopyComponent - }, { - path: 'edit/callnumber/:vol_id', - component: VolCopyComponent - }, { - path: 'edit/record/:record_id', - component: VolCopyComponent - }, { - path: 'edit/session/:session', + path: ':tab/:target/:target_id', component: VolCopyComponent /* }, { diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css index fabadae7d7..de37f46a49 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.css @@ -6,11 +6,10 @@ input[type="number"] { .vol-row { background-color: rgba(0,0,0,.03); - border-top: 1px solid rgba(0,0,0,.125); - border-bottom: 1px solid rgba(0,0,0,.125); + border-top: 1px solid #d9edf7; + border-bottom: 1px solid #d9edf7; } - .clear-button { border: none; background-color: rgba(0, 0, 0, 0.0); diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html index a29c3a7090..9edeb21944 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.html @@ -12,7 +12,7 @@ dialogBody="Delete {{deleteCopyCount}} Item(s)?"> -
+
@@ -260,3 +260,15 @@ + +
+ +
+ + + +
+ diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.ts index a7975ab976..4dc657440b 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/vol-edit.component.ts @@ -105,6 +105,8 @@ export class VolEditComponent implements OnInit { ); }); + if (Object.keys(this.bibParts).length === 0) { return; } + this.pcrud.search('bmp', {record: Object.keys(this.bibParts), deleted: 'f'}) .subscribe( @@ -162,6 +164,13 @@ export class VolEditComponent implements OnInit { } } + + addVol(org: IdlObject) { + if (!org) { return; } + const orgNode = this.context.findOrCreateOrgNode(org.id()); + this.createVols(orgNode, 1); + } + existingVolCount(orgNode: HoldingsTreeNode): number { return orgNode.children.filter(volNode => !volNode.target.isnew()).length; } @@ -372,7 +381,7 @@ export class VolEditComponent implements OnInit { } barcodeChanged(copy: IdlObject, barcode: string) { - copy.barcode(barcode); + // note: copy.barcode(barcode) applied via ngModel copy.ischanged(true); copy._dupe_barcode = false; @@ -405,7 +414,6 @@ export class VolEditComponent implements OnInit { } deleteOneCopy(copyNode: HoldingsTreeNode) { - const targetCopy = copyNode.target; const orgNodes = this.context.orgNodes(); @@ -455,6 +463,7 @@ export class VolEditComponent implements OnInit { } deleteOneVol(volNode: HoldingsTreeNode) { + let deleteVolIdx = null; const targetVol = volNode.target; diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html index 156540f789..ff1473f3ed 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.html @@ -1,11 +1,5 @@ -
-
- -
-
-
Holdings Editor Session Expired @@ -15,29 +9,50 @@ - -
- -
- - -
-
- -
- -
-
-
-
- - +
+ + + + + +
+
+
+ +
+
+ +
+
+
+ + + +
+
+
+ +
+
+
+ +
+
+ +
+ + +
+
+
+
+ + +
-
+ diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts index 5932ffb81a..b73777986a 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.component.ts @@ -12,11 +12,13 @@ import {VolCopyContext} from './volcopy'; import {ProgressInlineComponent} from '@eg/share/dialog/progress-inline.component'; import {AnonCacheService} from '@eg/share/util/anon-cache.service'; import {VolCopyService} from './volcopy.service'; +import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap'; const COPY_FLESH = { flesh: 1, flesh_fields: { - acp: ['call_number', 'location', 'parts', 'creator', 'editor'] + acp: ['call_number', 'location', 'parts', + 'creator', 'editor', 'stat_cat_entries'] } }; @@ -47,6 +49,10 @@ export class VolCopyComponent implements OnInit { loading = true; sessionExpired = false; + tab = 'holdings'; // holdings | attrs | config + target: string; // item | callnumber | record | session + targetId: string; // id value or session string + @ViewChild('loadingProgress', {static: false}) loadingProgress: ProgressInlineComponent; @@ -66,35 +72,54 @@ export class VolCopyComponent implements OnInit { ) { } ngOnInit() { - this.context = new VolCopyContext(); - this.context.org = this.org; // inject; - this.route.paramMap.subscribe( (params: ParamMap) => this.negotiateRoute(params)); } negotiateRoute(params: ParamMap) { - this.context.recordId = +params.get('record_id') || null; - this.context.volId = +params.get('vol_id') || null; - this.context.copyId = +params.get('copy_id') || null; - this.context.session = params.get('session') || null; - this.load(); + this.tab = params.get('tab') || 'holdings'; + this.target = params.get('target'); + this.targetId = params.get('target_id'); + + if (this.volcopy.currentContext) { + // Avoid clobbering the context on route change. + this.context = this.volcopy.currentContext; + } else { + this.context = new VolCopyContext(); + this.context.org = this.org; // inject; + } + + switch (this.target) { + case 'item': + this.context.copyId = +this.targetId; + break; + case 'callnumber': + this.context.volId = +this.targetId; + break; + case 'record': + this.context.recordId = +this.targetId; + break; + case 'session': + this.context.session = this.targetId; + break; + } + + if (!this.volcopy.currentContext) { + // Avoid refetching the data during route changes. + this.volcopy.currentContext = this.context; + this.load(); + } } load(copyIds?: number[]) { - this.sessionExpired = false; this.loading = true; this.context.reset(); - this.volcopy.fetchDefaults() - .then(_ => this.volcopy.fetchCopyStats()) + this.volcopy.load() .then(_ => this.fetchHoldings(copyIds)) .then(_ => this.volcopy.applyVolLabels( this.context.volNodes().map(n => n.target))) - .then(_ => this.holdings.fetchCallNumberClasses()) - .then(_ => this.holdings.fetchCallNumberPrefixes()) - .then(_ => this.holdings.fetchCallNumberSuffixes()) .then(_ => this.context.sortHoldings()) .then(_ => this.context.setRecordId()) .then(_ => this.loading = false); @@ -110,20 +135,36 @@ export class VolCopyComponent implements OnInit { this.context.sessionType = 'mixed'; return this.fetchSession(this.context.session); - } else if (this.context.recordId) { - this.context.sessionType = 'record'; - return this.fetchRecords(this.context.recordId); + } else if (this.context.copyId) { + this.context.sessionType = 'copy'; + return this.fetchCopies(this.context.copyId); } else if (this.context.volId) { this.context.sessionType = 'vol'; return this.fetchVols(this.context.volId); - } else if (this.context.copyId) { - this.context.sessionType = 'copy'; - return this.fetchCopies(this.context.copyId); + } else if (this.context.recordId) { + this.context.sessionType = 'record'; + return this.fetchRecords(this.context.recordId); } } + // Changing a tab in the UI means changing the route. + // Changing the route ultimately results in changing the tab. + beforeTabChange(evt: NgbTabChangeEvent) { + evt.preventDefault(); + this.tab = evt.nextId; + this.routeToTab(); + } + + routeToTab() { + const url = + `/staff/cat/volcopy/${this.tab}/${this.target}/${this.targetId}`; + + // Retain search parameters + this.router.navigate([url], {queryParamsHandling: 'merge'}); + } + fetchSession(session: string): Promise { return this.cache.getItem(session, 'edit-these-copies') @@ -317,6 +358,17 @@ export class VolCopyComponent implements OnInit { volumes.push(vol); }); + // De-flesh before posting + volumes.forEach(vol => { + vol.copies().forEach(copy => { + ['editor', 'creator', 'location'].forEach(field => { + if (typeof copy[field]() === 'object') { + copy[field](copy[field]().id()); + } + }); + }); + }); + if (volumes.length > 0) { this.saveApi(volumes); } else { 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 abaa8b8460..07e009e531 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 @@ -10,6 +10,8 @@ import {AuthService} from '@eg/core/auth.service'; import {VolCopyContext} from './volcopy'; import {HoldingsService, CallNumData} from '@eg/staff/share/holdings/holdings.service'; import {ServerStoreService} from '@eg/core/server-store.service'; +import {StoreService} from '@eg/core/store.service'; +import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component'; /* Managing volcopy data */ @@ -21,6 +23,19 @@ export class VolCopyService { defaultValues: any = null; copyStatuses: {[id: number]: IdlObject} = null; + // Track this here so it can survive route changes. + currentContext: VolCopyContext; + + ageProtectRules: IdlObject[] = []; + floatingGroups: IdlObject[] = []; + itemTypeMaps: IdlObject[] = []; + circModifiers: IdlObject[] = []; + statCats: IdlObject[] = []; + statCatEntryMap: {[id: number]: IdlObject} = {}; // entry id => entry + + templateNames: ComboboxEntry[] = []; + templates: any = {}; + constructor( private evt: EventService, private net: NetService, @@ -29,13 +44,112 @@ export class VolCopyService { private auth: AuthService, private pcrud: PcrudService, private holdings: HoldingsService, - private store: ServerStoreService + private store: StoreService, + private serverStore: ServerStoreService ) {} + + // Fetch the data that is always needed. + load(): Promise { + + if (this.itemTypeMaps.length > 0) { + return Promise.resolve(); + } + + return this.fetchDefaults() + .then(_ => this.holdings.fetchCallNumberClasses()) + .then(_ => this.holdings.fetchCallNumberPrefixes()) + .then(_ => this.holdings.fetchCallNumberSuffixes()) + .then(_ => this.fetchCopyStats()) + .then(_ => this.fetchTemplates()) + .then(_ => { + + return this.pcrud.retrieveAll('crahp') + .pipe(tap(rule => this.ageProtectRules.push(rule))).toPromise() + + }).then(_ => { + + this.ageProtectRules = this.ageProtectRules.sort( + (a, b) => a.name() < b.name() ? -1 : 1); + + }).then(_ => { + + return this.pcrud.retrieveAll('cfg') + .pipe(tap(rule => this.floatingGroups.push(rule))).toPromise(); + + }).then(_ => { + + this.floatingGroups = this.floatingGroups.sort( + (a, b) => a.name() < b.name() ? -1 : 1); + + }).then(_ => { + + return this.pcrud.retrieveAll('ccm') + .pipe(tap(rule => this.circModifiers.push(rule))).toPromise(); + + }).then(_ => { + + this.circModifiers = this.circModifiers.sort( + (a, b) => a.name() < b.name() ? -1 : 1); + + }).then(_ => { + + return this.pcrud.retrieveAll('citm') + .pipe(tap(itemType => this.itemTypeMaps.push(itemType))).toPromise(); + + }).then(_ => { + + this.itemTypeMaps = this.itemTypeMaps.sort( + (a, b) => a.value() < b.value() ? -1 : 1); + + }).then(_ => { + + return this.net.request('open-ils.circ', + 'open-ils.circ.stat_cat.asset.retrieve.all', + this.auth.token(), this.auth.user().ws_ou() + ).toPromise().then(stats => this.statCats = stats); + + }).then(_ => { + + // Sort most local to the front of the list. + this.statCats = this.statCats.sort((s1, s2) => { + const d1 = this.org.get(s1.owner()).ou_type().depth(); + const d2 = this.org.get(s2.owner()).ou_type().depth(); + + if (d1 > d2) { + return -1; + } else if (d1 < d2) { + return 1; + } else { + return s1.name() < s2.name() ? -1 : 1; + } + }); + + this.statCats.forEach(cat => { + cat.entries().forEach( + entry => this.statCatEntryMap[entry.id()] = entry); + }); + }); + } + + fetchTemplates(): Promise { + + // TODO: copy templates should be server settings + const tmpls = this.store.getLocalItem('cat.copy.templates'); + if (!tmpls) { return Promise.resolve(); } + + this.templates = tmpls; + this.templateNames = Object.keys(tmpls) + .sort((n1, n2) => n1 < n2 ? -1 : 1) + .map(name => ({id: name, label: name})); + + return Promise.resolve(); + } + fetchDefaults(): Promise { if (this.defaultValues) { return Promise.resolve(); } - return this.store.getItem('cat.copy.defaults').then( + return this.serverStore.getItem('cat.copy.defaults').then( defaults => { this.defaultValues = defaults || {}; } @@ -111,6 +225,7 @@ export class VolCopyService { copy.ref('f'); copy.mint_condition('t'); copy.parts([]); + copy.stat_cat_entries([]); return copy; } @@ -254,5 +369,8 @@ export class VolCopyService { return promise; } + + + } diff --git a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.ts b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.ts index 5d745fb2a0..d751c7eda5 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/volcopy/volcopy.ts @@ -170,11 +170,24 @@ export class VolCopyContext { o1.target.shortname() < o2.target.shortname() ? -1 : 1); } + // Changes pending and no unresolved issues. isSaveable(): boolean { const dupeBc = this.copyList().filter(c => c._dupe_barcode).length; if (dupeBc) { return false; } - return true; + const modified = (o: IdlObject): boolean => { + return o.isnew() || o.ischanged() || o.isdeleted(); + }; + + if (this.volNodes().filter(n => modified(n.target)).length > 0) { + return true; + } + + if (this.copyList().filter(c => modified(c)).length > 0) { + return true; + } + + return false; } } diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts index 52e89bee6b..687066fa2d 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/actions.component.ts @@ -98,7 +98,8 @@ export class RecordActionsComponent implements OnInit { // TODO: Support adding like call numbers by getting selected // call numbers from the holdings grid. addHoldings() { - this.holdings.spawnAddHoldingsUi(this.recId); + // -1 == create new call number + this.holdings.spawnAddHoldingsUi(this.recId, null, [{callnumber: -1}]); } } diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts index 439c5468c5..06951b8473 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/copy-alerts-dialog.component.ts @@ -41,6 +41,11 @@ export class CopyAlertsDialogComponent return this._mode; } + // If true, no attempt is made to save the new alerts to the + // database. It's assumed this takes place in the calling code. + // This is useful for creating alerts for new copies. + @Input() inPlaceMode = false; + // In 'create' mode, we may be adding notes to multiple copies. copies: IdlObject[]; // In 'manage' mode we only handle a single copy. @@ -78,7 +83,7 @@ export class CopyAlertsDialogComponent this.newAlert = this.idl.create('aca'); this.newAlert.create_staff(this.auth.user().id()); - if (this.copyIds.length === 0) { + if (this.copyIds.length === 0 && !this.inPlaceMode) { return throwError('copy ID required'); } @@ -117,6 +122,9 @@ export class CopyAlertsDialogComponent } getCopies(): Promise { + + if (this.inPlaceMode) { return Promise.resolve(); } + return this.pcrud.search('acp', {id: this.copyIds}, {}, {atomic: true}) .toPromise().then(copies => { this.copies = copies; @@ -149,6 +157,11 @@ export class CopyAlertsDialogComponent addNew() { if (!this.newAlert.alert_type()) { return; } + if (this.inPlaceMode) { + this.close(this.newAlert); + return; + } + const alerts: IdlObject[] = []; this.copies.forEach(c => { const a = this.idl.clone(this.newAlert); diff --git a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts index ccfd79bd81..f907b3bfbc 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/share/holdings/holdings.service.ts @@ -62,7 +62,8 @@ export class HoldingsService { return; } setTimeout(() => { - const url = `/eg2/staff/cat/volcopy/edit/session/${key}`; + const tab = hideVols ? 'attrs' : 'holdings'; + const url = `/eg2/staff/cat/volcopy/${tab}/session/${key}`; window.open(url, '_blank'); }); }); diff --git a/Open-ILS/src/eg2/src/styles.css b/Open-ILS/src/eg2/src/styles.css index d1144fd9fb..7780680d2a 100644 --- a/Open-ILS/src/eg2/src/styles.css +++ b/Open-ILS/src/eg2/src/styles.css @@ -225,6 +225,18 @@ body>.dropdown-menu {z-index: 2100;} color: black; } +/* Washed out version of the Bootstrap 'info' background. + * Useful for blocking out sections of a page/form without it + * being so intensely colorful */ +.bg-faint { + /*background-color: rgb(204, 229, 255, 0.3);*/ + + /* d9edf7 */ + /*background-color: rgb(217, 237, 247, 0.5);*/ + + background-color: rgba(0,0,0,.03); +} + /* Allow for larger XL dialogs */ @media (min-width: 1300px) { .modal-xl { max-width: 1200px; } } @media (min-width: 1600px) { .modal-xl { max-width: 1500px; } } -- 2.11.0