From 504b3249a34855910b4bfa88865ebb38508d3665 Mon Sep 17 00:00:00 2001 From: Tiffany Little Date: Wed, 17 Nov 2021 12:41:50 -0500 Subject: [PATCH] LP1929749 ACQ Load MARC Order Records port Signed-off-by: Tiffany Little --- .../app/staff/acq/picklist/acq-picklist.module.ts | 20 + .../src/app/staff/acq/picklist/routing.module.ts | 19 + .../app/staff/acq/picklist/upload.component.html | 300 +++++++++++ .../src/app/staff/acq/picklist/upload.component.ts | 580 +++++++++++++++++++++ .../src/app/staff/acq/picklist/upload.service.ts | 241 +++++++++ .../src/eg2/src/app/staff/acq/routing.module.ts | 4 + Open-ILS/src/eg2/src/app/staff/nav.component.html | 8 +- Open-ILS/src/sql/Pg/950.data.seed-values.sql | 10 + .../XXXX.data.picklist_uploader_templates.sql | 15 + Open-ILS/src/templates/staff/navbar.tt2 | 2 +- 10 files changed, 1194 insertions(+), 5 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/acq/picklist/acq-picklist.module.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/acq/picklist/routing.module.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.service.ts create mode 100644 Open-ILS/src/sql/Pg/upgrade/XXXX.data.picklist_uploader_templates.sql diff --git a/Open-ILS/src/eg2/src/app/staff/acq/picklist/acq-picklist.module.ts b/Open-ILS/src/eg2/src/app/staff/acq/picklist/acq-picklist.module.ts new file mode 100644 index 0000000000..4f8f4e101a --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/acq/picklist/acq-picklist.module.ts @@ -0,0 +1,20 @@ +import {NgModule} from '@angular/core'; +import {StaffCommonModule} from '@eg/staff/common.module'; +import {CatalogCommonModule} from '@eg/share/catalog/catalog-common.module'; +import {PicklistRoutingModule} from './routing.module'; +import {HttpClientModule} from '@angular/common/http'; +import {UploadComponent} from './upload.component'; + +@NgModule({ + declarations: [ + UploadComponent + ], + imports: [ + StaffCommonModule, + CatalogCommonModule, + PicklistRoutingModule, + HttpClientModule + ] +}) + +export class AcqPicklistModule {} \ No newline at end of file diff --git a/Open-ILS/src/eg2/src/app/staff/acq/picklist/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/acq/picklist/routing.module.ts new file mode 100644 index 0000000000..da8dea2c39 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/acq/picklist/routing.module.ts @@ -0,0 +1,19 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {UploadComponent} from './upload.component'; +import {VandelayService} from '@eg/staff/cat/vandelay/vandelay.service'; +import {PicklistUploadService} from './upload.service' + + +const routes: Routes = [{ + path: 'upload', + component: UploadComponent +}]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [VandelayService, PicklistUploadService] +}) + +export class PicklistRoutingModule {} \ No newline at end of file diff --git a/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.html b/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.html new file mode 100644 index 0000000000..567b8fc1f8 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.html @@ -0,0 +1,300 @@ + + + + + + + + + +
+
+
+ +
+
+ + +
+
+ + + +
+
+ +

Purchase Order

+
+
+ +
+ +
+ + +
+ +
+ +
+
+ +
+
+
+
+ +
+
+ + +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ + +
+
+ + +

Upload Settings

+ +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+
+
+ +
+
+ + +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +

This Upload

+
+
+ +
+
+ + +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ + Importing {{importSelection().recordIds.length}} Record(s) + + Importing Queue {{importSelection().queue.name()}} +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ + +
+

+
+ +
+
+

