basic EDI account management
authorGalen Charlton <gmc@equinoxinitiative.org>
Wed, 1 Apr 2020 22:02:25 +0000 (18:02 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Wed, 1 Apr 2020 22:04:34 +0000 (18:04 -0400)
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider.component.html
Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider.component.ts
Open-ILS/src/eg2/src/app/staff/acq/provider/acq-provider.module.ts
Open-ILS/src/eg2/src/app/staff/acq/provider/provider-edi-accounts.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/acq/provider/provider-edi-accounts.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/acq/provider/provider-record.service.ts
Open-ILS/src/eg2/src/app/staff/acq/provider/summary-pane.component.ts

index 6493d24..ec691f6 100644 (file)
@@ -59,7 +59,9 @@
         </ng-template>
       </ngb-tab>
       <ngb-tab title="EDI" i18n-title id="edi_accounts" [disabled]="!id">
-        <ng-template ngbTabContent>PROVIDER EDI TAB</ng-template>
+        <ng-template ngbTabContent>
+          <eg-provider-edi-accounts (desireSummarize)="onDesireSummarize($event, true)"></eg-provider-edi-accounts>
+        </ng-template>
       </ngb-tab>
       <ngb-tab title="Invoices" i18n-title id="invoices" [disabled]="!id">
         <ng-template ngbTabContent>
index bd009de..6441229 100644 (file)
@@ -31,7 +31,7 @@ export class AcqProviderComponent implements OnInit, AfterViewInit {
 
     onTabChange: ($event: NgbTabChangeEvent) => void;
 
-    onDesireSummarize: ($event: number) => void;
+    onDesireSummarize: ($event: number, updateSummaryOnly?: boolean) => void;
     onSummaryToggled: ($event: boolean) => void;
 
     constructor(
@@ -81,12 +81,15 @@ export class AcqProviderComponent implements OnInit, AfterViewInit {
             }
         };
 
-        this.onDesireSummarize = ($event) => {
+        this.onDesireSummarize = ($event, updateSummaryOnly = false) => {
             // $event is a provider ID
             this.providerSummaryPane.update($event);
             if (this.providerDetails) {
                 this.providerDetails.refresh();
             }
+            if (updateSummaryOnly) {
+                return;
+            }
             this.id = $event;
             this.providerRecord.fetch(this.id);
             this.showSearchForm = false;
index fe1a25c..cbfd46d 100644 (file)
@@ -11,6 +11,7 @@ import {ProviderContactsComponent} from './provider-contacts.component';
 import {ProviderContactAddressesComponent} from './provider-contact-addresses.component';
 import {ProviderHoldingsComponent} from './provider-holdings.component';
 import {ProviderAttributesComponent} from './provider-attributes.component';
+import {ProviderEdiAccountsComponent} from './provider-edi-accounts.component';
 import {ProviderInvoicesComponent} from './provider-invoices.component';
 import {ProviderPurchaseOrdersComponent} from './provider-purchase-orders.component';
 import {OrgFamilySelectModule} from '@eg/share/org-family-select/org-family-select.module';
@@ -29,6 +30,7 @@ import {ProviderRecordService} from './provider-record.service';
     ProviderContactAddressesComponent,
     ProviderHoldingsComponent,
     ProviderAttributesComponent,
+    ProviderEdiAccountsComponent,
     ProviderInvoicesComponent,
     ProviderPurchaseOrdersComponent,
     AcqProviderSummaryPaneComponent
diff --git a/Open-ILS/src/eg2/src/app/staff/acq/provider/provider-edi-accounts.component.html b/Open-ILS/src/eg2/src/app/staff/acq/provider/provider-edi-accounts.component.html
new file mode 100644 (file)
index 0000000..34e5a31
--- /dev/null
@@ -0,0 +1,39 @@
+<eg-string #createString i18n-text text="New EDI Account Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New EDI Account"></eg-string>
+<eg-string #successString i18n-text text="EDI Account Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="EDI Account Update Failed"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of EDI Account failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of EDI Account succeeded"></eg-string>
+<eg-string #setAsDefaultSuccessString i18n-text text="Successfully set EDI default account"></eg-string>
+<eg-string #setAsDefaultFailedtring i18n-text text="Failed to set EDI default account"></eg-string>
+
+<eg-confirm-dialog #confirmSetAsDefault
+  i18n-dialogTitle i18n-dialogBody
+  dialogTitle="Confirm Setting Default EDI Account"
+  dialogBody="Set {{selected ? selected.label() : ''}} as the default EDI account for {{provider ? provider.name() : ''}}?">
+</eg-confirm-dialog>
+
+<eg-grid #acqProviderEdiAccountsGrid
+  persistKey="acq.provider.edi_accounts.grid"
+  idlClass="acqedi" [dataSource]="gridSource"
+  [sortable]="true"
+  hideFields="provider"
+  [cellTextGenerator]="cellTextGenerator">
+  <eg-grid-toolbar-button 
+    label="New EDI Account" i18n-label (onClick)="createNew()">
+  </eg-grid-toolbar-button>
+  <eg-grid-toolbar-action label="Edit Selected" i18n-label (onClick)="editSelected($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="Set as Default" i18n-label (onClick)="setAsDefault($event)" [disableOnRows]="notOneSelectedRow">
+  </eg-grid-toolbar-action>
+</eg-grid>
+
+<eg-fm-record-editor #editDialog
+  idlClass="acqedi"
+  readonlyFields="id,provider"
+  hiddenFields="provider,last_activity"
+  fieldOrder="id,label,host,username,password,account,owner,path,in_dir,vendacct,vendcode,attr_set,use_attrs">
+</eg-fm-record-editor>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/acq/provider/provider-edi-accounts.component.ts b/Open-ILS/src/eg2/src/app/staff/acq/provider/provider-edi-accounts.component.ts
new file mode 100644 (file)
index 0000000..365fadd
--- /dev/null
@@ -0,0 +1,213 @@
+import {Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild} from '@angular/core';
+import {empty, throwError, Observable, from} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {Pager} from '@eg/share/util/pager';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridDataSource, GridCellTextGenerator} from '@eg/share/grid/grid';
+import {ProviderRecord, ProviderRecordService} from './provider-record.service';
+import {AcqProviderSearchFormComponent} from './acq-provider-search-form.component';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+
+@Component({
+  selector: 'eg-provider-edi-accounts',
+  templateUrl: 'provider-edi-accounts.component.html',
+})
+export class ProviderEdiAccountsComponent implements OnInit, AfterViewInit {
+
+    edi_accounts: any[] = [];
+
+    gridSource: GridDataSource;
+    @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+    @ViewChild('acqProviderEdiAccountsGrid', { static: true }) providerEdiAccountsGrid: GridComponent;
+    @ViewChild('confirmSetAsDefault', { static: true }) confirmSetAsDefault: ConfirmDialogComponent;
+    @ViewChild('successString', { static: true }) successString: StringComponent;
+    @ViewChild('createString', { static: false }) createString: StringComponent;
+    @ViewChild('createErrString', { static: false }) createErrString: StringComponent;
+    @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
+    @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;
+    @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
+    @ViewChild('setAsDefaultSuccessString', { static: true }) setAsDefaultSuccessString: StringComponent;
+    @ViewChild('setAsDefaultFailedString', { static: true }) setAsDefaultFailedString: StringComponent;
+
+    cellTextGenerator: GridCellTextGenerator;
+    provider: IdlObject;
+    selected: IdlObject;
+
+    canCreate: boolean;
+    canDelete: boolean;
+    notOneSelectedRow: (rows: IdlObject[]) => boolean;
+    deleteSelected: (rows: IdlObject[]) => void;
+
+    permissions: {[name: string]: boolean};
+
+    // Size of create/edito dialog.  Uses large by default.
+    @Input() dialogSize: 'sm' | 'lg' = 'lg';
+    @Output('desireSummarize') summarize: EventEmitter<number> = new EventEmitter<number>();
+
+    constructor(
+        private router: Router,
+        private route: ActivatedRoute,
+        private net: NetService,
+        private idl: IdlService,
+        private auth: AuthService,
+        private providerRecord: ProviderRecordService,
+        private toast: ToastService) {
+    }
+
+    ngOnInit() {
+        this.gridSource = this.getDataSource()
+        this.cellTextGenerator = {};
+        this.notOneSelectedRow = (rows: IdlObject[]) => (rows.length !== 1);
+        this.deleteSelected = (idlThings: IdlObject[]) => {
+            idlThings.forEach(idlThing => idlThing.isdeleted(true));
+            this.providerRecord.batchUpdate(idlThings).subscribe(
+                val => {
+                    console.debug('deleted: ' + val);
+                    this.deleteSuccessString.current()
+                        .then(str => this.toast.success(str));
+                },
+                err => {
+                    this.deleteFailedString.current()
+                        .then(str => this.toast.danger(str));
+                },
+                ()  => {
+                    this.providerRecord.refreshCurrent().then(
+                        () => {
+                            this.providerEdiAccountsGrid.reload();
+                        }
+                    );
+                }
+            );
+        };
+        this.providerEdiAccountsGrid.onRowActivate.subscribe(
+            (idlThing: IdlObject) => this.showEditDialog(idlThing)
+        );
+    }
+
+    ngAfterViewInit() {
+        console.log('this.providerRecord',this.providerRecord);
+    }
+
+    getDataSource(): GridDataSource {
+        const gridSource = new GridDataSource();
+
+        gridSource.getRows = (pager: Pager, sort: any[]) => {
+            this.provider = this.providerRecord.current();
+            if (!this.provider) {
+                return empty();
+            }
+            let edi_accounts = this.provider.edi_accounts()
+
+            if (sort.length > 0) {
+                edi_accounts = edi_accounts.sort((a, b) => {
+                    for (let i = 0; i < sort.length; i++) {
+                        let lt = -1;
+                        let sfield = sort[i].name;
+                        if (sort[i].dir.substring(0,1).toLowerCase() === 'd') {
+                            lt *= -1;
+                        }
+                        if (a[sfield]() < b[sfield]()) { return lt }
+                        if (a[sfield]() > b[sfield]()) { return lt * -1 }
+                    }
+                    return 0;
+                });
+
+            }
+
+            return from(edi_accounts.slice(pager.offset, pager.offset + pager.limit - 1));
+        };
+        return gridSource;
+    }
+
+    showEditDialog(providerEdiAccount: IdlObject): Promise<any> {
+        this.editDialog.mode = 'update';
+        this.editDialog.recordId = providerEdiAccount['id']();
+        return new Promise((resolve, reject) => {
+            this.editDialog.open({size: this.dialogSize}).subscribe(
+                result => {
+                    this.successString.current()
+                        .then(str => this.toast.success(str));
+                    this.providerRecord.refreshCurrent().then(
+                        () => this.providerEdiAccountsGrid.reload()
+                    );
+                    resolve(result);
+                },
+                error => {
+                    this.updateFailedString.current()
+                        .then(str => this.toast.danger(str));
+                    reject(error);
+                }
+            );
+        });
+    }
+
+    editSelected(providerEdiAccountFields: IdlObject[]) {
+        // Edit each IDL thing one at a time
+        const editOneThing = (providerEdiAccount: IdlObject) => {
+            if (!providerEdiAccount) { return; }
+
+            this.showEditDialog(providerEdiAccount).then(
+                () => editOneThing(providerEdiAccountFields.shift()));
+        };
+
+        editOneThing(providerEdiAccountFields.shift());
+    }
+
+    setAsDefault(providerEdiAccountFields: IdlObject[]) {
+        this.selected = providerEdiAccountFields[0];
+        this.confirmSetAsDefault.open().subscribe(confirmed => {
+            if (!confirmed) { return; }
+            this.providerRecord.refreshCurrent().then(() => {
+                this.provider.edi_default(providerEdiAccountFields[0].id());
+                this.provider.ischanged(true);
+                this.providerRecord.batchUpdate(this.provider).subscribe(
+                    val => {
+                        this.setAsDefaultSuccessString.current()
+                            .then(str => this.toast.success(str));
+                    },
+                    err => {
+                        this.setAsDefaultFailedString.current()
+                            .then(str => this.toast.danger(str));
+                    },
+                    () => {
+                        this.providerRecord.refreshCurrent(),
+                        this.summarize.emit(this.provider.id());
+                    }
+                );
+            });
+        });
+    }
+
+    createNew() {
+        this.editDialog.mode = 'create';
+        const edi_account = this.idl.create('acqedi');
+        edi_account.provider(this.provider.id());
+        edi_account.owner(this.auth.user().ws_ou())
+        edi_account.use_attrs(true);
+        this.editDialog.record = edi_account;
+        this.editDialog.recordId = null;
+        this.editDialog.open({size: this.dialogSize}).subscribe(
+            ok => {
+                this.createString.current()
+                    .then(str => this.toast.success(str));
+                this.providerRecord.refreshCurrent().then(
+                    () => this.providerEdiAccountsGrid.reload()
+                );
+            },
+            rejection => {
+                if (!rejection.dismissed) {
+                    this.createErrString.current()
+                        .then(str => this.toast.danger(str));
+                }
+            }
+        );
+    }
+
+}
index bf97196..be2b5e6 100644 (file)
@@ -39,11 +39,16 @@ export class ProviderRecordService {
         return this.pcrud.search('acqpro', { id: id },
             {
                 flesh: 3,
-                flesh_fields: { acqpro:  ['attributes','holdings_subfields', 'contacts', 'addresses', 'provider_notes'],
+                flesh_fields: { acqpro:  [
+                                           'attributes', 'holdings_subfields', 'contacts',
+                                           'addresses', 'provider_notes',
+                                           'edi_accounts',
+                                         ],
                                 acqpa:   ['provider'],
                                 acqpc:   ['provider','addresses'],
                                 acqphsm: ['provider'],
                                 acqlipad:['provider'],
+                                acqedi:  ['attr_set'],
                               }
             },
             {}
@@ -58,6 +63,21 @@ export class ProviderRecordService {
         return this.currentProvider ? this.currentProvider.record : null;
     }
 
+    // FIXME: ultimately, this is needed because eg-fm-record-editor currently can't
+    //        handle records that have linked fields fleshed; if/when it could,
+    //        we could just flesh edi_default and be done
+    currentEdiDefault(): Promise<any> {
+        if (this.currentProviderId) {
+            if (this.currentProvider.record.edi_default()) {
+                return this.pcrud.retrieve('acqedi', this.currentProvider.record.edi_default()).toPromise();
+            } else {
+                return Promise.resolve(null);
+            }
+        } else {
+            return Promise.reject();
+        }
+    }
+
     fetch(id: number): Promise<any> {
         return new Promise((resolve, reject) => {
             this.getProviderRecord(id).subscribe(
index bc8b447..38de105 100644 (file)
@@ -141,7 +141,9 @@ export class AcqProviderSummaryPaneComponent implements OnInit, AfterViewInit {
                     this.provider_addresses = provider.addresses();
                     this.provider_san = provider.san();
                     if (provider.edi_default()) {
-                        this.provider_edi_default = provider.edi_default().label();
+                        this.prov.currentEdiDefault().then(acqedi => {
+                            this.provider_edi_default = acqedi.label();
+                        });
                     } else {
                         this.provider_edi_default = '';
                     }