+
+
+
+
+ +
+
Queue
+ + +
diff --git a/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.ts b/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.ts new file mode 100644 index 0000000000..86b691874e --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.component.ts @@ -0,0 +1,580 @@ +import {Component, OnInit, AfterViewInit, Input, + ViewChild, OnDestroy} from '@angular/core'; +import {Subject} from 'rxjs'; +import {tap} from 'rxjs/operators'; +import {IdlObject} from '@eg/core/idl.service'; +import {NetService} from '@eg/core/net.service'; +import {EventService} from '@eg/core/event.service'; +import {OrgService} from '@eg/core/org.service'; +import {AuthService} from '@eg/core/auth.service'; +import {ToastService} from '@eg/share/toast/toast.service'; +import {ComboboxComponent, + ComboboxEntry} from '@eg/share/combobox/combobox.component'; +import {VandelayImportSelection, + VANDELAY_UPLOAD_PATH} from '@eg/staff/cat/vandelay/vandelay.service'; +import {HttpClient, HttpRequest, HttpEventType} from '@angular/common/http'; +import {HttpResponse, HttpErrorResponse} from '@angular/common/http'; +import {ProgressInlineComponent} from '@eg/share/dialog/progress-inline.component'; +import {AlertDialogComponent} from '@eg/share/dialog/alert.component'; +import {ServerStoreService} from '@eg/core/server-store.service'; +import {PicklistUploadService} from './upload.service'; +import {OrgSelectComponent} from '@eg/share/org-select/org-select.component' + + +const TEMPLATE_SETTING_NAME = 'eg.acq.picklist.upload.templates'; + +const TEMPLATE_ATTRS = [ + 'createPurchaseOrder', + 'activatePurchaseOrder', + 'selectedProvider', + 'orderingAgency', + 'selectedFiscalYear', + 'loadItems', + 'selectedBibSource', + 'selectedMatchSet', + 'mergeOnExact', + 'importNonMatching', + 'mergeOnBestMatch', + 'mergeOnSingleMatch', + 'selectedMergeProfile', + 'selectedFallThruMergeProfile', + 'minQualityRatio' +]; + +const ORG_SETTINGS = [ + 'acq.upload.default.activate_po', + 'acq.upload.default.create_po', + 'acq.upload.default.provider', + 'acq.upload.default.vandelay.import_non_matching', + 'acq.upload.default.vandelay.load_item_for_imported', + 'acq.upload.default.vandelay.low_quality_fall_thru_profile', + 'acq.upload.default.vandelay.match_set', + 'acq.upload.default.vandelay.merge_on_best', + 'acq.upload.default.vandelay.merge_on_exact', + 'acq.upload.default.vandelay.merge_on_single', + 'acq.upload.default.vandelay.merge_profile', + 'acq.upload.default.vandelay.quality_ratio' +]; + + +@Component({ + templateUrl: './upload.component.html' +}) +export class UploadComponent implements OnInit, AfterViewInit, OnDestroy { + + settings: Object = {}; + recordType: string; + selectedQueue: ComboboxEntry; + + + activeSelectionListId: number; + activeQueueId: number; + orderingAgency: number; + selectedFiscalYear: number; + selectedSelectionList: ComboboxEntry; + selectedBibSource: number; + selectedProvider: number; + selectedMatchSet: number; + importDefId: number; + selectedMergeProfile: number; + selectedFallThruMergeProfile: number; + selectedFile: File; + newPO: number; + + defaultMatchSet: string; + + createPurchaseOrder: boolean; + activatePurchaseOrder: boolean; + loadItems: boolean; + + importNonMatching: boolean; + mergeOnExact: boolean; + mergeOnSingleMatch: boolean; + mergeOnBestMatch: boolean; + minQualityRatio: number; + + isUploading: boolean; + uploadProcessing: boolean; + uploadComplete: boolean; + + // Generated by the server + sessionKey: string; + + selectedTemplate: string; + formTemplates: {[name: string]: any}; + newTemplateName: string; + + @ViewChild('fileSelector', { static: false }) private fileSelector; + @ViewChild('uploadProgress', { static: true }) + private uploadProgress: ProgressInlineComponent; + + @ViewChild('formTemplateSelector', { static: true }) + private formTemplateSelector: ComboboxComponent; + @ViewChild('bibSourceSelector', { static: true }) + private bibSourceSelector: ComboboxComponent; + @ViewChild('providerSelector', {static: true}) + private providerSelector: ComboboxComponent; + @ViewChild('fiscalYearSelector', { static: true }) + private fiscalYearSelector: ComboboxComponent; + @ViewChild('selectionListSelector', { static: true }) + private selectionListSelector: ComboboxComponent; + @ViewChild('matchSetSelector', { static: true }) + private matchSetSelector: ComboboxComponent; + @ViewChild('mergeProfileSelector', { static: true }) + private mergeProfileSelector: ComboboxComponent; + @ViewChild('fallThruMergeProfileSelector', { static: true }) + private fallThruMergeProfileSelector: ComboboxComponent; + @ViewChild('dupeQueueAlert', { static: true }) + private dupeQueueAlert: AlertDialogComponent; + + constructor( + private http: HttpClient, + private toast: ToastService, + private evt: EventService, + private net: NetService, + private auth: AuthService, + private org: OrgService, + private store: ServerStoreService, + private vlagent: PicklistUploadService + ) { + this.applyDefaults(); + this.applySettings(); + } + + applySettings(): Promise { + return this.store.getItemBatch(ORG_SETTINGS) + .then(settings => { + this.createPurchaseOrder = settings['acq.upload.default.create_po']; + this.activatePurchaseOrder = settings['acq.upload.default.activate_po']; + this.selectedProvider = Number(settings['acq.upload.default.provider']); + this.importNonMatching = settings['acq.upload.default.vandelay.import_non_matching']; + this.loadItems = settings['acq.upload.default.vandelay.load_item_for_imported']; + this.selectedFallThruMergeProfile = Number(settings['acq.upload.default.vandelay.low_quality_fall_thru_profile']); + this.selectedMatchSet = Number(settings['acq.upload.default.vandelay.match_set']); + this.mergeOnBestMatch = settings['acq.upload.default.vandelay.merge_on_best']; + this.mergeOnExact = settings['acq.upload.default.vandelay.merge_on_exact']; + this.mergeOnSingleMatch = settings['acq.upload.default.vandelay.merge_on_single']; + this.selectedMergeProfile = Number(settings['acq.upload.default.vandelay.merge_profile']); + this.minQualityRatio = Number(settings['acq.upload.default.vandelay.quality_ratio']); + }); + } + applyDefaults() { + this.minQualityRatio = 0; + this.recordType = 'bib'; + this.formTemplates = {}; + if (this.vlagent.importSelection) { + + if (!this.vlagent.importSelection.queue) { + // Incomplete import selection, clear it. + this.vlagent.importSelection = null; + return; + } + + const queue = this.vlagent.importSelection.queue; + this.selectedMatchSet = queue.match_set(); + + } + } + + ngOnInit() {} + + ngAfterViewInit() { + this.loadStartupData(); + } + + ngOnDestroy() { + this.clearSelection(); + } + + importSelection(): VandelayImportSelection { + return this.vlagent.importSelection; + } + + loadStartupData(): Promise { + + + const promises = [ + this.vlagent.getMergeProfiles(), + this.vlagent.getAllQueues('bib'), + this.vlagent.getMatchSets('bib'), + this.vlagent.getBibSources(), + this.vlagent.getFiscalYears(), + this.vlagent.getProvidersList(), + this.vlagent.getSelectionLists(), + this.vlagent.getItemImportDefs(), + this.org.settings(['vandelay.default_match_set']).then( + s => this.defaultMatchSet = s['vandelay.default_match_set']), + this.loadTemplates() + ]; + + return Promise.all(promises); + } + + + orgOnChange(org: IdlObject) { + this.orderingAgency = org.id() + } + + loadTemplates() { + this.store.getItem(TEMPLATE_SETTING_NAME).then( + templates => { + this.formTemplates = templates || {}; + + Object.keys(this.formTemplates).forEach(name => { + if (this.formTemplates[name].default) { + this.selectedTemplate = name; + } + }); + } + ); + } + + formatTemplateEntries(): ComboboxEntry[] { + const entries = []; + + Object.keys(this.formTemplates || {}).forEach( + name => entries.push({id: name, label: name})); + + return entries; + } + + formatEntries(etype: string): ComboboxEntry[] { + const rtype = this.recordType; + let list; + + switch (etype) { + case 'bibSources': + return (this.vlagent.bibSources || []).map( + s => { + return {id: s.id(), label: s.source()}; + }); + + case 'providersList': + return (this.vlagent.providersList || []).map( + p => { + return {id: p.id(), label: p.code()}; + }); + + case 'fiscalYears': + return (this.vlagent.fiscalYears || []).map( + fy => { + return {id: fy.id(), label: fy.year()}; + }); + break; + + case 'selectionLists': + list = this.vlagent.selectionLists; + break; + + case 'activeQueues': + list = (this.vlagent.allQueues[rtype] || []); + break; + + case 'matchSets': + list = this.vlagent.matchSets['bib']; + break; + + + case 'importItemDefs': + list = this.vlagent.importItemAttrDefs; + break; + + case 'mergeProfiles': + list = this.vlagent.mergeProfiles; + break; + } + + return (list || []).map(item => { + return {id: item.id(), label: item.name()}; + }); + } + + selectEntry($event: ComboboxEntry, etype: string) { + const id = $event ? $event.id : null; + + switch (etype) { + case 'recordType': + this.recordType = id; + break; + + case 'providersList': + this.selectedProvider = id; + break; + + case 'bibSources': + this.selectedBibSource = id; + break; + + case 'fiscalYears': + this.selectedFiscalYear = id; + break; + + case 'selectionLists': + this.selectedSelectionList = id; + break; + + case 'matchSets': + this.selectedMatchSet = id; + break; + + + case 'mergeProfiles': + this.selectedMergeProfile = id; + break; + + case 'FallThruMergeProfile': + this.selectedFallThruMergeProfile = id; + break; + } + } + + fileSelected($event) { + this.selectedFile = $event.target.files[0]; + } + + hasNeededData(): boolean { + return this.selectedQueue && + Boolean(this.selectedFile) && + Boolean(this.selectedFiscalYear) && + Boolean(this.selectedProvider) && + Boolean(this.orderingAgency); + } + + + upload() { + this.sessionKey = null; + this.isUploading = true; + this.uploadComplete = false; + this.resetProgressBars(); + + this.resolveSelectionList(), + this.resolveQueue() + .then( + queueId => { + this.activeQueueId = queueId; + return this.uploadFile(); + }, + err => Promise.reject('queue create failed') + ).then( + ok => this.processUpload(), + err => Promise.reject('process spool failed') + ).then( + ok => { + this.isUploading = false; + this.uploadComplete = true; + }, + err => { + console.log('file upload failed: ', err); + this.isUploading = false; + this.resetProgressBars(); + + } + ); + } + + resetProgressBars() { + this.uploadProgress.update({value: 0, max: 1}); + } + + resolveQueue(): Promise { + + if (this.selectedQueue.freetext) { + return this.vlagent.createQueue( + this.selectedQueue.label, + this.recordType, + this.importDefId, + this.selectedMatchSet, + ).then( + id => id, + err => { + const evt = this.evt.parse(err); + if (evt) { + if (evt.textcode.match(/QUEUE_EXISTS/)) { + this.dupeQueueAlert.open(); + } else { + alert(evt); // server error + } + } + + return Promise.reject('Queue Create Failed'); + } + ); + } else { + return Promise.resolve(this.selectedQueue.id); + } + } + + resolveSelectionList(): Promise { + if (!this.selectedSelectionList) { + return Promise.resolve() + } + if (this.selectedSelectionList.id) { + this.activeSelectionListId = this.selectedSelectionList.id + } + if (this.selectedSelectionList.freetext) { + + return this.vlagent.createSelectionList( + this.selectedSelectionList.label, + this.orderingAgency + ).then( + value => this.activeSelectionListId = value + ); + } + return Promise.resolve(this.activeSelectionListId); + } + + uploadFile(): Promise { + + if (this.vlagent.importSelection) { + return Promise.resolve(); + } + + const formData: FormData = new FormData(); + + formData.append('ses', this.auth.token()); + formData.append('marc_upload', + this.selectedFile, this.selectedFile.name); + + if (this.selectedBibSource) { + formData.append('bib_source', '' + this.selectedBibSource); + } + + const req = new HttpRequest('POST', VANDELAY_UPLOAD_PATH, formData, + {reportProgress: true, responseType: 'text'}); + + return this.http.request(req).pipe(tap( + evt => { + if (evt.type === HttpEventType.UploadProgress) { + this.uploadProgress.update( + {value: evt.loaded, max: evt.total}); + + } else if (evt instanceof HttpResponse) { + this.sessionKey = evt.body as string; + console.log( + 'vlagent file uploaded OK with key ' + this.sessionKey); + } + }, + + (err: HttpErrorResponse) => { + console.error(err); + this.toast.danger(err.error); + } + )).toPromise(); + } + + processUpload(): Promise { + + this.uploadProcessing = true; + + if (this.vlagent.importSelection) { + return Promise.resolve(); + } + + let spoolType = this.recordType; + + const vandelayOptions = { + import_no_match: this.importNonMatching, + auto_overlay_exact: this.mergeOnExact, + auto_overlay_best_match: this.mergeOnBestMatch, + auto_overlay_1match: this.mergeOnSingleMatch, + merge_profile: this.selectedMergeProfile, + fall_through_merge_profile: this.selectedFallThruMergeProfile, + match_quality_ratio: this.minQualityRatio, + bib_source: this.selectedBibSource, + create_assets: this.loadItems, + queue_name: this.selectedQueue.label + } + + const args = { + + provider: this.selectedProvider, + ordering_agency: this.orderingAgency, + create_po: this.createPurchaseOrder, + activate_po: this.activatePurchaseOrder, + fiscal_year: this.selectedFiscalYear, + picklist: this.activeSelectionListId, + vandelay: vandelayOptions + } + + + const method = `open-ils.acq.process_upload_records`; + + return new Promise((resolve, reject) => { + this.net.request( + 'open-ils.acq', method, + this.auth.token(), this.sessionKey, args + ).subscribe( + progress => { + const resp = this.evt.parse(progress); + console.log(progress); + if (resp) { console.error(resp); return reject();} + if (progress.complete) { + this.uploadProcessing = false; + this.uploadComplete = true; + } + if (progress.purchase_order) {this.newPO = progress.purchase_order.id();} + } + ); + }); + } + + clearSelection() { + this.vlagent.importSelection = null; + this.activeSelectionListId = null; + } + + + saveTemplate() { + + const template = {}; + TEMPLATE_ATTRS.forEach(key => template[key] = this[key]); + + console.debug('Saving import profile', template); + + this.formTemplates[this.selectedTemplate] = template; + return this.store.setItem(TEMPLATE_SETTING_NAME, this.formTemplates); + } + + markTemplateDefault() { + + Object.keys(this.formTemplates).forEach( + name => delete this.formTemplates.default + ); + + this.formTemplates[this.selectedTemplate].default = true; + + return this.store.setItem(TEMPLATE_SETTING_NAME, this.formTemplates); + } + + templateSelectorChange(entry: ComboboxEntry) { + + if (!entry) { + this.selectedTemplate = ''; + return; + } + + this.selectedTemplate = entry.label; // label == name + + if (entry.freetext) { + return; + } + + const template = this.formTemplates[entry.id]; + + TEMPLATE_ATTRS.forEach(key => this[key] = template[key]); + + this.bibSourceSelector.applyEntryId(this.selectedBibSource); + this.matchSetSelector.applyEntryId(this.selectedMatchSet); + this.providerSelector.applyEntryId(this.selectedProvider); + this.fiscalYearSelector.applyEntryId(this.selectedFiscalYear); + this.mergeProfileSelector.applyEntryId(this.selectedMergeProfile); + this.fallThruMergeProfileSelector.applyEntryId(this.selectedFallThruMergeProfile); + } + + deleteTemplate() { + delete this.formTemplates[this.selectedTemplate]; + this.formTemplateSelector.selected = null; + return this.store.setItem(TEMPLATE_SETTING_NAME, this.formTemplates); + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.service.ts b/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.service.ts new file mode 100644 index 0000000000..0cf0718f36 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/acq/picklist/upload.service.ts @@ -0,0 +1,241 @@ +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {tap, map} from 'rxjs/operators'; +import {HttpClient} from '@angular/common/http'; +import {saveAs} from 'file-saver'; +import {IdlService, IdlObject} from '@eg/core/idl.service'; +import {OrgService} from '@eg/core/org.service'; +import {NetService} from '@eg/core/net.service'; +import {AuthService} from '@eg/core/auth.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {PermService} from '@eg/core/perm.service'; +import {EventService} from '@eg/core/event.service'; +import {ProgressDialogComponent} from '@eg/share/dialog/progress.component'; +import {VandelayImportSelection} from '@eg/staff/cat/vandelay/vandelay.service' + + +@Injectable() +export class PicklistUploadService { + + allQueues: {[qtype: string]: IdlObject[]}; + attrDefs: {[atype: string]: IdlObject[]}; + bibSources: IdlObject[]; + matchSets: {[stype: string]: IdlObject[]}; + importItemAttrDefs: IdlObject[]; + mergeProfiles: IdlObject[]; + providersList: IdlObject[]; + fiscalYears: IdlObject[]; + selectionLists: IdlObject[]; + queueType: string; + recordType: string; + + + importSelection: VandelayImportSelection; + + constructor( + private http: HttpClient, + private idl: IdlService, + private org: OrgService, + private evt: EventService, + private net: NetService, + private auth: AuthService, + private pcrud: PcrudService, + private perm: PermService + ) { + this.attrDefs = {}; + this.allQueues = {}; + this.matchSets = {}; + this.importSelection = null; + this.queueType = 'acq'; + this.recordType = 'bib'; + } + + getAttrDefs(dtype: string): Promise { + if (this.attrDefs[dtype]) { + return Promise.resolve(this.attrDefs[dtype]); + } + const cls = (dtype === 'bib') ? 'vqbrad' : 'vqarad'; + const orderBy = {}; + orderBy[cls] = 'id'; + return this.pcrud.retrieveAll(cls, + {order_by: orderBy}, {atomic: true}).toPromise() + .then(list => { + this.attrDefs[dtype] = list; + return list; + }); + } + + getMergeProfiles(): Promise { + if (this.mergeProfiles) { + return Promise.resolve(this.mergeProfiles); + } + + const owners = this.org.ancestors(this.auth.user().ws_ou(), true); + return this.pcrud.search('vmp', + {owner: owners}, {order_by: {vmp: ['name']}}, {atomic: true}) + .toPromise().then(profiles => { + this.mergeProfiles = profiles; + return profiles; + }); + } + + getProvidersList(): Promise { + if (this.providersList) { + return Promise.resolve(this.providersList); + } + + const owners = this.org.ancestors(this.auth.user().ws_ou(), true); + return this.pcrud.search('acqpro', + {owner: owners}, {order_by: {acqpro: ['code']}}, {atomic: true}) + .toPromise().then(providers => { + this.providersList = providers; + return providers; + }); + } + + getSelectionLists(): Promise { + if (this.selectionLists) { + return Promise.resolve(this.selectionLists); + } + + const owners = this.auth.user().id(); + return this.pcrud.search('acqpl', + {owner: owners}, {order_by: {acqpl: ['name']}}, {atomic: true}) + .toPromise().then(lists => { + this.selectionLists = lists; + return lists; + }); + } + + getAllQueues(qtype: string): Promise { + if (this.allQueues[qtype]) { + return Promise.resolve(this.allQueues[qtype]); + } else { + this.allQueues[qtype] = []; + } + + return this.net.request( + 'open-ils.vandelay', + `open-ils.vandelay.bib_queue.owner.retrieve`, + this.auth.token() + ).pipe(tap( + queue => this.allQueues[qtype].push(queue) + )).toPromise().then(() => this.allQueues[qtype]); + } + + getBibSources(): Promise { + if (this.bibSources) { + return Promise.resolve(this.bibSources); + } + + return this.pcrud.retrieveAll('cbs', + {order_by: {cbs: 'id'}}, + {atomic: true} + ).toPromise().then(sources => { + this.bibSources = sources; + return sources; + }); + } + + getFiscalYears(): Promise { + if (this.fiscalYears) { + return Promise.resolve(this.fiscalYears); + } + + return this.pcrud.retrieveAll('acqfy', + {order_by: {acqfy: 'year'}}, + {atomic: true} + ).toPromise().then(years => { + this.fiscalYears = years; + return years; + }); + } + + getItemImportDefs(): Promise { + if (this.importItemAttrDefs) { + return Promise.resolve(this.importItemAttrDefs); + } + + const owners = this.org.ancestors(this.auth.user().ws_ou(), true); + return this.pcrud.search('viiad', {owner: owners}, {}, {atomic: true}) + .toPromise().then(defs => { + this.importItemAttrDefs = defs; + return defs; + }); + } + + getMatchSets(mtype: string): Promise { + + const mstype = 'biblio' + + if (this.matchSets[mtype]) { + return Promise.resolve(this.matchSets[mtype]); + } else { + this.matchSets[mtype] = []; + } + + const owners = this.org.ancestors(this.auth.user().ws_ou(), true); + + return this.pcrud.search('vms', + {owner: owners, mtype: mstype}, {}, {atomic: true}) + .toPromise().then(sets => { + this.matchSets[mtype] = sets; + return sets; + }); + } + + + createQueue( + queueName: string, + queueType: string, + importDefId: number, + matchSet: number): Promise { + + const method = `open-ils.vandelay.bib_queue.create`; + queueType = 'acq'; + + + return new Promise((resolve, reject) => { + this.net.request( + 'open-ils.vandelay', method, + this.auth.token(), queueName, null, queueType, + matchSet, importDefId + ).subscribe(queue => { + const e = this.evt.parse(queue); + if (e) { + reject(e); + } else { + this.allQueues['bib'].push(queue); + resolve(queue.id()); + } + }); + }); + } + + createSelectionList( + picklistName: string, + picklistOrg: number + ): Promise { + + const newpicklist = this.idl.create('acqpl'); + newpicklist.owner(this.auth.user().id()); + newpicklist.name(picklistName); + newpicklist.org_unit(picklistOrg) + + return new Promise((resolve, reject) => { + this.net.request( + 'open-ils.acq', 'open-ils.acq.picklist.create', + this.auth.token(), newpicklist + ).subscribe((picklist) => { + if (this.evt.parse(picklist)) { + console.error(picklist); + } else { + console.log(picklist); + resolve(picklist); + } + }); + }); + } + +} + diff --git a/Open-ILS/src/eg2/src/app/staff/acq/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/acq/routing.module.ts index 8cde15192f..5c118cec59 100644 --- a/Open-ILS/src/eg2/src/app/staff/acq/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/acq/routing.module.ts @@ -9,6 +9,10 @@ const routes: Routes = [ { path: 'provider', loadChildren: () => import('./provider/acq-provider.module').then(m => m.AcqProviderModule) + }, + { path: 'picklist', + loadChildren: () => + import('./picklist/acq-picklist.module').then(m => m.AcqPicklistModule) } ]; diff --git a/Open-ILS/src/eg2/src/app/staff/nav.component.html b/Open-ILS/src/eg2/src/app/staff/nav.component.html index 6040ccf465..4fe326325a 100644 --- a/Open-ILS/src/eg2/src/app/staff/nav.component.html +++ b/Open-ILS/src/eg2/src/app/staff/nav.component.html @@ -283,10 +283,10 @@ - - Load MARC Order Records - + routerLink="/staff/acq/picklist/upload"> + + Load MARC Order Records + diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index bc00d09506..44d7af372c 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -22081,3 +22081,13 @@ VALUES (2,'usrname'), (3,'fullname') ; + +INSERT INTO config.workstation_setting_type (name, grp, datatype, label) +VALUES ( + 'eg.acq.picklist.upload.templates', 'acq', 'object', + oils_i18n_gettext( + 'eg.acq.picklist.upload.templates', + 'Picklist Upload Form Templates', + 'cwst', 'label' + ) +); diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.picklist_uploader_templates.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.picklist_uploader_templates.sql new file mode 100644 index 0000000000..7b1e98d417 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.picklist_uploader_templates.sql @@ -0,0 +1,15 @@ +BEGIN; + +SELECT evergreen.upgrade_deps_block_check('TODO', :eg_version); + +INSERT INTO config.workstation_setting_type (name, grp, datatype, label) +VALUES ( + 'eg.acq.picklist.upload.templates','acq','object', + oils_i18n_gettext( + 'eg.acq.picklist.upload.templates', + 'Acq Picklist Uploader Templates', + 'cwst','label' + ) +); + +COMMIT; \ No newline at end of file diff --git a/Open-ILS/src/templates/staff/navbar.tt2 b/Open-ILS/src/templates/staff/navbar.tt2 index 2047c4d1b0..9d75b62624 100644 --- a/Open-ILS/src/templates/staff/navbar.tt2 +++ b/Open-ILS/src/templates/staff/navbar.tt2 @@ -414,7 +414,7 @@
  • - + [% l('Load MARC Order Records') %] -- 2.11.0