--- /dev/null
+#acq-provider-search-form {
+ border-radius: 0px 0px 7px 7px;
+ background-color: rgb(247, 247, 247);
+ box-shadow: 1px 2px 3px -1px rgba(0, 0, 0, .2);
+}
--- /dev/null
+<div id="acq-provider-search-form" class="pl-3 pr-3 pt-3 pb-3 mb-3">
+<form>
+ <div class="row mb-1">
+ <div class="col-lg">
+ <input i18n-placeholder placeholder="Provider Name" [ngModelOptions]="{standalone: true}" [(ngModel)]="providerName" type="text" class="form-control" />
+ </div>
+ <div class="col-lg">
+ <input i18n-placeholder placeholder="Code" [ngModelOptions]="{standalone: true}" [(ngModel)]="providerCode" type="text" class="form-control" />
+ </div>
+ <div class="col-lg-4">
+ <eg-org-family-select i18n-labelText labelText="Owner"
+ [limitPerms]="['VIEW_PROVIDER','MANAGE_PROVIDER','ADMIN_PROVIDER']"
+ [ngModelOptions]="{standalone: true}" [(ngModel)]="providerOwners">
+ </eg-org-family-select>
+ </div>
+ <div class="col-lg-2 text-right">
+ <button class="btn btn-primary mr-1" (click)="submitSearch()" type="submit" i18n>Search</button>
+ <button class="btn btn-secondary" (click)="clearSearch()" type="button" i18n>Reset Form</button>
+ <button class="btn" [hidden]="collapsed" (click)="toggleCollapse()" type="submit" i18n><span class="material-icons">expand_less</span></button>
+ <button class="btn" [hidden]="!collapsed" (click)="toggleCollapse()" type="submit" i18n><span class="material-icons">expand_more</span></button>
+ </div>
+ </div>
+
+ <div class="row mb-1" [hidden]="collapsed">
+ <div class="col-lg">
+ <input i18n-placeholder placeholder="Contact Name" [ngModelOptions]="{standalone: true}" [(ngModel)]="contactName" type="text" class="form-control" />
+ </div>
+ <div class="col-lg">
+ <input i18n-placeholder placeholder="Provider Email" [ngModelOptions]="{standalone: true}" [(ngModel)]="providerEmail" type="text" class="form-control" />
+ </div>
+ <div class="col-lg">
+ <input i18n-placeholder placeholder="Provider Phone" [ngModelOptions]="{standalone: true}" [(ngModel)]="providerPhone" type="text" class="form-control" />
+ </div>
+ <div class="col-lg">
+ <eg-combobox i18n-placeholder placeholder="Currency"
+ idlClass="acqct" [asyncSupportsEmptyTermClick]="true"
+ (onChange)="providerCurrencyType = $event ? $event.id : null" [asyncSupportsEmptyTermClick]="true"></eg-combobox>
+ </div>
+ </div>
+
+ <div class="row mb-1" [hidden]="collapsed">
+ <div class="col-lg">
+ <input i18n-placeholder placeholder="SAN" [ngModelOptions]="{standalone: true}" [(ngModel)]="providerSAN" type="text" class="form-control" />
+ </div>
+ <div class="col-lg">
+ <!-- edi default account link -->
+ <eg-combobox i18n-placeholder placeholder="EDI Default"
+ idlClass="acqedi" [asyncSupportsEmptyTermClick]="true"
+ (onChange)="providerEDIDefault = $event ? $event.id : null"></eg-combobox>
+ </div>
+ <div class="col-lg">
+ <input i18n-placeholder placeholder="URL" [ngModelOptions]="{standalone: true}" [(ngModel)]="providerURL" type="text" class="form-control" />
+ </div>
+ <div class="col-lg form-group">
+ <label for="acqproIsActive" i18n>Active?</label>
+ <select class="form-control" id="acqproIsActive" [ngModelOptions]="{standalone: true}" [(ngModel)]="providerIsActive">
+ <option i18n value="active">Yes</option>
+ <option i18n value="inactive">No</option>
+ <option i18n value="any">Any</option>
+ </select>
+ </div>
+ </div>
+</form>
+</div>
--- /dev/null
+import {Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild} from '@angular/core';
+import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+import {Router, ActivatedRoute} from '@angular/router';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {AuthService} from '@eg/core/auth.service';
+import {AcqProviderSearchTerm, AcqProviderSearch} from './acq-provider-search.service';
+import {StoreService} from '@eg/core/store.service';
+import {OrgFamily} from '@eg/share/org-family-select/org-family-select.component';
+
+@Component({
+ selector: 'eg-acq-provider-search-form',
+ styleUrls: ['acq-provider-search-form.component.css'],
+ templateUrl: './acq-provider-search-form.component.html'
+})
+
+export class AcqProviderSearchFormComponent implements OnInit, AfterViewInit {
+
+ @Output() searchSubmitted = new EventEmitter<AcqProviderSearch>();
+
+ collapsed = false;
+
+ providerName = '';
+ providerCode = '';
+ providerOwners: OrgFamily;
+ contactName = '';
+ providerEmail = '';
+ providerPhone = '';
+ providerCurrencyType = '';
+ providerSAN = '';
+ providerEDIDefault = null;
+ providerURL = '';
+ providerIsActive = 'active';
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private pcrud: PcrudService,
+ private localStore: StoreService,
+ private idl: IdlService,
+ private toast: ToastService,
+ private auth: AuthService,
+ ) {}
+
+ ngOnInit() {
+ const self = this;
+ this.providerOwners = {primaryOrgId: this.auth.user().ws_ou(), includeDescendants: true};
+ this.collapsed = this.localStore.getLocalItem('eg.acq.provider.search.collapse_form') || false;
+ }
+
+ ngAfterViewInit() {}
+
+ clearSearch() {
+ this.providerName = '';
+ this.providerCode = '';
+ this.providerOwners = {primaryOrgId: this.auth.user().ws_ou(), includeDescendants: true};
+ this.contactName = '';
+ this.providerEmail = '';
+ this.providerPhone = '';
+ this.providerCurrencyType = '';
+ this.providerSAN = '';
+ this.providerEDIDefault = null;
+ this.providerURL = '';
+ this.providerIsActive = 'active';
+ }
+
+ submitSearch() {
+
+ const searchTerms: AcqProviderSearchTerm[] = [];
+ if (this.providerName) {
+ searchTerms.push({ classes: ['acqpro'], fields: ['name'], op: 'ilike', value: this.providerName });
+ }
+ if (this.providerCode) {
+ searchTerms.push({ classes: ['acqpro'], fields: ['code'], op: 'ilike', value: this.providerCode });
+ }
+ if (this.providerOwners) {
+ searchTerms.push({ classes: ['acqpro'], fields: ['owner'], op: 'in', value: this.providerOwners.orgIds });
+ }
+ if (this.contactName) {
+ searchTerms.push({ classes: ['acqpc'], fields: ['name'], op: 'ilike', value: this.contactName });
+ }
+ if (this.providerEmail) {
+ searchTerms.push({ classes: ['acqpro', 'acqpc'], fields: ['email', 'email'], op: 'ilike', value: this.providerEmail });
+ }
+ if (this.providerPhone) {
+ // this requires the flesh hash to contain: { ... join: { acqpc: { type: 'left' } } ... }
+ searchTerms.push({
+ classes: ['acqpc', 'acqpro', 'acqpro'],
+ fields: ['phone', 'phone', 'fax_phone'],
+ op: 'ilike',
+ value: this.providerPhone,
+ });
+ }
+ if (this.providerCurrencyType) {
+ searchTerms.push({ classes: ['acqpro'], fields: ['currency_type'], op: '=', value: this.providerCurrencyType });
+ }
+ if (this.providerSAN) {
+ searchTerms.push({ classes: ['acqpro'], fields: ['san'], op: 'ilike', value: this.providerSAN });
+ }
+ if (this.providerEDIDefault) {
+ searchTerms.push({ classes: ['acqpro'], fields: ['edi_default'], op: '=', value: this.providerEDIDefault });
+ }
+ if (this.providerURL) {
+ searchTerms.push({ classes: ['acqpro'], fields: ['url'], op: 'ilike', value: this.providerURL });
+ }
+ switch (this.providerIsActive) {
+ case 'active': {
+ searchTerms.push({ classes: ['acqpro'], fields: ['active'], op: '=', value: 't' });
+ break;
+ }
+ case 'inactive': {
+ searchTerms.push({ classes: ['acqpro'], fields: ['active'], op: '=', value: 'f' });
+ break;
+ }
+ }
+
+ // tossing setTimeout here to ensure that the
+ // grid data source is fully initialized
+ setTimeout(() => {
+ this.searchSubmitted.emit({
+ terms: searchTerms,
+ });
+ });
+ }
+
+ toggleCollapse() {
+ this.collapsed = ! this.collapsed;
+ this.localStore.setLocalItem('eg.acq.provider.search.collapse_form', this.collapsed);
+ }
+
+}
--- /dev/null
+import {Injectable} from '@angular/core';
+import {empty, throwError} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {AuthService} from '@eg/core/auth.service';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {Pager} from '@eg/share/util/pager';
+import {EventService} from '@eg/core/event.service';
+import {ProviderRecordService} from './provider-record.service';
+
+export interface AcqProviderSearchTerm {
+ classes: string[];
+ fields: string[];
+ op: string;
+ value: any;
+}
+
+export interface AcqProviderSearch {
+ terms: AcqProviderSearchTerm[];
+}
+
+@Injectable()
+export class AcqProviderSearchService {
+
+ _terms: AcqProviderSearchTerm[] = [];
+ firstRun = true;
+
+ constructor(
+ private evt: EventService,
+ private auth: AuthService,
+ private pcrud: PcrudService,
+ private providerRecord: ProviderRecordService
+ ) {
+ this.firstRun = true;
+ }
+
+ setSearch(search: AcqProviderSearch) {
+ this._terms = search.terms;
+ this.firstRun = false;
+ }
+
+ generateSearchJoins(): any {
+ const joinPart = new Object();
+ let class_list = new Array();
+
+ // get all the classes used
+ this._terms.forEach(term => { class_list = class_list.concat(term.classes); });
+
+ // filter out acqpro, empty names, and make unique
+ class_list = class_list.filter((x, i, a) => x && x !== 'acqpro' && a.indexOf(x) === i);
+
+ // build a join clause for use in the "opts" part of a pcrud query
+ class_list.forEach(cls => { joinPart[cls] = {type : 'left' }; });
+
+ if (Object.keys(joinPart).length === 0) { return null; }
+ return joinPart;
+ }
+
+ generateSearch(filters): any {
+ // base query to grab all providers
+ const base = { id: { '!=': null } };
+ const query: any = new Array();
+ query.push(base);
+
+ // handle supplied search terms
+ this._terms.forEach(term => {
+ if (term.value === '') {
+ return;
+ }
+
+ // not const because we may want an array
+ let query_obj = new Object();
+ const query_arr = new Array();
+
+ let op = term.op;
+ if (!op) { op = '='; } // just in case
+
+ let val = term.value;
+ if (op === 'ilike') {
+ val = '%' + val + '%';
+ }
+
+ let isOR = false;
+ term.fields.forEach( (field, ind) => {
+ const curr_cls = term.classes[ind];
+
+ // remove any OUs that the user doesn't have provider view
+ // permission for
+ if (curr_cls === 'acqpro' && field === 'owner' && op === 'in') {
+ val = val.filter(ou => {
+ return this.providerRecord.getViewOUs().includes(ou);
+ });
+ }
+
+ if (ind === 1) {
+ // we're OR'ing in other classes/fields
+ // and this is the first so restructure
+ const first_cls = term.classes[0];
+ isOR = true;
+ let tmp = new Object();
+ if (first_cls) {
+ tmp['+' + first_cls] = query_obj;
+ } else {
+ tmp = query_obj;
+ }
+
+ query_arr.push(tmp);
+ }
+
+ if (curr_cls) {
+ if (isOR) {
+ const tmp = new Object();
+ tmp['+' + curr_cls] = new Object();
+ tmp['+' + curr_cls][field] = new Object();
+ tmp['+' + curr_cls][field][op] = val;
+ query_arr.push(tmp);
+ } else {
+ query_obj['+' + curr_cls] = new Object();
+ query_obj['+' + curr_cls][field] = new Object();
+ query_obj['+' + curr_cls][field][op] = val;
+ }
+ } else {
+ if (isOR) {
+ const tmp = new Object();
+ tmp[field] = new Object();
+ tmp[field][op] = val;
+ query_arr.push(tmp);
+ } else {
+ query_obj[field] = new Object();
+ query_obj[field][op] = val;
+ }
+ }
+
+ });
+
+ if (isOR) { query_obj = {'-or': query_arr}; }
+ query.push(query_obj);
+ });
+
+ // handle grid filters
+ // note that date filters coming from the grid do not need
+ // to worry about __castdate because the grid filter supplies
+ // both the start and end times
+ const observables = [];
+ Object.keys(filters).forEach(filterField => {
+ filters[filterField].forEach(condition => {
+ query.push(condition);
+ });
+ });
+ return query;
+ }
+
+ getDataSource(): GridDataSource {
+ const gridSource = new GridDataSource();
+
+ // we'll sort by provder name by default
+ gridSource.sort = [{ name: 'name', dir: 'ASC' }];
+
+ gridSource.getRows = (pager: Pager, sort: any[]) => {
+
+ // don't do a search the very first time we
+ // get invoked, which is during initialization; we'll
+ // let components higher up the change decide whether
+ // to submit a search
+ if (this.firstRun) {
+ this.firstRun = false;
+ return empty();
+ }
+
+ const joins = this.generateSearchJoins();
+ const query = this.generateSearch(gridSource.filters);
+
+ const opts = {};
+ if (joins) { opts['join'] = joins; }
+ opts['offset'] = pager.offset;
+ opts['limit'] = pager.limit;
+ opts['au_by_id'] = true;
+
+ if (sort.length > 0) {
+ opts['order_by'] = [];
+ sort.forEach(sort_clause => {
+ opts['order_by'].push({
+ class: 'acqpro',
+ field: sort_clause.name,
+ direction: sort_clause.dir
+ });
+ });
+ }
+
+ return this.pcrud.search('acqpro',
+ query,
+ opts
+ ).pipe(
+ map(res => {
+ if (this.evt.parse(res)) {
+ throw throwError(res);
+ } else {
+ return res;
+ }
+ }),
+ );
+ };
+ return gridSource;
+ }
+}
--- /dev/null
+<eg-staff-banner bannerText="Providers" i18n-bannerText>
+</eg-staff-banner>
+<eg-string #createString i18n-text text="New Provider Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New Provider"></eg-string>
+
+<eg-confirm-dialog #leaveConfirm
+ i18n-dialogTitle i18n-dialogBody
+ dialogTitle="Unsaved Changes Warning"
+ dialogBody="There are unsaved changes. Are you sure you want to leave?">
+</eg-confirm-dialog>
+
+<div><div class="row">
+
+<div class="col">
+<div class="row mb-2">
+ <div class="col-lg">
+ <h3 i18n *ngIf="id && !showSearchForm">{{providerRecord.currentProvider?.record.name()}} ({{providerRecord.currentProvider?.record.code()}})</h3>
+ </div>
+ <div class="col-lg-auto">
+ <div class="btn-toolbar" role="toolbar">
+ <div class="button-grp mr-2">
+ <button class="btn btn-primary" [hidden]="showSearchForm" (click)="showSearchForm = !showSearchForm" i18n>Show Search Form</button>
+ <button class="btn btn-primary" [hidden]="!showSearchForm" (click)="showSearchForm = !showSearchForm" i18n>Hide Search Form</button>
+ </div>
+ <div class="button-grp mr-2">
+ <button class="btn btn-primary" (click)="createNew()" [disabled]="!providerRecord.checkIfCanAdminAtAll()" i18n>New Provider</button>
+ </div>
+ </div>
+ </div>
+</div>
+<div class="row mb-5" [hidden]="!showSearchForm">
+ <div class="col-lg-12">
+ <eg-provider-results #acqProviderResults (desireSummarize)="onDesireSummarize($event)" (summarizeSearchFormOpen)="onDesireSummarize($event, false, false)"></eg-provider-results>
+ </div>
+</div>
+
+<h3 i18n *ngIf="id && showSearchForm">{{providerRecord.currentProvider?.record.name()}} ({{providerRecord.currentProvider?.record.code()}})</h3>
+<div class="row">
+<div class="col-lg-auto">
+ <eg-acq-provider-summary-pane #acqSearchProviderSummary
+ (summaryToggled)="onSummaryToggled($event)" [providerId]="id">
+ </eg-acq-provider-summary-pane>
+</div>
+
+<div class="col">
+<div class="row" id="acq-provider-page" [hidden]="!id">
+ <div class="col-lg-12">
+ <ngb-tabset #acqProviderTabs [activeId]="activeTab" (tabChange)="onTabChange($event)">
+ <ngb-tab title="Provider" i18n-title id="details" [disabled]="!id">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <eg-provider-details #providerDetails (desireSummarize)="onDesireSummarize($event, true)"></eg-provider-details>
+ </ng-template>
+ </ngb-tab>
+ <ngb-tab title="Addresses" i18n-title id="addresses" [disabled]="!id || !this.providerRecord.currentProvider || !this.providerRecord.currentProvider.canAdmin">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <eg-provider-addresses></eg-provider-addresses>
+ </ng-template>
+ </ngb-tab>
+ <ngb-tab title="Contacts" i18n-title id="contacts" [disabled]="!id || !this.providerRecord.currentProvider || !this.providerRecord.currentProvider.canAdmin">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <eg-provider-contacts (desireSummarize)="onDesireSummarize($event, true)"></eg-provider-contacts>
+ </ng-template>
+ </ngb-tab>
+ <ngb-tab title="Attribute Definitions" i18n-title id="attributes" [disabled]="!id || !this.providerRecord.currentProvider || !this.providerRecord.currentProvider.canAdmin">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <eg-provider-attributes></eg-provider-attributes>
+ </ng-template>
+ </ngb-tab>
+ <ngb-tab title="Holdings Definitions" i18n-title id="holdings" [disabled]="!id || !this.providerRecord.currentProvider || !this.providerRecord.currentProvider.canAdmin">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <eg-provider-holdings #providerHoldings></eg-provider-holdings>
+ </ng-template>
+ </ngb-tab>
+ <ngb-tab title="EDI" i18n-title id="edi_accounts" [disabled]="!id || !this.providerRecord.currentProvider || !this.providerRecord.currentProvider.canAdmin">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <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>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <eg-provider-invoices></eg-provider-invoices>
+ </ng-template>
+ </ngb-tab>
+ <ngb-tab title="POs" i18n-title id="purchase_orders" [disabled]="!id">
+ <ng-template ngbTabContent>
+ <div class="row mt-3">
+ <div class="col-lg-12 text-right pb-1">
+ <button class="btn btn-secondary btn-sm" [disabled]="activeTab == defaultTabType"
+ (click)="setDefaultTab()" i18n>Set Default View</button>
+ </div>
+ </div>
+ <eg-provider-purchase-orders></eg-provider-purchase-orders>
+ </ng-template>
+ </ngb-tab>
+ </ngb-tabset>
+ </div>
+</div>
+</div>
+</div>
+</div>
+
+</div></div>
+
+<eg-fm-record-editor #createDialog
+ idlClass="acqpro"
+ fieldOrder="active,name,code,currency_type,default_claim_policy,default_copy_count,edi_default,owner,url,san,prepayment_required"
+ [remainOpenOnError]="true"
+ [fieldOptions]="{currency_type:{preloadLinkedValues:true},edi_default:{preloadLinkedValues:true},default_claim_policy:{preloadLinkedValues:true}}"
+ hiddenFields="id,email,phone,fax_phone,holding_tag,primary_contact">
+</eg-fm-record-editor>
--- /dev/null
+import {Component, OnInit, AfterViewInit, ViewChild, ChangeDetectorRef, OnDestroy} from '@angular/core';
+import {filter, takeUntil} from 'rxjs/operators';
+import {Subject, Observable, of} from 'rxjs';
+import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+import {Router, ActivatedRoute, ParamMap, RouterEvent, NavigationEnd} from '@angular/router';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AcqProviderSummaryPaneComponent} from './summary-pane.component';
+import {ProviderDetailsComponent} from './provider-details.component';
+import {ProviderHoldingsComponent} from './provider-holdings.component';
+import {ProviderResultsComponent} from './provider-results.component';
+import {ProviderRecordService} from './provider-record.service';
+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 {AuthService} from '@eg/core/auth.service';
+import {StoreService} from '@eg/core/store.service';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+
+@Component({
+ templateUrl: './acq-provider.component.html'
+})
+
+export class AcqProviderComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ activeTab = '';
+ showSearchForm = false;
+ id = null;
+ validTabTypes = ['details', 'addresses', 'contacts', 'attributes', 'holdings', 'edi_accounts', 'purchase_orders', 'invoices'];
+ defaultTabType = 'details';
+ @ViewChild('acqSearchProviderSummary', { static: true }) providerSummaryPane: AcqProviderSummaryPaneComponent;
+ @ViewChild('acqProviderResults', { static: true }) acqProviderResults: ProviderResultsComponent;
+ @ViewChild('providerDetails', { static: false }) providerDetails: ProviderDetailsComponent;
+ @ViewChild('providerHoldings', { static: false }) providerHoldings: ProviderHoldingsComponent;
+ @ViewChild('createDialog', { static: true }) createDialog: FmRecordEditorComponent;
+ @ViewChild('createString', { static: false }) createString: StringComponent;
+ @ViewChild('createErrString', { static: false }) createErrString: StringComponent;
+ @ViewChild('leaveConfirm', { static: true }) leaveConfirm: ConfirmDialogComponent;
+
+ onTabChange: ($event: NgbTabChangeEvent) => void;
+
+ onDesireSummarize: ($event: number, updateSummaryOnly?: boolean, hideSearchForm?: boolean) => void;
+ onSummaryToggled: ($event: boolean) => void;
+
+ previousUrl: string = null;
+ public destroyed = new Subject<any>();
+ _alreadyDeactivated = false;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private auth: AuthService,
+ private pcrud: PcrudService,
+ private idl: IdlService,
+ private providerRecord: ProviderRecordService,
+ private toast: ToastService,
+ private store: StoreService,
+ private changeDetector: ChangeDetectorRef
+ ) {
+ this.router.events.pipe(
+ filter((event: RouterEvent) => event instanceof NavigationEnd),
+ takeUntil(this.destroyed)
+ ).subscribe(routeEvent => {
+ if (routeEvent instanceof NavigationEnd) {
+ if (this.previousUrl != null &&
+ routeEvent.url === '/staff/acq/provider' &&
+ this.previousUrl === routeEvent.url) {
+ this.acqProviderResults.resetSearch();
+ }
+ this.previousUrl = routeEvent.url;
+ }
+ });
+ }
+
+ ngOnInit() {
+ const self = this;
+
+ const tabTypeParam = this.route.snapshot.paramMap.get('tab');
+ const idParam = this.route.snapshot.paramMap.get('id');
+
+ this.defaultTabType =
+ this.store.getLocalItem('eg.acq.provider.default_tab') || 'details';
+
+ if (idParam) {
+ this.showSearchForm = false;
+ this.id = idParam;
+ if (!tabTypeParam) {
+ this.activeTab = this.defaultTabType;
+ this.router.navigate(['/staff', 'acq', 'provider', this.id, this.activeTab]);
+ }
+ }
+
+ if (tabTypeParam) {
+ this.showSearchForm = false;
+ if (this.validTabTypes.includes(tabTypeParam)) {
+ this.activeTab = tabTypeParam;
+ } else {
+ this.activeTab = this.defaultTabType;
+ this.router.navigate(['/staff', 'acq', 'provider', this.id, this.activeTab]);
+ }
+ } else {
+ this.showSearchForm = true;
+ }
+
+ this.onTabChange = ($event) => {
+ $event.preventDefault();
+ this.canDeactivate().subscribe(canLeave => {
+ if (!canLeave) { return; }
+ this._alreadyDeactivated = true; // don't trigger again on the route change
+ if (this.validTabTypes.includes($event.nextId)) {
+ this.activeTab = $event.nextId;
+ const id = this.route.snapshot.paramMap.get('id');
+ this.router.navigate(['/staff', 'acq', 'provider', this.id, $event.nextId]);
+ }
+ });
+ };
+
+ this.onDesireSummarize = ($event, updateSummaryOnly = false, hideSearchForm = true) => {
+ this.id = $event;
+ this.providerRecord.fetch(this.id).then(() => {
+ // $event is a provider ID
+ this.providerSummaryPane.update($event);
+ if (this.providerDetails) {
+ this.providerDetails.refresh();
+ }
+ if (updateSummaryOnly) {
+ return;
+ }
+ this.providerRecord.announceProviderUpdated();
+ if (hideSearchForm) {
+ this.showSearchForm = false;
+ }
+ this.activeTab = this.defaultTabType;
+ this.router.navigate(['/staff', 'acq', 'provider', this.id, this.activeTab]);
+ });
+ };
+
+ this.onSummaryToggled = ($event) => {
+ // in case this is useful for a better implementation of reflowing the UI
+ };
+ }
+
+ ngAfterViewInit() {
+ this.changeDetector.detectChanges();
+ }
+
+ ngOnDestroy(): void {
+ this.destroyed.next();
+ this.destroyed.complete();
+ }
+
+ setDefaultTab() {
+ this.defaultTabType = this.activeTab;
+ this.store.setLocalItem('eg.acq.provider.default_tab', this.activeTab);
+ }
+
+ createNew() {
+ this.createDialog.mode = 'create';
+ const provider = this.idl.create('acqpro');
+ provider.active(true);
+ provider.owner(this.auth.user().ws_ou());
+ provider.default_copy_count(1);
+ this.createDialog.record = provider;
+ this.createDialog.recordId = null;
+ this.createDialog.open({size: 'lg'}).subscribe(
+ ok => {
+ this.createString.current()
+ .then(str => this.toast.success(str));
+ this.onDesireSummarize(ok.id());
+ },
+ rejection => {
+ if (!rejection.dismissed) {
+ this.createErrString.current()
+ .then(str => this.toast.danger(str));
+ }
+ }
+ );
+ }
+
+ canDeactivate(): Observable<boolean> {
+ if (this._alreadyDeactivated) {
+ // one freebie
+ this._alreadyDeactivated = false;
+ return of(true);
+ }
+ if ((this.providerHoldings && this.providerHoldings.isDirty()) ||
+ (this.providerDetails && this.providerDetails.isDirty())) {
+ return this.leaveConfirm.open();
+ } else {
+ return of(true);
+ }
+ }
+}
--- /dev/null
+import {NgModule} from '@angular/core';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {AcqProviderRoutingModule} from './routing.module';
+import {AcqProviderComponent} from './acq-provider.component';
+import {AcqProviderSearchFormComponent} from './acq-provider-search-form.component';
+import {AcqProviderSummaryPaneComponent} from './summary-pane.component';
+import {ProviderResultsComponent} from './provider-results.component';
+import {ProviderDetailsComponent} from './provider-details.component';
+import {ProviderAddressesComponent} from './provider-addresses.component';
+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';
+import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module';
+import {ProviderRecordService} from './provider-record.service';
+
+@NgModule({
+ declarations: [
+ AcqProviderComponent,
+ AcqProviderSearchFormComponent,
+ AcqProviderSummaryPaneComponent,
+ ProviderResultsComponent,
+ ProviderDetailsComponent,
+ ProviderAddressesComponent,
+ ProviderContactsComponent,
+ ProviderContactAddressesComponent,
+ ProviderHoldingsComponent,
+ ProviderAttributesComponent,
+ ProviderEdiAccountsComponent,
+ ProviderInvoicesComponent,
+ ProviderPurchaseOrdersComponent,
+ AcqProviderSummaryPaneComponent
+ ],
+ imports: [
+ StaffCommonModule,
+ OrgFamilySelectModule,
+ FmRecordEditorModule,
+ AcqProviderRoutingModule
+ ],
+ providers: [
+ ProviderRecordService
+ ],
+})
+
+export class AcqProviderModule {
+}
--- /dev/null
+<eg-string #createString i18n-text text="New Provider Address Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New Provider Address"></eg-string>
+<eg-string #successString i18n-text text="Provider Address Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Provider Address Update Failed"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of Provider Address failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Provider Address succeeded"></eg-string>
+
+<eg-grid #acqProviderAddressesGrid
+ persistKey="acq.provider.addresses"
+ idlClass="acqpa" [dataSource]="gridSource"
+ [sortable]="true"
+ [filterable]="true"
+ hideFields="provider,id"
+ [cellTextGenerator]="cellTextGenerator">
+ <eg-grid-toolbar-button
+ label="New Provider Address" 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-column name="address_type"></eg-grid-column>
+ <eg-grid-column name="street1"></eg-grid-column>
+ <eg-grid-column name="street2"></eg-grid-column>
+ <eg-grid-column name="city"></eg-grid-column>
+ <eg-grid-column name="county"></eg-grid-column>
+ <eg-grid-column name="state"></eg-grid-column>
+ <eg-grid-column name="country"></eg-grid-column>
+ <eg-grid-column name="post_code"></eg-grid-column>
+ <eg-grid-column name="fax_phone"></eg-grid-column>
+ <eg-grid-column name="valid"></eg-grid-column>
+</eg-grid>
+
+<eg-fm-record-editor #editDialog
+ idlClass="acqpa"
+ readonlyFields="id,provider"
+ fieldOrder="id,valid,address_type,provider,street1,street2,city,county,state,country,post_code,fax_phone">
+</eg-fm-record-editor>
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, OnDestroy, Input, ViewChild} from '@angular/core';
+import {empty, throwError, Observable, from, Subscription} 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 {PcrudService} from '@eg/core/pcrud.service';
+import {EventService} from '@eg/core/event.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';
+
+@Component({
+ selector: 'eg-provider-addresses',
+ templateUrl: 'provider-addresses.component.html',
+})
+export class ProviderAddressesComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ addresses: any[] = [];
+
+ gridSource: GridDataSource;
+ @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+ @ViewChild('acqProviderAddressesGrid', { static: true }) providerAddressesGrid: GridComponent;
+ @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;
+
+ cellTextGenerator: GridCellTextGenerator;
+ provider: IdlObject;
+
+ canCreate: boolean;
+ canDelete: boolean;
+ deleteSelected: (rows: IdlObject[]) => void;
+
+ permissions: {[name: string]: boolean};
+
+ subscription: Subscription;
+
+ // Size of create/edito dialog. Uses large by default.
+ @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private evt: EventService,
+ private pcrud: PcrudService,
+ private idl: IdlService,
+ private auth: AuthService,
+ private providerRecord: ProviderRecordService,
+ private toast: ToastService) {
+ }
+
+ ngOnInit() {
+ this.gridSource = this.getDataSource();
+ this.cellTextGenerator = {};
+ 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.providerAddressesGrid.reload()
+ );
+ }
+ );
+ };
+ this.providerAddressesGrid.onRowActivate.subscribe(
+ (idlThing: IdlObject) => this.showEditDialog(idlThing)
+ );
+ this.subscription = this.providerRecord.providerUpdated$.subscribe(
+ id => {
+ this.providerAddressesGrid.reload();
+ }
+ );
+ }
+
+ ngAfterViewInit() {
+ console.log('this.providerRecord', this.providerRecord);
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ generateSearch(filters): any {
+ const query: any = new Array();
+
+ Object.keys(filters).forEach(filterField => {
+ filters[filterField].forEach(condition => {
+ query.push(condition);
+ });
+ });
+ return query;
+ }
+
+ getDataSource(): GridDataSource {
+ const gridSource = new GridDataSource();
+
+ gridSource.getRows = (pager: Pager, sort: any[]) => {
+ this.provider = this.providerRecord.current();
+ if (!this.provider) {
+ return empty();
+ }
+ let addresses = this.provider.addresses();
+
+ const query = this.generateSearch(gridSource.filters);
+ if (query.length) {
+ query.unshift( { id: addresses.map(a => a.id()) } );
+
+ const opts = {};
+ opts['offset'] = pager.offset;
+ opts['limit'] = pager.limit;
+ opts['au_by_id'] = true;
+
+ if (sort.length > 0) {
+ opts['order_by'] = [];
+ sort.forEach(sort_clause => {
+ opts['order_by'].push({
+ class: 'acqpa',
+ field: sort_clause.name,
+ direction: sort_clause.dir
+ });
+ });
+ }
+
+ return this.pcrud.search('acqpa',
+ query,
+ opts
+ ).pipe(
+ map(res => {
+ if (this.evt.parse(res)) {
+ throw throwError(res);
+ } else {
+ return res;
+ }
+ }),
+ );
+ }
+
+ if (sort.length > 0) {
+ addresses = addresses.sort((a, b) => {
+ for (let i = 0; i < sort.length; i++) {
+ let lt = -1;
+ const 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(addresses.slice(pager.offset, pager.offset + pager.limit));
+ };
+ return gridSource;
+ }
+
+ showEditDialog(providerAddress: IdlObject): Promise<any> {
+ this.editDialog.mode = 'update';
+ this.editDialog.recordId = providerAddress['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.providerAddressesGrid.reload()
+ );
+ resolve(result);
+ },
+ error => {
+ this.updateFailedString.current()
+ .then(str => this.toast.danger(str));
+ reject(error);
+ }
+ );
+ });
+ }
+
+ editSelected(providerAddressFields: IdlObject[]) {
+ // Edit each IDL thing one at a time
+ const editOneThing = (providerAddress: IdlObject) => {
+ if (!providerAddress) { return; }
+
+ this.showEditDialog(providerAddress).then(
+ () => editOneThing(providerAddressFields.shift()));
+ };
+
+ editOneThing(providerAddressFields.shift());
+ }
+
+ createNew() {
+ this.editDialog.mode = 'create';
+ const address = this.idl.create('acqpa');
+ address.provider(this.provider.id());
+ address.valid(true);
+ this.editDialog.record = address;
+ 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.providerAddressesGrid.reload()
+ );
+ },
+ rejection => {
+ if (!rejection.dismissed) {
+ this.createErrString.current()
+ .then(str => this.toast.danger(str));
+ }
+ }
+ );
+ }
+}
--- /dev/null
+<eg-string #createString i18n-text text="New Provider Attributes Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New Provider Attributes"></eg-string>
+<eg-string #successString i18n-text text="Provider Attributes Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Provider Attributes Update Failed"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of Provider Attributes failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Provider Attributes succeeded"></eg-string>
+
+<eg-grid #acqProviderAttributesGrid
+ persistKey="acq.provider.attributes"
+ idlClass="acqlipad" [dataSource]="gridSource"
+ [sortable]="true"
+ hideFields="provider"
+ [cellTextGenerator]="cellTextGenerator">
+
+ <eg-grid-toolbar-button label="New Attributes" 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>
+
+<eg-fm-record-editor #editDialog
+ idlClass="acqlipad"
+ readonlyFields="id,provider"
+ fieldOrder="id,provider,code,description,xpath,remove,ident">
+</eg-fm-record-editor>
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, OnDestroy, Input, ViewChild} from '@angular/core';
+import {empty, throwError, Observable, from, Subscription} 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 {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';
+
+
+@Component({
+ selector: 'eg-provider-attributes',
+ templateUrl: 'provider-attributes.component.html',
+})
+export class ProviderAttributesComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ @Input() providerId: any;
+ attributes: any[] = [];
+
+ gridSource: GridDataSource;
+ @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+ @ViewChild('acqProviderAttributesGrid', { static: true }) providerAttributesGrid: GridComponent;
+ @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;
+
+ cellTextGenerator: GridCellTextGenerator;
+ provider: IdlObject;
+
+ canCreate: boolean;
+ canDelete: boolean;
+ deleteSelected: (rows: IdlObject[]) => void;
+
+ permissions: {[name: string]: boolean};
+
+ subscription: Subscription;
+
+ // Size of create/edito dialog. Uses large by default.
+ @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private auth: AuthService,
+ private idl: IdlService,
+ private providerRecord: ProviderRecordService,
+ private toast: ToastService) {
+
+ }
+
+ ngOnInit() {
+ this.gridSource = this.getDataSource();
+ this.cellTextGenerator = {};
+ 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.providerAttributesGrid.reload()
+ );
+ }
+ );
+ };
+ this.providerAttributesGrid.onRowActivate.subscribe(
+ (idlThing: IdlObject) => this.showEditDialog(idlThing)
+ );
+ this.subscription = this.providerRecord.providerUpdated$.subscribe(
+ id => {
+ this.providerAttributesGrid.reload();
+ }
+ );
+
+ }
+
+ ngAfterViewInit() {
+ console.log('this.providerRecord', this.providerRecord);
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ getDataSource(): GridDataSource {
+ const gridSource = new GridDataSource();
+
+ gridSource.getRows = (pager: Pager, sort: any[]) => {
+ this.provider = this.providerRecord.current();
+ if (!this.provider) {
+ return empty();
+ }
+ let attributes = this.provider.attributes();
+
+ if (sort.length > 0) {
+ attributes = attributes.sort((a, b) => {
+ for (let i = 0; i < sort.length; i++) {
+ let lt = -1;
+ const 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(attributes.slice(pager.offset, pager.offset + pager.limit));
+ };
+ return gridSource;
+ }
+
+ showEditDialog(providerAttribute: IdlObject): Promise<any> {
+ this.editDialog.mode = 'update';
+ this.editDialog.recordId = providerAttribute['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.providerAttributesGrid.reload()
+ );
+ resolve(result);
+ },
+ error => {
+ this.updateFailedString.current()
+ .then(str => this.toast.danger(str));
+ reject(error);
+ }
+ );
+ });
+ }
+
+ editSelected(providerAttributesFields: IdlObject[]) {
+ // Edit each IDL thing one at a time
+ const editOneThing = (providerAttributes: IdlObject) => {
+ if (!providerAttributes) { return; }
+
+ this.showEditDialog(providerAttributes).then(
+ () => editOneThing(providerAttributesFields.shift()));
+ };
+
+ editOneThing(providerAttributesFields.shift());
+ }
+
+ createNew() {
+ this.editDialog.mode = 'create';
+ const attributes = this.idl.create('acqlipad');
+ attributes.provider(this.provider.id());
+ this.editDialog.record = attributes;
+ 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.providerAttributesGrid.reload()
+ );
+ },
+ rejection => {
+ if (!rejection.dismissed) {
+ this.createErrString.current()
+ .then(str => this.toast.danger(str));
+ }
+ }
+ );
+ }
+}
--- /dev/null
+<eg-string #createString i18n-text text="New Contact Address Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New Contact Address"></eg-string>
+<eg-string #successString i18n-text text="Contact Address Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Contact Address Update Failed"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of Contact Address failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Contact Address succeeded"></eg-string>
+
+<eg-grid #acqProviderContactAddressesGrid
+ persistKey="acq.provider.contact.addresses"
+ idlClass="acqpca" [dataSource]="gridSource"
+ [sortable]="true"
+ [filterable]="true"
+ hideFields="contact,id"
+ [cellTextGenerator]="cellTextGenerator">
+ <eg-grid-toolbar-button
+ label="New Contact Address" 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-column name="address_type"></eg-grid-column>
+ <eg-grid-column name="street1"></eg-grid-column>
+ <eg-grid-column name="street2"></eg-grid-column>
+ <eg-grid-column name="city"></eg-grid-column>
+ <eg-grid-column name="county"></eg-grid-column>
+ <eg-grid-column name="state"></eg-grid-column>
+ <eg-grid-column name="country"></eg-grid-column>
+ <eg-grid-column name="post_code"></eg-grid-column>
+ <eg-grid-column name="fax_phone"></eg-grid-column>
+ <eg-grid-column name="valid"></eg-grid-column>
+</eg-grid>
+
+<eg-fm-record-editor #editDialog
+ idlClass="acqpca"
+ readonlyFields="id,contact"
+ fieldOrder="id,valid,address_type,contact,street1,street2,city,county,state,country,post_code,fax_phone">
+</eg-fm-record-editor>
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, Input, 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 {PcrudService} from '@eg/core/pcrud.service';
+import {EventService} from '@eg/core/event.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';
+
+@Component({
+ selector: 'eg-provider-contact-addresses',
+ templateUrl: 'provider-contact-addresses.component.html',
+})
+export class ProviderContactAddressesComponent implements OnInit, AfterViewInit {
+
+ addresses: any[] = [];
+
+ gridSource: GridDataSource;
+ @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+ @ViewChild('acqProviderContactAddressesGrid', { static: true }) providerContactAddressesGrid: GridComponent;
+ @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;
+
+ cellTextGenerator: GridCellTextGenerator;
+
+ canCreate: boolean;
+ canDelete: boolean;
+ deleteSelected: (rows: IdlObject[]) => void;
+ reloadGrid: () => void;
+
+ permissions: {[name: string]: boolean};
+
+ // Size of create/edito dialog. Uses large by default.
+ @Input() dialogSize: 'sm' | 'lg' = 'lg';
+ @Input() contactId: any;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private evt: EventService,
+ private idl: IdlService,
+ private auth: AuthService,
+ private providerRecord: ProviderRecordService,
+ private toast: ToastService,
+ private pcrud: PcrudService) {
+ }
+
+ ngOnInit() {
+ this.gridSource = this.getDataSource();
+ this.cellTextGenerator = {};
+ this.reloadGrid = () => this.providerContactAddressesGrid.reload();
+ this.deleteSelected = (idlThings: IdlObject[]) => {
+ idlThings.forEach(idlThing => idlThing.isdeleted(true));
+ this.pcrud.autoApply(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.providerContactAddressesGrid.reload()
+ );
+ }
+ );
+ };
+ this.providerContactAddressesGrid.onRowActivate.subscribe(
+ (idlThing: IdlObject) => this.showEditDialog(idlThing)
+ );
+ }
+
+
+ ngAfterViewInit() {
+ console.log('this.contactId', this.contactId);
+ }
+
+ generateSearch(filters): any {
+ const query: any = new Array();
+
+ Object.keys(filters).forEach(filterField => {
+ filters[filterField].forEach(condition => {
+ query.push(condition);
+ });
+ });
+ return query;
+ }
+
+ getDataSource(): GridDataSource {
+ const gridSource = new GridDataSource();
+
+ gridSource.getRows = (pager: Pager, sort: any[]) => {
+ if (!this.contactId) {
+ return empty();
+ }
+ const cid = this.contactId;
+ const contact = this.providerRecord.current().contacts().filter( c => c.id() === cid)[0];
+ let addresses = contact.addresses();
+
+ const query = this.generateSearch(gridSource.filters);
+ if (query.length) {
+ query.unshift( { id: addresses.map(a => a.id()) } );
+
+ const opts = {};
+ opts['offset'] = pager.offset;
+ opts['limit'] = pager.limit;
+ opts['au_by_id'] = true;
+
+ if (sort.length > 0) {
+ opts['order_by'] = [];
+ sort.forEach(sort_clause => {
+ opts['order_by'].push({
+ class: 'acqpca',
+ field: sort_clause.name,
+ direction: sort_clause.dir
+ });
+ });
+ }
+
+ return this.pcrud.search('acqpca',
+ query,
+ opts
+ ).pipe(
+ map(res => {
+ if (this.evt.parse(res)) {
+ throw throwError(res);
+ } else {
+ return res;
+ }
+ }),
+ );
+ }
+
+ if (sort.length > 0) {
+ addresses = addresses.sort((a, b) => {
+ for (let i = 0; i < sort.length; i++) {
+ let lt = -1;
+ const 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(addresses.slice(pager.offset, pager.offset + pager.limit));
+ };
+ return gridSource;
+ }
+
+ showEditDialog(providerContactAddress: IdlObject): Promise<any> {
+ this.editDialog.mode = 'update';
+ this.editDialog.recordId = providerContactAddress['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.providerContactAddressesGrid.reload()
+ );
+ resolve(result);
+ },
+ error => {
+ this.updateFailedString.current()
+ .then(str => this.toast.danger(str));
+ reject(error);
+ }
+ );
+ });
+ }
+
+ editSelected(providerContactAddressFields: IdlObject[]) {
+ // Edit each IDL thing one at a time
+ const editOneThing = (providerContactAddress: IdlObject) => {
+ if (!providerContactAddress) { return; }
+
+ this.showEditDialog(providerContactAddress).then(
+ () => editOneThing(providerContactAddressFields.shift()));
+ };
+
+ editOneThing(providerContactAddressFields.shift());
+ }
+
+ createNew() {
+ this.editDialog.mode = 'create';
+ const address = this.idl.create('acqpca');
+ address.contact(this.contactId);
+ address.valid(true);
+ this.editDialog.record = address;
+ 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.providerContactAddressesGrid.reload()
+ );
+ },
+ rejection => {
+ if (!rejection.dismissed) {
+ this.createErrString.current()
+ .then(str => this.toast.danger(str));
+ }
+ }
+ );
+ }
+}
--- /dev/null
+<eg-string #createString i18n-text text="New Provider Contact Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New Provider Contact"></eg-string>
+<eg-string #successString i18n-text text="Provider Contact Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Provider Contact Update Failed"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of Provider Contact failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Provider Contact succeeded"></eg-string>
+<eg-string #setAsPrimarySuccessString i18n-text text="Successfully set primary contact"></eg-string>
+<eg-string #setAsPrimaryFailedtring i18n-text text="Failed to set primary contact"></eg-string>
+<eg-string #unsetAsPrimarySuccessString i18n-text text="Successfully removed primary contact"></eg-string>
+<eg-string #unsetAsPrimaryFailedtring i18n-text text="Failed to remove primary contact"></eg-string>
+
+<ng-template #emailTmpl let-contact="row">
+ <a href="mailto:{{contact.email()}}">{{contact.email()}}</a>
+</ng-template>
+
+<ng-template #phoneTmpl let-contact="row">
+ <a href="tel:{{contact.phone()}}">{{contact.phone()}}</a>
+</ng-template>
+
+<eg-confirm-dialog #confirmSetAsPrimary
+ i18n-dialogTitle i18n-dialogBody
+ dialogTitle="Confirm Setting Primary Contact"
+ dialogBody="Set {{selectedContact ? selectedContact.name() : ''}} as the primary contact for {{provider ? provider.name() : ''}}?">
+</eg-confirm-dialog>
+
+<eg-confirm-dialog #confirmUnsetAsPrimary
+ i18n-dialogTitle i18n-dialogBody
+ dialogTitle="Confirm Unsetting Primary Contact"
+ dialogBody="Unset {{selectedContact ? selectedContact.name() : ''}} as the primary contact for {{provider ? provider.name() : ''}}?">
+</eg-confirm-dialog>
+
+<eg-grid #acqProviderContactsGrid
+ persistKey="acq.provider.contacts"
+ idlClass="acqpc" [dataSource]="gridSource"
+ [sortable]="true"
+ [disableMultiSelect]="true"
+ [filterable]="true"
+ hideFields="provider"
+ [cellTextGenerator]="cellTextGenerator">
+
+ <eg-grid-toolbar-button label="New Provider Contact" 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 Primary Contact" i18n-label (onClick)="setAsPrimary($event)" [disableOnRows]="cannotSetPrimaryContact">
+ </eg-grid-toolbar-action>
+ <eg-grid-toolbar-action label="Unset as Primary Contact" i18n-label (onClick)="unsetAsPrimary($event)" [disableOnRows]="cannotUnsetPrimaryContact">
+ </eg-grid-toolbar-action>
+
+
+ <eg-grid-column path="email" [cellTemplate]="emailTmpl" [disableTooltip]="true"></eg-grid-column>
+ <eg-grid-column path="phone" [cellTemplate]="phoneTmpl" [disableTooltip]="true"></eg-grid-column>
+ <eg-grid-column [filterable]="false" [sortable]="false" i18n-label label="Is Primary?" path="_is_primary" datatype="bool"></eg-grid-column>
+</eg-grid>
+
+<ng-container *ngIf="selectedContact">
+ <hr><h3 i18n>Addresses for: {{selectedContact.name()}}</h3>
+ <eg-provider-contact-addresses
+ #providerContactAddresses
+ [contactId]="selectedContact.id()">
+ </eg-provider-contact-addresses>
+</ng-container>
+
+<eg-fm-record-editor #editDialog
+ idlClass="acqpc"
+ readonlyFields="id,provider"
+ fieldOrder="id,provider,name,role,email,phone">
+</eg-fm-record-editor>
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, OnDestroy, Input, Output, ViewChild, EventEmitter, ChangeDetectorRef} from '@angular/core';
+import {empty, throwError, Observable, from, Subscription} 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 {PcrudService} from '@eg/core/pcrud.service';
+import {EventService} from '@eg/core/event.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 {ProviderRecordService} from './provider-record.service';
+import {ProviderContactAddressesComponent} from './provider-contact-addresses.component';
+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 {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+
+
+@Component({
+ selector: 'eg-provider-contacts',
+ templateUrl: 'provider-contacts.component.html',
+})
+export class ProviderContactsComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ @Input() providerId: any;
+ contacts: any[] = [];
+
+ gridSource: GridDataSource;
+ @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+ @ViewChild('providerContactAddresses', { static: false }) providerContactAddresses: ProviderContactAddressesComponent;
+ @ViewChild('acqProviderContactsGrid', { static: true }) providerContactsGrid: GridComponent;
+ @ViewChild('confirmSetAsPrimary', { static: true }) confirmSetAsPrimary: ConfirmDialogComponent;
+ @ViewChild('confirmUnsetAsPrimary', { static: true }) confirmUnsetAsPrimary: 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('setAsPrimarySuccessString', { static: true }) setAsPrimarySuccessString: StringComponent;
+ @ViewChild('setAsPrimaryFailedString', { static: true }) setAsPrimaryFailedString: StringComponent;
+ @ViewChild('unsetAsPrimarySuccessString', { static: true }) unsetAsPrimarySuccessString: StringComponent;
+ @ViewChild('unsetAsPrimaryFailedString', { static: true }) unsetAsPrimaryFailedString: StringComponent;
+
+ @Output() desireSummarize: EventEmitter<number> = new EventEmitter<number>();
+
+ cellTextGenerator: GridCellTextGenerator;
+ provider: IdlObject;
+ selectedContact: IdlObject;
+
+ canCreate: boolean;
+ canDelete: boolean;
+ deleteSelected: (rows: IdlObject[]) => void;
+ cannotSetPrimaryContact: (rows: IdlObject[]) => boolean;
+ cannotUnsetPrimaryContact: (rows: IdlObject[]) => boolean;
+
+ permissions: {[name: string]: boolean};
+
+ subscription: Subscription;
+
+ // Size of create/edito dialog. Uses large by default.
+ @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private changeDetector: ChangeDetectorRef,
+ private net: NetService,
+ private pcrud: PcrudService,
+ private evt: EventService,
+ private auth: AuthService,
+ private idl: IdlService,
+ private providerRecord: ProviderRecordService,
+ private toast: ToastService) {
+ }
+
+ ngOnInit() {
+ this.gridSource = this.getDataSource();
+ this.cellTextGenerator = {
+ email: row => row.email(),
+ phone: row => row.phone(),
+ };
+ this.cannotSetPrimaryContact = (rows: IdlObject[]) => (rows.length !== 1 || (rows.length === 1 && rows[0]._is_primary));
+ this.cannotUnsetPrimaryContact = (rows: IdlObject[]) => (rows.length !== 1 || (rows.length === 1 && !rows[0]._is_primary));
+ 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));
+ this.desireSummarize.emit(this.provider.id());
+ },
+ err => {
+ this.deleteFailedString.current()
+ .then(str => this.toast.danger(str));
+ },
+ () => {
+ this.providerRecord.refreshCurrent().then(
+ () => this.providerContactsGrid.reload()
+ );
+ }
+ );
+ };
+ this.providerContactsGrid.onRowActivate.subscribe(
+ (idlThing: IdlObject) => this.showEditDialog(idlThing)
+ );
+ this.subscription = this.providerRecord.providerUpdated$.subscribe(
+ id => {
+ this.providerContactsGrid.reload();
+ }
+ );
+ }
+
+ ngAfterViewInit() {
+ console.log('this.providerRecord', this.providerRecord);
+ console.log('this.providerContactAddresses', this.providerContactAddresses);
+ this.providerContactsGrid.onRowClick.subscribe(
+ (idlThing: IdlObject) => {
+ this.selectedContact = idlThing;
+ console.debug('selected contact', this.selectedContact);
+ // ensure that the contact address grid is instantiated
+ this.changeDetector.detectChanges();
+ this.providerContactAddresses.reloadGrid();
+ }
+ );
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ generateSearch(filters): any {
+ const query: any = new Array();
+
+ Object.keys(filters).forEach(filterField => {
+ filters[filterField].forEach(condition => {
+ query.push(condition);
+ });
+ });
+ return query;
+ }
+
+ getDataSource(): GridDataSource {
+ const gridSource = new GridDataSource();
+
+ gridSource.getRows = (pager: Pager, sort: any[]) => {
+ this.provider = this.providerRecord.current();
+ if (!this.provider) {
+ return empty();
+ }
+ let contacts = this.provider.contacts();
+
+ const query = this.generateSearch(gridSource.filters);
+ if (query.length) {
+ query.unshift( { id: contacts.map(a => a.id()) } );
+
+ const opts = {};
+ opts['offset'] = pager.offset;
+ opts['limit'] = pager.limit;
+ opts['au_by_id'] = true;
+
+ if (sort.length > 0) {
+ opts['order_by'] = [];
+ sort.forEach(sort_clause => {
+ opts['order_by'].push({
+ class: 'acqpc',
+ field: sort_clause.name,
+ direction: sort_clause.dir
+ });
+ });
+ }
+
+ return this.pcrud.search('acqpc',
+ query,
+ opts
+ ).pipe(
+ map(res => {
+ if (this.evt.parse(res)) {
+ throw throwError(res);
+ } else {
+ return res;
+ }
+ }),
+ );
+ }
+
+ if (sort.length > 0) {
+ contacts = contacts.sort((a, b) => {
+ for (let i = 0; i < sort.length; i++) {
+ let lt = -1;
+ const 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(contacts.slice(pager.offset, pager.offset + pager.limit));
+ };
+ return gridSource;
+ }
+
+ showEditDialog(providerContact: IdlObject): Promise<any> {
+ this.editDialog.mode = 'update';
+ this.editDialog.recordId = providerContact['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.providerContactsGrid.reload()
+ );
+ this.desireSummarize.emit(this.provider.id());
+ resolve(result);
+ },
+ error => {
+ this.updateFailedString.current()
+ .then(str => this.toast.danger(str));
+ reject(error);
+ }
+ );
+ });
+ }
+
+ editSelected(providerContactFields: IdlObject[]) {
+ // Edit each IDL thing one at a time
+ const editOneThing = (providerContact: IdlObject) => {
+ if (!providerContact) { return; }
+
+ this.showEditDialog(providerContact).then(
+ () => editOneThing(providerContactFields.shift()));
+ };
+
+ editOneThing(providerContactFields.shift());
+ }
+
+ createNew() {
+ this.editDialog.mode = 'create';
+ const contact = this.idl.create('acqpc');
+ contact.provider(this.provider.id());
+ this.editDialog.record = contact;
+ 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.providerContactsGrid.reload()
+ );
+ this.desireSummarize.emit(this.provider.id());
+ },
+ rejection => {
+ if (!rejection.dismissed) {
+ this.createErrString.current()
+ .then(str => this.toast.danger(str));
+ }
+ }
+ );
+ }
+
+ setAsPrimary(providerContacts: IdlObject[]) {
+ this.selectedContact = providerContacts[0];
+ this.confirmSetAsPrimary.open().subscribe(confirmed => {
+ if (!confirmed) { return; }
+ this.providerRecord.refreshCurrent().then(() => {
+ this.provider.primary_contact(providerContacts[0].id());
+ this.provider.ischanged(true);
+ this.providerRecord.batchUpdate(this.provider).subscribe(
+ val => {
+ this.setAsPrimarySuccessString.current()
+ .then(str => this.toast.success(str));
+ },
+ err => {
+ this.setAsPrimaryFailedString.current()
+ .then(str => this.toast.danger(str));
+ },
+ () => {
+ this.providerRecord.refreshCurrent().then(
+ () => {
+ this.providerContactsGrid.reload();
+ this.desireSummarize.emit(this.provider.id());
+ }
+ );
+ }
+ );
+ });
+ });
+ }
+
+ unsetAsPrimary(providerContacts: IdlObject[]) {
+ this.selectedContact = providerContacts[0];
+ this.confirmUnsetAsPrimary.open().subscribe(confirmed => {
+ if (!confirmed) { return; }
+ this.providerRecord.refreshCurrent().then(() => {
+ this.provider.primary_contact(null);
+ this.provider.ischanged(true);
+ this.providerRecord.batchUpdate(this.provider).subscribe(
+ val => {
+ this.unsetAsPrimarySuccessString.current()
+ .then(str => this.toast.success(str));
+ },
+ err => {
+ this.unsetAsPrimaryFailedString.current()
+ .then(str => this.toast.danger(str));
+ },
+ () => {
+ this.providerRecord.refreshCurrent().then(
+ () => {
+ this.providerContactsGrid.reload();
+ this.desireSummarize.emit(this.provider.id());
+ }
+ );
+ }
+ );
+ });
+ });
+ }
+}
--- /dev/null
+<eg-string #successString i18n-text text="Provider Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Provider Update Failed"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of Provider failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Provider succeeded"></eg-string>
+
+<eg-fm-record-editor #editDialog
+ idlClass="acqpro"
+ [mode]="permittedMode()"
+ [hideBanner]="true" displayMode="inline"
+ [record]="provider"
+ (recordSaved)="updateProvider($event)"
+ readonlyFields="id"
+ hiddenFields="holding_tag,primary_contact"
+ [fieldOptions]="{currency_type:{preloadLinkedValues:true},edi_default:{preloadLinkedValues:true},default_claim_policy:{preloadLinkedValues:true}}"
+ fieldOrder="active,name,code,id,currency_type,default_claim_policy,default_copy_count,edi_default,owner,phone,fax_phone,email,url,san,prepayment_required"
+>
+</eg-fm-record-editor>
--- /dev/null
+import {Component, OnInit, 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 {IdlService, IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {ProviderRecord, ProviderRecordService} from './provider-record.service';
+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';
+
+@Component({
+ selector: 'eg-provider-details',
+ templateUrl: 'provider-details.component.html',
+})
+export class ProviderDetailsComponent implements OnInit {
+
+ @ViewChild('successString', { static: true }) successString: StringComponent;
+ @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
+ @ViewChild('deleteFailedString', { static: true }) deleteFailedString: StringComponent;
+ @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
+ @ViewChild('editDialog', { static: false}) editDialog: FmRecordEditorComponent;
+
+ provider: IdlObject;
+
+ permissions: {[name: string]: boolean};
+
+ @Output() desireSummarize: 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.refresh();
+ }
+
+ _deflesh() {
+ if (!this.provider) {
+ return;
+ }
+ // unflesh the currency type and edi_default fields so that the
+ // record editor can display them
+ // TODO: make fm-editor be able to handle fleshed linked fields
+ if (this.provider.currency_type()) {
+ this.provider.currency_type(this.provider.currency_type().code());
+ }
+ if (this.provider.edi_default() && typeof this.provider.edi_default() !== 'number') {
+ this.provider.edi_default(this.provider.edi_default().id());
+ }
+ }
+
+ updateProvider(providerId: any) {
+ this.desireSummarize.emit(this.provider.id());
+ }
+
+ refresh() {
+ this.provider = this.idl.clone(this.providerRecord.current());
+ this._deflesh();
+ }
+
+ permittedMode(): string {
+ // TODO - looks like fm-editor may have (via its modePerms) incompletely-implemented
+ // work to vary the mode depending on whether the user has permission
+ // to update a record, which would make this moot.
+ if (!this.providerRecord.currentProviderRecord()) {
+ return 'view';
+ }
+ return this.providerRecord.currentProviderRecord().canAdmin ? 'update' : 'view';
+ }
+
+ isDirty(): boolean {
+ return (this.editDialog) ? this.editDialog.isDirty() : false;
+ }
+}
--- /dev/null
+<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"
+ 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-button
+ label="View EDI Messages" i18n-label (onClick)="displayEdiMessages($event)" [disabled]="acqProviderEdiAccountsGrid.context.getSelectedRows().length !== 1">
+ </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-toolbar-action label="View EDI Messages" i18n-label (onClick)="displayEdiMessages($event)" [disableOnRows]="notOneSelectedRow">
+ </eg-grid-toolbar-action>
+ <eg-grid-column [filterable]="false" [sortable]="false" i18n-label label="Is Default?" path="_is_default" datatype="bool"></eg-grid-column>
+</eg-grid>
+
+<ng-container *ngIf="viewEdiMessages">
+ <h2 i18n>EDI messages for account {{selectedEdiAccountLabel}}</h2>
+ <eg-grid #acqProviderEdiMessagesGrid
+ persistKey="acq.provider.edi_messages"
+ idlClass="acqedim" [dataSource]="ediMessagesSource"
+ [sortable]="true"
+ [filterable]="true">
+ </eg-grid>
+</ng-container>
+
+<eg-string #hostHelpStr text="EDI FTP or SCP server, including protocol. For example, ftp://ftp.example.org." i18n-text></eg-string>
+<eg-string #usernameHelpStr text="Username supplied by provider."></eg-string>
+<eg-string #passwordHelpStr text="Password supplied by provider."></eg-string>
+<eg-string #pathHelpStr text="Directory on the provider's server where Evergreen should deposit order files."></eg-string>
+<eg-string #indirHelpStr text="Directory on the provider's server where Evergreen should retrieve order responses and invoices."></eg-string>
+<eg-string #vendacctHelpStr text="Supplied by provider."></eg-string>
+<eg-string #vendcodeHelpStr text="Supplied by provider."></eg-string>
+<eg-string #accountHelpStr text="Supplied by provider."></eg-string>
+
+<eg-fm-record-editor #editDialog
+ idlClass="acqedi"
+ readonlyFields="id,provider"
+ hiddenFields="provider,last_activity"
+ [fieldOptions]="{
+ host: {helpText: hostHelpStr},
+ username: {helpText: usernameHelpStr},
+ password: {helpText: passwordHelpStr},
+ path: {helpText: pathHelpStr},
+ in_dir: {helpText: indirHelpStr},
+ vendacct: {helpText: vendacctHelpStr},
+ vendcode: {helpText: vendcodeHelpStr},
+ account: {helpText: accountHelpStr}
+ }"
+ fieldOrder="id,label,host,username,password,account,owner,path,in_dir,vendacct,vendcode,attr_set,use_attrs">
+</eg-fm-record-editor>
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, OnDestroy, Input, Output, EventEmitter, ViewChild, ChangeDetectorRef} from '@angular/core';
+import {empty, throwError, Observable, from, Subscription} 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';
+import {PcrudService} from '@eg/core/pcrud.service';
+
+@Component({
+ selector: 'eg-provider-edi-accounts',
+ templateUrl: 'provider-edi-accounts.component.html',
+})
+export class ProviderEdiAccountsComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ edi_accounts: any[] = [];
+
+ gridSource: GridDataSource;
+ ediMessagesSource: GridDataSource;
+ @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+ @ViewChild('acqProviderEdiAccountsGrid', { static: true }) providerEdiAccountsGrid: GridComponent;
+ @ViewChild('acqProviderEdiMessagesGrid', { static: false }) providerEdiMessagesGrid: 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;
+
+ viewEdiMessages: boolean;
+ selectedEdiAccountId: number;
+ selectedEdiAccountLabel = '';
+
+ permissions: {[name: string]: boolean};
+
+ subscription: Subscription;
+
+ // Size of create/edito dialog. Uses large by default.
+ @Input() dialogSize: 'sm' | 'lg' = 'lg';
+ @Output() desireSummarize: EventEmitter<number> = new EventEmitter<number>();
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private changeDetector: ChangeDetectorRef,
+ private net: NetService,
+ private idl: IdlService,
+ private auth: AuthService,
+ private pcrud: PcrudService,
+ private providerRecord: ProviderRecordService,
+ private toast: ToastService) {
+ }
+
+ ngOnInit() {
+ this.gridSource = this.getDataSource();
+ this.ediMessagesSource = this.getEdiMessagesSource();
+ this.viewEdiMessages = false;
+ this.selectedEdiAccountId = null;
+ 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)
+ );
+ this.subscription = this.providerRecord.providerUpdated$.subscribe(
+ id => {
+ this.providerEdiAccountsGrid.reload();
+ }
+ );
+ }
+
+ ngAfterViewInit() {
+ console.log('this.providerRecord', this.providerRecord);
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ 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;
+ const 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));
+ };
+ return gridSource;
+ }
+
+ getEdiMessagesSource(): GridDataSource {
+ const gridSource = new GridDataSource();
+ gridSource.getRows = (pager: Pager, sort: any[]) => {
+ const orderBy: any = {acqedim: 'create_time desc'};
+ if (sort.length) {
+ orderBy.acqedim = sort[0].name + ' ' + sort[0].dir;
+ }
+
+ // base query to grab everything
+ const base: Object = {
+ account: this.selectedEdiAccountId
+ };
+ const query: any = new Array();
+ query.push(base);
+
+ // and add any filters
+ Object.keys(gridSource.filters).forEach(key => {
+ Object.keys(gridSource.filters[key]).forEach(key2 => {
+ query.push(gridSource.filters[key][key2]);
+ });
+ });
+ return this.pcrud.search('acqedim',
+ query, {
+ flesh: 3,
+ flesh_fields: {acqedim: ['account', 'purchase_order']},
+ offset: pager.offset,
+ limit: pager.limit,
+ order_by: orderBy
+ });
+ };
+ 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().then(
+ () => {
+ this.providerEdiAccountsGrid.reload();
+ this.desireSummarize.emit(this.provider.id());
+ }
+ );
+ }
+ );
+ });
+ });
+ }
+
+ displayEdiMessages(providerEdiAccountFields: IdlObject[]) {
+ this.selectedEdiAccountId = providerEdiAccountFields[0].id();
+ this.selectedEdiAccountLabel = providerEdiAccountFields[0].label();
+ this.viewEdiMessages = true;
+ this.changeDetector.detectChanges();
+ this.providerEdiMessagesGrid.reload();
+ }
+
+ 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));
+ }
+ }
+ );
+ }
+
+}
--- /dev/null
+<eg-string #createString i18n-text text="New Provider Holdings Subfield Added"></eg-string>
+<eg-string #createErrString i18n-text text="Failed to Create New Provider Holdings Subfield"></eg-string>
+<eg-string #successString i18n-text text="Provider Holdings Subfield Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Provider Holdings Subfield Update Failed"></eg-string>
+<eg-string #deleteFailedString i18n-text text="Delete of Provider Holdings Subfield failed or was not allowed"></eg-string>
+<eg-string #deleteSuccessString i18n-text text="Delete of Provider Holdings Subfield succeeded"></eg-string>
+<eg-string #successTagString i18n-text text="Provider Holdings Tag Update Succeeded"></eg-string>
+<eg-string #updateFailedTagString i18n-text text="Provider Holdings Subfield Update Failed"></eg-string>
+
+<ng-template #nameCellTemplate let-row="row">
+ <ng-container [ngSwitch]="row.name()">
+ <ng-container *ngSwitchCase="'barcode'" i18n>Barcode</ng-container>
+ <ng-container *ngSwitchCase="'call_number'" i18n>Call Number</ng-container>
+ <ng-container *ngSwitchCase="'circ_modifier'" i18n>Circulation Modifier</ng-container>
+ <ng-container *ngSwitchCase="'collection_code'" i18n>Collection Code</ng-container>
+ <ng-container *ngSwitchCase="'estimated_price'" i18n>Estimated Price</ng-container>
+ <ng-container *ngSwitchCase="'fund_code'" i18n>Fund Code</ng-container>
+ <ng-container *ngSwitchCase="'note'" i18n>Note</ng-container>
+ <ng-container *ngSwitchCase="'owning_lib'" i18n>Owning Library</ng-container>
+ <ng-container *ngSwitchCase="'quantity'" i18n>Quantity</ng-container>
+ <ng-container *ngSwitchCase="'copy_location'" i18n>Shelving Location</ng-container>
+ <ng-container *ngSwitchDefault i18n>{{row.name()}}</ng-container>
+ </ng-container>
+</ng-template>
+
+<form *ngIf="provider" #holdingTagForm="ngForm">
+ <div class="form-group row">
+ <label for="holdings-tag" class="col-auto col-form-label" i18n>Holdings Tag</label>
+ <div class="col-auto">
+ <input id="holdings-tag" type="text" [(ngModel)]="provider._holding_tag" name="holding_tag" class="form-control" />
+ </div>
+ <div class="col-auto">
+ <button type="submit" class="btn btn-info"
+ (click)="updateProvider($event) && holdingTagForm.markAsPristine()"
+ [disabled]="!holdingTagForm.dirty || (provider && provider._holding_tag == provider.holding_tag())" i18n>Save</button>
+ </div>
+ </div>
+</form>
+
+<eg-grid #acqProviderHoldingsGrid
+ persistKey="acq.provider.holdings"
+ idlClass="acqphsm" [dataSource]="gridSource"
+ [sortable]="true"
+ hideFields="provider"
+ [cellTextGenerator]="cellTextGenerator">
+
+ <eg-grid-toolbar-button label="New Holdings Subfield" i18n-label (onClick)="createNew()"
+ [disabled]="!(provider && provider.holding_tag())"></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-column path="id"></eg-grid-column>
+ <eg-grid-column path="name" [cellTemplate]="nameCellTemplate"></eg-grid-column>
+ <eg-grid-column path="subfield"></eg-grid-column>
+
+</eg-grid>
+
+<ng-template #nameTemplate let-field="field" let-record="record">
+ <eg-combobox
+ [startId]="record[field.name]()"
+ [required]="field.isRequired()"
+ (onChange)="record[field.name]($event.id)"
+ [allowFreeText]="false">
+ <eg-combobox-entry entryId="barcode" entryLabel="Barcode" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="call_number" entryLabel="Call Number" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="circ_modifier" entryLabel="Circulation Modifier" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="collection_code" entryLabel="Collection Code" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="estimated_price" entryLabel="Estimated Price" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="fund_code" entryLabel="Fund Code" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="note" entryLabel="Note" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="owning_lib" entryLabel="Owning Library" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="quantity" entryLabel="Quantity" i18n-entryLabel></eg-combobox-entry>
+ <eg-combobox-entry entryId="copy_location" entryLabel="Shelving Location" i18n-entryLabel></eg-combobox-entry>
+ </eg-combobox>
+</ng-template>
+
+<eg-fm-record-editor #editDialog
+ idlClass="acqphsm"
+ readonlyFields="id,provider"
+ [fieldOptions]="{name:{customTemplate:{template:nameTemplate}}}"
+ fieldOrder="id,provider,name,subfield">
+</eg-fm-record-editor>
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, OnDestroy, Input, ViewChild} from '@angular/core';
+import {NgForm} from '@angular/forms';
+import {empty, throwError, Observable, from, Subscription} 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 {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';
+
+
+@Component({
+ selector: 'eg-provider-holdings',
+ templateUrl: 'provider-holdings.component.html',
+})
+export class ProviderHoldingsComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ @Input() providerId: any;
+ holdings: any[] = [];
+
+ gridSource: GridDataSource;
+ @ViewChild('editDialog', { static: true }) editDialog: FmRecordEditorComponent;
+ @ViewChild('acqProviderHoldingsGrid', { static: true }) providerHoldingsGrid: GridComponent;
+ @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('successTagString', { static: true }) successTagString: StringComponent;
+ @ViewChild('updateFailedTagString', { static: false }) updateFailedTagString: StringComponent;
+ @ViewChild('holdingTagForm', { static: false}) holdingTagForm: NgForm;
+
+ cellTextGenerator: GridCellTextGenerator;
+ provider: IdlObject;
+
+ canCreate: boolean;
+ canDelete: boolean;
+ deleteSelected: (rows: IdlObject[]) => void;
+
+ permissions: {[name: string]: boolean};
+
+ subscription: Subscription;
+
+ // Size of create/edito dialog. Uses large by default.
+ @Input() dialogSize: 'sm' | 'lg' = 'lg';
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private auth: AuthService,
+ private idl: IdlService,
+ private providerRecord: ProviderRecordService,
+ private toast: ToastService) {
+
+ }
+
+ ngOnInit() {
+ this.gridSource = this.getDataSource();
+ this.cellTextGenerator = {
+ name: row => row.name(),
+ };
+ 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.providerHoldingsGrid.reload()
+ );
+ }
+ );
+ };
+ this.providerHoldingsGrid.onRowActivate.subscribe(
+ (idlThing: IdlObject) => this.showEditDialog(idlThing)
+ );
+ this.subscription = this.providerRecord.providerUpdated$.subscribe(
+ id => {
+ this.providerHoldingsGrid.reload();
+ }
+ );
+ }
+
+ ngAfterViewInit() {
+ if (this.providerRecord.current()) {
+ // sometimes needs to force a refresh in case we updated that tag,
+ // navigated away (and confirmed that we wanted to abandon the change),
+ // then navigated back
+ this.providerRecord.current()['_holding_tag'] = this.providerRecord.current().holding_tag();
+ }
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ updateProvider(providerId: any) {
+ this.provider.holding_tag(this.provider._holding_tag);
+ this.provider.ischanged(true);
+ this.providerRecord.batchUpdate([this.provider]).subscribe(
+ val => {
+ this.successTagString.current()
+ .then(str => this.toast.success(str));
+ },
+ err => {
+ this.updateFailedTagString.current()
+ .then(str => this.toast.danger(str));
+ },
+ () => {
+ this.providerRecord.refreshCurrent().then(
+ () => { this.provider = this.providerRecord.current(); }
+ );
+ }
+ );
+ }
+
+ getDataSource(): GridDataSource {
+ const gridSource = new GridDataSource();
+
+ gridSource.getRows = (pager: Pager, sort: any[]) => {
+ this.provider = this.providerRecord.current();
+ if (!this.provider) {
+ return empty();
+ }
+ let holdings = this.provider.holdings_subfields();
+
+ if (sort.length > 0) {
+ holdings = holdings.sort((a, b) => {
+ for (let i = 0; i < sort.length; i++) {
+ let lt = -1;
+ const 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(holdings.slice(pager.offset, pager.offset + pager.limit));
+ };
+ return gridSource;
+ }
+
+ showEditDialog(providerHolding: IdlObject): Promise<any> {
+ this.editDialog.mode = 'update';
+ this.editDialog.recordId = providerHolding['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.providerHoldingsGrid.reload()
+ );
+ resolve(result);
+ },
+ error => {
+ this.updateFailedString.current()
+ .then(str => this.toast.danger(str));
+ reject(error);
+ }
+ );
+ });
+ }
+
+ editSelected(providerHoldingsFields: IdlObject[]) {
+ // Edit each IDL thing one at a time
+ const editOneThing = (providerHoldings: IdlObject) => {
+ if (!providerHoldings) { return; }
+
+ this.showEditDialog(providerHoldings).then(
+ () => editOneThing(providerHoldingsFields.shift()));
+ };
+
+ editOneThing(providerHoldingsFields.shift());
+ }
+
+ createNew() {
+ this.editDialog.mode = 'create';
+ const holdings = this.idl.create('acqphsm');
+ holdings.provider(this.provider.id());
+ this.editDialog.record = holdings;
+ 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.providerHoldingsGrid.reload()
+ );
+ },
+ rejection => {
+ if (!rejection.dismissed) {
+ this.createErrString.current()
+ .then(str => this.toast.danger(str));
+ }
+ }
+ );
+ }
+
+ isDirty(): boolean {
+ return (this.providerRecord.current()['_holding_tag'] === this.providerRecord.current().holding_tag()) ? false :
+ (this.holdingTagForm && this.holdingTagForm.dirty) ? this.holdingTagForm.dirty : false;
+ }
+}
--- /dev/null
+<ng-template #inv_identTmpl let-invoice="row">
+ <a href="/eg/staff/acq/legacy/invoice/view/{{invoice.id()}}"
+ target="_blank">
+ {{invoice.inv_ident()}}
+ </a>
+</ng-template>
+<ng-template #providerTmpl let-invoice="row">
+ <a routerLink="/staff/acq/provider/{{invoice.provider().id()}}"
+ target="_blank">
+ {{invoice.provider().code()}}
+ </a>
+</ng-template>
+<ng-template #shipperTmpl let-invoice="row">
+ <a routerLink="/staff/acq/provider/{{invoice.shipper().id()}}"
+ target="_blank">
+ {{invoice.shipper().code()}}
+ </a>
+</ng-template>
+
+<eg-grid #acqProviderInvoicesGrid
+ persistKey="acq.provider.invoices"
+ [stickyHeader]="true"
+ [filterable]="true"
+ [sortable]="true"
+ [cellTextGenerator]="cellTextGenerator"
+ idlClass="acqinv" [dataSource]="gridSource">
+
+ <eg-grid-toolbar-action label="Print Selected Invoices" i18n-label
+ (onClick)="printSelectedInvoices($event)" [disableOnRows]="noSelectedRows">
+ </eg-grid-toolbar-action>
+
+ <eg-grid-column path="inv_ident" [cellTemplate]="inv_identTmpl"></eg-grid-column>
+ <eg-grid-column path="provider" [cellTemplate]="providerTmpl" [filterable]="false" [hidden]="true"></eg-grid-column>
+ <eg-grid-column path="shipper" [cellTemplate]="shipperTmpl"></eg-grid-column>
+
+ <eg-grid-column path="id" [hidden]="true"></eg-grid-column>
+ <eg-grid-column path="recv_date" [hidden]="true"></eg-grid-column>
+ <eg-grid-column [asyncSupportsEmptyTermClick]="true" path="recv_method"></eg-grid-column>
+ <eg-grid-column [asyncSupportsEmptyTermClick]="true" path="payment_method"></eg-grid-column>
+
+</eg-grid>
+
+<eg-alert-dialog #printfail i18n-dialogBody
+ dialogBody="Could not print the selected invoices.">
+</eg-alert-dialog>
--- /dev/null
+import {Component, OnInit, AfterViewInit, OnDestroy, Input, ViewChild} from '@angular/core';
+import {Observable, Subscription} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {Pager} from '@eg/share/util/pager';
+import {IdlObject} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
+import {PrintService} from '@eg/share/print/print.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 {AcqSearchService, AcqSearchTerm, AcqSearch} from '../search/acq-search.service';
+import {AttrDefsService} from '../search/attr-defs.service';
+import {ProviderRecord, ProviderRecordService} from './provider-record.service';
+
+@Component({
+ selector: 'eg-provider-invoices',
+ templateUrl: 'provider-invoices.component.html',
+ providers: [AcqSearchService, AttrDefsService]
+})
+export class ProviderInvoicesComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ @Input() initialSearchTerms: AcqSearchTerm[] = [];
+
+ gridSource: GridDataSource;
+ @ViewChild('acqProviderInvoicesGrid', { static: true }) providerInvoicesGrid: GridComponent;
+ @ViewChild('printfail', { static: true }) private printfail: AlertDialogComponent;
+
+ noSelectedRows: (rows: IdlObject[]) => boolean;
+
+ cellTextGenerator: GridCellTextGenerator;
+
+ subscription: Subscription;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private printer: PrintService,
+ private evt: EventService,
+ private net: NetService,
+ private auth: AuthService,
+ private providerRecord: ProviderRecordService,
+ private acqSearch: AcqSearchService) {
+ }
+
+ ngOnInit() {
+ this.gridSource = this.acqSearch.getAcqSearchDataSource('invoice');
+ this.noSelectedRows = (rows: IdlObject[]) => (rows.length === 0);
+ this.cellTextGenerator = {
+ inv_ident: row => row.inv_ident(),
+ provider: row => row.provider().code(),
+ shipper: row => row.shipper().code(),
+ };
+ this.subscription = this.providerRecord.providerUpdated$.subscribe(
+ id => {
+ this.resetSearch();
+ }
+ );
+ }
+
+ ngAfterViewInit() {
+ this.resetSearch();
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+ resetSearch() {
+ const provider = this.providerRecord.current();
+ if (provider) {
+ setTimeout(() => {
+ this.acqSearch.setSearch({
+ terms: [{
+ field: 'acqinv:provider',
+ op: '',
+ value1: provider.id(),
+ value2: '',
+ }],
+ conjunction: 'all',
+ });
+ this.providerInvoicesGrid.reload();
+ });
+ }
+ }
+
+ // TODO - copied from InvoiceResultsComponent, could be
+ // consolidated
+ printSelectedInvoices(rows: IdlObject[]) {
+ const that = this;
+ let html = '<style type="text/css">.acq-invoice-' +
+ 'voucher {page-break-after:always;}' +
+ '</style>\n';
+ this.net.request(
+ 'open-ils.acq',
+ 'open-ils.acq.invoice.print.html',
+ this.auth.token(), rows.map( invoice => invoice.id() )
+ ).subscribe(
+ (res) => {
+ if (this.evt.parse(res)) {
+ console.error(res);
+ this.printfail.open();
+ } else {
+ html += res.template_output().data();
+ }
+ },
+ (err) => {
+ console.error(err);
+ this.printfail.open();
+ },
+ () => this.printer.print({
+ text: html,
+ printContext: 'default'
+ })
+ );
+ }
+
+}
--- /dev/null
+<ng-template #nameTmpl let-purchaseorder="row">
+ <a href="/eg/staff/acq/legacy/po/view/{{purchaseorder.id()}}"
+ target="_blank">
+ {{purchaseorder.name()}}
+ </a>
+</ng-template>
+
+<ng-template #providerTmpl let-purchaseorder="row">
+ <a routerLink="/staff/acq/provider/{{purchaseorder.provider().id()}}"
+ target="_blank">
+ {{purchaseorder.provider().code()}}
+ </a>
+</ng-template>
+
+<eg-grid #acqProviderPurchaseOrdersGrid
+ persistKey="acq.provider.purchaseorders"
+ [stickyHeader]="true"
+ [filterable]="true"
+ [sortable]="true"
+ [cellTextGenerator]="cellTextGenerator"
+ idlClass="acqpo" [dataSource]="gridSource">
+
+ <eg-grid-column path="name" [cellTemplate]="nameTmpl"></eg-grid-column>
+ <eg-grid-column path="id"></eg-grid-column>
+ <eg-grid-column path="provider" [hidden]="true" [filterable]="false" [cellTemplate]="providerTmpl"></eg-grid-column>
+ <eg-grid-column path="ordering_agency"></eg-grid-column>
+ <eg-grid-column path="create_time"></eg-grid-column>
+ <eg-grid-column path="edit_time"></eg-grid-column>
+ <eg-grid-column path="order_date"></eg-grid-column>
+
+ <eg-grid-column path="creator" [hidden]="true"></eg-grid-column>
+ <eg-grid-column path="editor" [hidden]="true"></eg-grid-column>
+ <eg-grid-column path="owner" [hidden]="true"></eg-grid-column>
+ <eg-grid-column [asyncSupportsEmptyTermClick]="true" i18n-label label="Status" path="state" [disableTooltip]="true"></eg-grid-column>
+ <eg-grid-column [asyncSupportsEmptyTermClick]="true" path="cancel_reason"></eg-grid-column>
+ <eg-grid-column path="prepayment_required" [sortable]="false"></eg-grid-column>
+
+</eg-grid>
--- /dev/null
+import {Component, OnInit, AfterViewInit, OnDestroy, Input, ViewChild} from '@angular/core';
+import {Observable, Subscription} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {Pager} from '@eg/share/util/pager';
+import {IdlObject} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
+import {PrintService} from '@eg/share/print/print.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 {AcqSearchService, AcqSearchTerm, AcqSearch} from '../search/acq-search.service';
+import {AttrDefsService} from '../search/attr-defs.service';
+import {ProviderRecord, ProviderRecordService} from './provider-record.service';
+
+@Component({
+ selector: 'eg-provider-purchase-orders',
+ templateUrl: 'provider-purchase-orders.component.html',
+ providers: [AcqSearchService, AttrDefsService]
+})
+export class ProviderPurchaseOrdersComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ @Input() initialSearchTerms: AcqSearchTerm[] = [];
+
+ gridSource: GridDataSource;
+ @ViewChild('acqProviderPurchaseOrdersGrid', { static: true }) providerPurchaseOrdersGrid: GridComponent;
+ @ViewChild('printfail', { static: true }) private printfail: AlertDialogComponent;
+
+ noSelectedRows: (rows: IdlObject[]) => boolean;
+
+ cellTextGenerator: GridCellTextGenerator;
+
+ subscription: Subscription;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private printer: PrintService,
+ private evt: EventService,
+ private net: NetService,
+ private auth: AuthService,
+ private providerRecord: ProviderRecordService,
+ private acqSearch: AcqSearchService) {
+ }
+
+ ngOnInit() {
+ this.gridSource = this.acqSearch.getAcqSearchDataSource('purchase_order');
+ this.noSelectedRows = (rows: IdlObject[]) => (rows.length === 0);
+ this.cellTextGenerator = {
+ inv_ident: row => row.inv_ident(),
+ provider: row => row.provider().code(),
+ shipper: row => row.shipper().code(),
+ };
+ this.subscription = this.providerRecord.providerUpdated$.subscribe(
+ id => {
+ this.resetSearch();
+ }
+ );
+ }
+
+ ngAfterViewInit() {
+ this.resetSearch();
+ }
+
+ resetSearch() {
+ const provider = this.providerRecord.current();
+ if (provider) {
+ setTimeout(() => {
+ this.acqSearch.setSearch({
+ terms: [{
+ field: 'acqpo:provider',
+ op: '',
+ value1: provider.id(),
+ value2: '',
+ }],
+ conjunction: 'all',
+ });
+ this.providerPurchaseOrdersGrid.reload();
+ });
+ }
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe();
+ }
+
+}
--- /dev/null
+import {Injectable} from '@angular/core';
+import {Observable, from} from 'rxjs';
+import {empty, throwError, Subject} from 'rxjs';
+import {map, defaultIfEmpty} from 'rxjs/operators';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {PermService} from '@eg/core/perm.service';
+
+export class ProviderSummary {
+}
+
+export class ProviderRecord {
+ id: number;
+ record: IdlObject;
+ canDelete: boolean;
+ canAdmin: boolean;
+
+ constructor(record: IdlObject) {
+ this.id = Number(record.id());
+ this.record = record;
+ this.canDelete = false;
+ this.canAdmin = false;
+ }
+}
+
+@Injectable()
+export class ProviderRecordService {
+
+ private currentProvider: ProviderRecord;
+ private currentProviderId: number = null;
+
+ private providerUpdatedSource = new Subject<number>();
+ providerUpdated$ = this.providerUpdatedSource.asObservable();
+
+ private permissions: any;
+ private viewOUs: number[] = [];
+
+ constructor(
+ private idl: IdlService,
+ private net: NetService,
+ private pcrud: PcrudService,
+ private perm: PermService
+ ) {
+ this.currentProvider = null;
+ this.loadPerms();
+ }
+
+ loadPerms(): Promise<any> {
+ if (this.permissions) {
+ return Promise.resolve();
+ }
+ return this.perm.hasWorkPermAt(['ADMIN_PROVIDER', 'MANAGE_PROVIDER', 'VIEW_PROVIDER'], true).then(permMap => {
+ this.permissions = permMap;
+ this.viewOUs.concat(permMap['VIEW_PROVIDER']);
+ this.permissions['ADMIN_PROVIDER'].forEach(ou => {
+ if (!this.viewOUs.includes(ou)) {
+ this.viewOUs.push(ou);
+ }
+ });
+ this.permissions['MANAGE_PROVIDER'].forEach(ou => {
+ if (!this.viewOUs.includes(ou)) {
+ this.viewOUs.push(ou);
+ }
+ });
+ });
+ }
+
+ getProviderRecord(id: number): Observable<ProviderRecord> {
+ console.debug('fetching provider ' + id);
+ this.currentProviderId = id;
+ const emptyGuard = this.idl.create('acqpro');
+ emptyGuard.id('no_provider_fetched');
+ return this.pcrud.search('acqpro', { id: id },
+ {
+ flesh: 3,
+ flesh_fields: { acqpro: [
+ 'attributes', 'holdings_subfields', 'contacts',
+ 'addresses', 'provider_notes',
+ 'edi_accounts', 'currency_type', 'edi_default'
+ ],
+ acqpa: ['provider'],
+ acqpc: ['provider', 'addresses'],
+ acqphsm: ['provider'],
+ acqlipad: ['provider'],
+ acqedi: ['attr_set', 'provider'],
+ }
+ },
+ {}
+ ).pipe(defaultIfEmpty(emptyGuard), map(acqpro => {
+ if (acqpro.id() === 'no_provider_fetched') {
+ throw new Error('no provider to fetch');
+ }
+ const provider = new ProviderRecord(acqpro);
+ // make a copy of holding_tag for use by the holdings definitions tab
+ acqpro['_holding_tag'] = acqpro.holding_tag();
+ acqpro.edi_accounts().forEach(acct => {
+ acct['_is_default'] = false;
+ if (acqpro.edi_default()) {
+ if (acct.id() === acqpro.edi_default().id()) {
+ acct['_is_default'] = true;
+ }
+ }
+ });
+ acqpro.contacts().forEach(acct => {
+ acct['_is_primary'] = false;
+ if (acqpro.primary_contact()) {
+ if (acct.id() === acqpro.primary_contact()) {
+ acct['_is_primary'] = true;
+ }
+ }
+ });
+ this.currentProvider = provider;
+ this.checkIfCanDelete(provider);
+ this.checkIfCanAdmin(provider);
+ return provider;
+ }));
+ }
+
+ checkIfCanDelete(prov: ProviderRecord) {
+ this.pcrud.search('acqpo', { provider: prov.id }, { limit: 1 }).toPromise()
+ .then(acqpo => {
+ if (!acqpo || acqpo.length === 0) {
+ this.pcrud.search('jub', { provider: prov.id }, { limit: 1 }).toPromise()
+ .then(jub => {
+ if (!jub || jub.length === 0) {
+ this.pcrud.search('acqinv', { provider: prov.id }, { limit: 1 }).toPromise()
+ .then(acqinv => {
+ prov.canDelete = true;
+ });
+ }
+ });
+ }
+ });
+ }
+
+ checkIfCanAdmin(prov: ProviderRecord) {
+ this.loadPerms().then(x => {
+ if (Object.keys(this.permissions).length > 0 &&
+ this.permissions['ADMIN_PROVIDER'].includes(prov.record.owner())) {
+ prov.canAdmin = true;
+ }
+ });
+ }
+
+ checkIfCanAdminAtAll(): boolean {
+ if (typeof this.permissions === 'undefined') {
+ return false;
+ }
+ if (Object.keys(this.permissions).length > 0 &&
+ this.permissions['ADMIN_PROVIDER'].length > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ getViewOUs(): number[] {
+ return this.viewOUs;
+ }
+
+ current(): IdlObject {
+ return this.currentProvider ? this.currentProvider.record : null;
+ }
+ currentProviderRecord(): ProviderRecord {
+ return this.currentProvider ? this.currentProvider : null;
+ }
+
+ fetch(id: number): Promise<any> {
+ return new Promise((resolve, reject) => {
+ this.getProviderRecord(id).subscribe(
+ result => {
+ resolve();
+ },
+ error => {
+ reject();
+ },
+ );
+ });
+ }
+
+ refreshCurrent(): Promise<any> {
+ if (this.currentProviderId) {
+ return this.fetch(this.currentProviderId);
+ } else {
+ return Promise.reject();
+ }
+ }
+
+ batchUpdate(list: IdlObject | IdlObject[]): Observable<any> {
+ return this.pcrud.autoApply(list);
+ }
+
+ announceProviderUpdated() {
+ this.providerUpdatedSource.next(this.currentProviderId);
+ }
+
+}
--- /dev/null
+<eg-acq-provider-search-form #providerSearchForm (searchSubmitted)="doSearch($event)"></eg-acq-provider-search-form>
+
+<ng-template #contactTmpl let-provider="row">
+ <ul>
+ <li *ngFor="let c of provider.contacts()">{{c.name()}}</li>
+ </ul>
+</ng-template>
+
+<eg-grid #acqSearchProviderGrid
+ persistKey="acq.provider.search.results"
+ idlClass="acqpro" [dataSource]="gridSource"
+ [stickyHeader]="true"
+ [filterable]="true"
+ [sortable]="true"
+ [disableMultiSelect]="true"
+ (onRowClick)="previewRow($event, false)"
+ (onRowActivate)="previewRow($event)"
+ [cellTextGenerator]="cellTextGenerator">
+
+ <eg-grid-toolbar-action label="Retrieve Provider" i18n-label (onClick)="retrieveRow($event)"></eg-grid-toolbar-action>
+ <eg-grid-column [asyncSupportsEmptyTermClick]="true" path="currency_type" [hidden]="true"></eg-grid-column>
+ <eg-grid-column [asyncSupportsEmptyTermClick]="true" path="default_claim_policy" [hidden]="true"></eg-grid-column>
+ <eg-grid-column path="contacts" [cellTemplate]="contactTmpl" [filterable]="false" [sortable]="false" [hidden]="true" [disableTooltip]="true"></eg-grid-column>
+</eg-grid>
+
--- /dev/null
+import {Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild, ElementRef} from '@angular/core';
+import {Observable} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {Pager} from '@eg/share/util/pager';
+import {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 {AcqProviderSearchService, AcqProviderSearchTerm, AcqProviderSearch} from './acq-provider-search.service';
+import {AcqProviderSearchFormComponent} from './acq-provider-search-form.component';
+
+@Component({
+ selector: 'eg-provider-results',
+ templateUrl: 'provider-results.component.html',
+ providers: [AcqProviderSearchService]
+})
+export class ProviderResultsComponent implements OnInit, AfterViewInit {
+
+ gridSource: GridDataSource;
+ @ViewChild('acqSearchProviderGrid', { static: true }) providerResultsGrid: GridComponent;
+ @ViewChild('providerSearchForm', { static: true }) providerSearchForm: AcqProviderSearchFormComponent;
+
+ cellTextGenerator: GridCellTextGenerator;
+ @Output() previewRow: (row: any, hideSearchForm?: boolean) => void;
+ @Output() desireSummarize: EventEmitter<number> = new EventEmitter<number>();
+ @Output() summarizeSearchFormOpen: EventEmitter<number> = new EventEmitter<number>();
+
+ constructor(
+ private elementRef: ElementRef,
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private auth: AuthService,
+ private providerSearch: AcqProviderSearchService) {
+ }
+
+ ngOnInit() {
+ this.gridSource = this.providerSearch.getDataSource();
+
+ this.cellTextGenerator = {
+ provider: row => row.provider().code(),
+ name: row => row.name(),
+ };
+
+ this.previewRow = (row: any, hideSearchForm = true) => {
+ if (hideSearchForm) {
+ this.desireSummarize.emit(row.id());
+ } else {
+ this.summarizeSearchFormOpen.emit(row.id());
+ }
+ };
+ }
+
+ ngAfterViewInit() {
+ // check if we're visible; if we are, we've
+ // likely come in directly from the main Provider Search
+ // menu item and should go ahead and submit the
+ // form with default values
+ // see: https://stackoverflow.com/questions/37843907/angular2-is-there-a-way-to-know-when-a-component-is-hidden
+ const elm = this.elementRef.nativeElement;
+ if (elm.offsetParent !== null) {
+ setTimeout(x => this.providerSearchForm.submitSearch());
+ }
+ }
+
+ retrieveRow(rows: IdlObject[]) {
+ this.desireSummarize.emit(rows[0].id());
+ }
+
+ resetSearch() {
+ this.providerSearchForm.clearSearch();
+ setTimeout(x => this.providerSearchForm.submitSearch());
+ }
+
+ doSearch(search: AcqProviderSearch) {
+ setTimeout(() => {
+ this.providerSearch.setSearch(search);
+ this.providerResultsGrid.reload();
+ });
+ }
+}
--- /dev/null
+import {Injectable} from '@angular/core';
+import {Observable} from 'rxjs';
+import {Router, Resolve, RouterStateSnapshot,
+ ActivatedRouteSnapshot, CanDeactivate} from '@angular/router';
+import {ProviderRecordService} from './provider-record.service';
+
+@Injectable()
+export class ProviderResolver implements Resolve<Promise<any[]>> {
+
+ savedId: number = null;
+
+ constructor(
+ private router: Router,
+ private providerRecord: ProviderRecordService,
+ ) {}
+
+ resolve(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot): Promise<any[]> {
+
+ console.debug('ProviderResolver:resolve()');
+
+ const id = Number(route.paramMap.get('id'));
+
+ if (this.savedId !== null && this.savedId === id) {
+ // don't refetch
+ return Promise.all([
+ Promise.resolve(),
+ ]);
+ } else {
+ this.savedId = id;
+ return Promise.all([
+ this.providerRecord.fetch(id).then(
+ ok => {
+ console.debug(this.providerRecord.current());
+ },
+ err => {
+ this.router.navigate(['/staff', 'acq', 'provider']);
+ }
+ ),
+ ]);
+ }
+ }
+
+}
+
+// following example of https://www.concretepage.com/angular-2/angular-candeactivate-guard-example
+export interface DeactivationGuarded {
+ canDeactivate(): Observable<boolean> | Promise<boolean> | boolean;
+}
+
+@Injectable()
+export class CanLeaveAcqProviderGuard implements CanDeactivate<DeactivationGuarded> {
+ canDeactivate(component: DeactivationGuarded): Observable<boolean> | Promise<boolean> | boolean {
+ return component.canDeactivate ? component.canDeactivate() : true;
+ }
+}
--- /dev/null
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {AcqProviderComponent} from './acq-provider.component';
+import {ProviderResolver, CanLeaveAcqProviderGuard} from './resolver.service';
+
+const routes: Routes = [
+ { path: '',
+ component: AcqProviderComponent,
+ runGuardsAndResolvers: 'always'
+ },
+ { path: ':id',
+ component: AcqProviderComponent,
+ resolve: { providerResolver : ProviderResolver },
+ runGuardsAndResolvers: 'always'
+ },
+ { path: ':id/:tab',
+ component: AcqProviderComponent,
+ resolve: { providerResolver : ProviderResolver },
+ canDeactivate: [CanLeaveAcqProviderGuard],
+ runGuardsAndResolvers: 'always'
+ }
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule],
+ providers: [ProviderResolver, CanLeaveAcqProviderGuard]
+})
+
+export class AcqProviderRoutingModule {}
--- /dev/null
+#acq-provider-summary-pane {
+ border-radius: 0px 0px 7px 7px;
+ background-color: rgb(247, 247, 247);
+ box-shadow: 1px 2px 3px -1px rgba(0, 0, 0, .2);
+}
+.row { border-bottom: dotted thin; }
+.row.provider_id { }
+.row.provider_name { }
+.row.provider_code { }
+.row.provider_owner { }
+.row.provider_currency_type { }
+.row.provider_holding_tag { display: none; }
+.row.provider_addresses { display: none; }
+.row.provider_san { }
+.row.provider_edi_default { }
+.row.provider_active { }
+.row.provider_prepayment_required { display: none; }
+.row.provider_url { }
+.row.provider_email { display: none; }
+.row.provider_phone { display: none; }
+.row.provider_fax_phone { display: none; }
+.row.provider_default_claim_policy { display: none; }
+.row.provider_default_copy_count { display: none; }
+.row.provider_contacts { }
+.provider_contact_role { font-style: italic; }
+.provider_primary_contact { color: red; }
+.row.provider_provider_notes { display: none; }
--- /dev/null
+<button class="btn" *ngIf="provider_id" [hidden]="!collapsed" (click)="toggleCollapse()" type="submit" i18n><span class="material-icons">expand_more</span></button>
+<button class="btn" *ngIf="provider_id" [hidden]="collapsed" (click)="toggleCollapse()" type="submit" i18n><span class="material-icons">expand_less</span></button>
+
+<div id="acq-provider-summary-pane" [hidden]="!provider_id || collapsed" class="pl-3 pr-3 pt-3 pb-3 mb-3">
+
+<ng-template #errorStrTmpl i18n>Provider Deletion Failed</ng-template>
+<eg-string #deleteSuccessString i18n-text text="Successfully deleted provider."></eg-string>
+<eg-string #errorString [template]="errorStrTmpl"></eg-string>
+
+<eg-confirm-dialog #delConfirm
+ i18n-dialogTitle i18n-dialogBody
+ dialogTitle="Confirm Delete"
+ dialogBody="Delete Provider {{provider ? provider.code() : ''}}?">
+</eg-confirm-dialog>
+
+<div class="row provider_name">
+<div class="col">{{provider_name_label}}</div>
+<div class="col">{{provider_name}}</div>
+</div>
+
+<div class="row provider_code">
+<div class="col">{{provider_code_label}}</div>
+<div class="col">{{provider_code}}</div>
+</div>
+
+<div class="row provider_owner">
+<div class="col">{{provider_owner_label}}</div>
+<div class="col">{{provider_owner}}</div>
+</div>
+
+<div class="row provider_id">
+<div class="col">{{provider_id_label}}</div>
+<div class="col">{{provider_id}}</div>
+</div>
+
+<div class="row provider_currency_type">
+<div class="col">{{provider_currency_type_label}}</div>
+<div class="col">{{provider_currency_type}}</div>
+</div>
+
+<div class="row provider_contacts">
+<div class="col">{{provider_contacts_label}}</div>
+<div class="col">
+ <div *ngFor="let contact of provider_contacts">
+ <span *ngIf="contact.role()"><span class="provider_contact_role">{{contact.role()}} : </span><span>{{contact.name()}}</span></span>
+ <span *ngIf="!contact.role()">{{contact.name()}}</span>
+ <span *ngIf="contact._is_primary"><span class="provider_primary_contact"> (primary)</span></span>
+ </div>
+</div>
+</div>
+
+<div class="row provider_san_tag">
+<div class="col">{{provider_san_label}}</div>
+<div class="col">{{provider_san}}</div>
+</div>
+
+<div class="row provider_edi_default">
+<div class="col">{{provider_edi_default_label}}</div>
+<div class="col">{{provider_edi_default}}</div>
+</div>
+
+<div class="row provider_url">
+<div class="col">{{provider_url_label}}</div>
+<div class="col" *ngIf="provider_url"><a target="_blank" href="{{provider_url}}">{{provider_url}}</a></div>
+</div>
+
+<div class="row provider_holding_tag">
+<div class="col">{{provider_holding_tag_label}}</div>
+<div class="col">{{provider_holding_tag}}</div>
+</div>
+
+<div class="row provider_addresses">
+<div class="col">{{provider_addresses_label}}</div>
+<div class="col">{{provider_addresses}}</div>
+</div>
+
+<div class="row provider_active">
+<div class="col">{{provider_active_label}}</div>
+<div class="col"><eg-bool [value]="provider_active == 't'"></eg-bool></div>
+</div>
+
+<div class="row provider_prepayment_required">
+<div class="col">{{provider_prepayment_required_label}}</div>
+<div class="col">{{provider_prepayment_required}}</div>
+</div>
+
+<div class="row provider_email">
+<div class="col">{{provider_email_label}}</div>
+<div class="col">{{provider_email}}</div>
+</div>
+
+<div class="row provider_phone">
+<div class="col">{{provider_phone_label}}</div>
+<div class="col">{{provider_phone}}</div>
+</div>
+
+<div class="row provider_fax_phone">
+<div class="col">{{provider_fax_phone_label}}</div>
+<div class="col">{{provider_fax_phone}}</div>
+</div>
+
+<div class="row provider_default_claim_policy">
+<div class="col">{{provider_default_claim_policy_label}}</div>
+<div class="col">{{provider_default_claim_policy}}</div>
+</div>
+
+<div class="row provider_default_copy_count">
+<div class="col">{{provider_default_copy_count_label}}</div>
+<div class="col">{{provider_default_copy_count}}</div>
+</div>
+
+<div class="row provider_provider_notes">
+<div class="col">{{provider_provider_notes_label}}</div>
+<div class="col">{{provider_provider_notes}}</div>
+</div>
+
+</div>
+
+<button class="btn btn-primary"
+ [hidden]="collapsed"
+ *ngIf="provider_id"
+ [disabled]="!canDeleteProvider()"
+ (click)="deleteProvider()"
+ type="submit" i18n>
+Delete Provider</button>
--- /dev/null
+import {Component, OnInit, AfterViewInit, Input, Output, EventEmitter, ViewChild} from '@angular/core';
+import {Router} from '@angular/router';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+import {AuthService} from '@eg/core/auth.service';
+import {ProviderRecord, ProviderRecordService} from './provider-record.service';
+
+@Component({
+ selector: 'eg-acq-provider-summary-pane',
+ styleUrls: ['summary-pane.component.css'],
+ templateUrl: './summary-pane.component.html'
+})
+
+export class AcqProviderSummaryPaneComponent implements OnInit, AfterViewInit {
+
+ @ViewChild('deleteSuccessString', { static: true }) deleteSuccessString: StringComponent;
+
+ collapsed = false;
+
+ provider_id = '';
+ provider_name = '';
+ provider_code = '';
+ provider_owner = '';
+ provider_currency_type = '';
+ provider_holding_tag = '';
+ provider_addresses = '';
+ provider_san = '';
+ provider_edi_default = '';
+ provider_active = '';
+ provider_prepayment_required = '';
+ provider_url = '';
+ provider_email = '';
+ provider_phone = '';
+ provider_fax_phone = '';
+ provider_default_claim_policy = '';
+ provider_default_copy_count = '';
+ provider_contacts = '';
+ provider_provider_notes = '';
+
+ provider_id_label;
+ provider_name_label;
+ provider_code_label;
+ provider_owner_label;
+ provider_currency_type_label;
+ provider_holding_tag_label;
+ provider_addresses_label;
+ provider_san_label;
+ provider_edi_default_label;
+ provider_active_label;
+ provider_prepayment_required_label;
+ provider_url_label;
+ provider_email_label;
+ provider_phone_label;
+ provider_fax_phone_label;
+ provider_default_claim_policy_label;
+ provider_default_copy_count_label;
+ provider_contacts_label;
+ provider_provider_notes_label;
+
+ @Input() providerId: any;
+ @ViewChild('errorString', { static: true }) errorString: StringComponent;
+ @ViewChild('delConfirm', { static: true }) delConfirm: ConfirmDialogComponent;
+ @Output() summaryToggled: EventEmitter<boolean> = new EventEmitter<boolean>();
+
+ provider: IdlObject;
+ provRec: ProviderRecord;
+
+ constructor(
+ private router: Router,
+ private pcrud: PcrudService,
+ private idl: IdlService,
+ private org: OrgService,
+ private toast: ToastService,
+ private auth: AuthService,
+ private prov: ProviderRecordService,
+ ) {}
+
+ ngOnInit() {
+ this.provider_id_label = this.idl.classes['acqpro'].field_map['id'].label;
+ this.provider_name_label = this.idl.classes['acqpro'].field_map['name'].label;
+ this.provider_code_label = this.idl.classes['acqpro'].field_map['code'].label;
+ this.provider_owner_label = this.idl.classes['acqpro'].field_map['owner'].label;
+ this.provider_currency_type_label = this.idl.classes['acqpro'].field_map['currency_type'].label;
+ this.provider_holding_tag_label = this.idl.classes['acqpro'].field_map['holding_tag'].label;
+ this.provider_addresses_label = this.idl.classes['acqpro'].field_map['addresses'].label;
+ this.provider_san_label = this.idl.classes['acqpro'].field_map['san'].label;
+ this.provider_edi_default_label = this.idl.classes['acqpro'].field_map['edi_default'].label;
+ this.provider_active_label = this.idl.classes['acqpro'].field_map['active'].label;
+ this.provider_prepayment_required_label = this.idl.classes['acqpro'].field_map['prepayment_required'].label;
+ this.provider_url_label = this.idl.classes['acqpro'].field_map['url'].label;
+ this.provider_email_label = this.idl.classes['acqpro'].field_map['email'].label;
+ this.provider_phone_label = this.idl.classes['acqpro'].field_map['phone'].label;
+ this.provider_fax_phone_label = this.idl.classes['acqpro'].field_map['fax_phone'].label;
+ this.provider_default_claim_policy_label = this.idl.classes['acqpro'].field_map['default_claim_policy'].label;
+ this.provider_default_copy_count_label = this.idl.classes['acqpro'].field_map['default_copy_count'].label;
+ this.provider_contacts_label = this.idl.classes['acqpro'].field_map['contacts'].label;
+ this.provider_provider_notes_label = this.idl.classes['acqpro'].field_map['provider_notes'].label;
+ }
+
+ ngAfterViewInit() {
+ if (this.providerId) {
+ this.update(this.providerId);
+ }
+ }
+
+ update(newProvider: any) {
+ function no_provider() {
+ // FIXME: empty the pane or keep last summarized view?
+ this.provider_id = '';
+ this.provider_name = '';
+ this.provider_code = '';
+ this.provider_owner = '';
+ this.provider_currency_type = '';
+ this.provider_holding_tag = '';
+ this.provider_addresses = '';
+ this.provider_san = '';
+ this.provider_edi_default = '';
+ this.provider_active = '';
+ this.provider_prepayment_required = '';
+ this.provider_url = '';
+ this.provider_email = '';
+ this.provider_phone = '';
+ this.provider_fax_phone = '';
+ this.provider_default_claim_policy = '';
+ this.provider_default_copy_count = '';
+ this.provider_contacts = '';
+ this.provider_provider_notes = '';
+ }
+
+ if (newProvider) {
+ const providerRecord = this.prov.currentProviderRecord();
+ const provider = providerRecord.record;
+ if (provider) {
+ this.provRec = providerRecord;
+ this.provider = provider;
+ this.provider_id = provider.id();
+ this.provider_name = provider.name();
+ this.provider_code = provider.code();
+ this.provider_owner = this.org.get(provider.owner()).shortname();
+ this.provider_currency_type = provider.currency_type() ? provider.currency_type().label() : '';
+ this.provider_holding_tag = provider.holding_tag();
+ this.provider_addresses = provider.addresses();
+ this.provider_san = provider.san();
+ if (typeof provider.edi_default() === 'object') {
+ this.provider_edi_default = provider.edi_default() ? provider.edi_default().label() : '';
+ } else {
+ // not fleshed, presumably because user doesn't have
+ // permission to retrieve EDI accounts
+ this.provider_edi_default = '';
+ }
+ this.provider_active = provider.active();
+ this.provider_prepayment_required = provider.prepayment_required();
+ this.provider_url = provider.url();
+ this.provider_email = provider.email();
+ this.provider_phone = provider.phone();
+ this.provider_fax_phone = provider.fax_phone();
+ this.provider_default_claim_policy = provider.default_claim_policy();
+ this.provider_default_copy_count = provider.default_copy_count();
+ this.provider_contacts = provider.contacts();
+ this.provider_provider_notes = provider.provider_notes();
+ } else {
+ this.provider = null;
+ no_provider();
+ }
+ } else {
+ no_provider();
+ }
+ }
+
+ deleteProvider() {
+ this.delConfirm.open().subscribe(confirmed => {
+ if (!confirmed) { return; }
+
+ this.pcrud.remove(this.provider)
+ .subscribe(
+ ok2 => {
+ this.deleteSuccessString.current()
+ .then(str => this.toast.success(str));
+ this.router.navigate(['/staff', 'acq', 'provider']);
+ },
+ err => {
+ this.errorString.current()
+ .then(str => this.toast.danger(str));
+ },
+ () => {
+ console.log('deleteProvider, what is this?');
+ }
+ );
+ });
+
+ }
+
+ canDeleteProvider() {
+ if (this.provider && this.provider.id()) {
+ return this.provRec.canAdmin && this.provRec.canDelete;
+ } else {
+ return false;
+ }
+ }
+
+ toggleCollapse() {
+ this.collapsed = ! this.collapsed;
+ this.summaryToggled.emit(this.collapsed);
+ }
+
+}
{ path: 'search',
loadChildren: () =>
import('./search/acq-search.module').then(m => m.AcqSearchModule)
+ },
+ { path: 'provider',
+ loadChildren: () =>
+ import('./provider/acq-provider.module').then(m => m.AcqProviderModule)
}
];
</a>
</ng-template>
<ng-template #providerTmpl let-invoice="row">
- <a href="/eg/staff/admin/acq/conify/provider/{{invoice.provider().id()}}"
+ <a routerLink="/staff/acq/provider/{{invoice.provider().id()}}"
target="_blank">
{{invoice.provider().code()}}
</a>
</ng-template>
<ng-template #shipperTmpl let-invoice="row">
- <a href="/eg/staff/admin/acq/conify/provider/{{invoice.shipper().id()}}"
+ <a routerLink="/staff/acq/provider/{{invoice.shipper().id()}}"
target="_blank">
{{invoice.shipper().code()}}
</a>
</ng-template>
<ng-template #providerTmpl let-lineitem="row">
- <a *ngIf="lineitem.provider()" href="/eg/staff/admin/acq/conify/provider/{{lineitem.provider().id()}}"
+ <a *ngIf="lineitem.provider()" routerLink="/staff/acq/provider/{{lineitem.provider().id()}}"
target="_blank">
{{lineitem.provider().code()}}
</a>
</ng-template>
<ng-template #providerTmpl let-purchaseorder="row">
- <a href="/eg/staff/admin/acq/conify/provider/{{purchaseorder.provider().id()}}"
+ <a routerLink="/staff/acq/provider/{{purchaseorder.provider().id()}}"
target="_blank">
{{purchaseorder.provider().code()}}
</a>
<eg-link-table-link i18n-label label="Line Item MARC Attribute Definitions"
routerLink="/staff/admin/acq/lineitem_marc_attr_definition"></eg-link-table-link>
<eg-link-table-link i18n-label label="Providers"
- url="/eg/staff/admin/acq/conify/provider"></eg-link-table-link>
- <!-- TODO
- routerLink="/staff/admin/acq/provider"></eg-link-table-link>
- -->
+ routerLink="/staff/acq/provider"></eg-link-table-link>
</eg-link-table>
</div>
<span i18n>General Search</span>
</a>
<div class="dropdown-divider"></div>
+ <a class="dropdown-item"
+ routerLink="/staff/acq/provider">
+ <span class="material-icons" aria-hidden="true">search</span>
+ <span i18n>Provider Search</span>
+ </a>
+ <div class="dropdown-divider"></div>
<a class="dropdown-item"
routerLink="/staff/acq/search/selectionlists">
<span class="material-icons" aria-hidden="true">view_list</span>
onClick="location.href = '[% ctx.base_path %]/acq/funding_source/list';">[% l('Funding Sources') %]</div>
<div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy"
onClick="location.href = '[% ctx.base_path %]/conify/global/acq/provider';">[% l('Providers') %]</div>
+ <!-- what about this one? onClick="location.href = '/eg2/staff/acq/provider';">[% l('Providers') %]</div> -->
<div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy"
onClick="location.href = '[% ctx.base_path %]/acq/currency_type/list';">[% l('Currency Types') %]</div>
<div dojoType="dijit.MenuItem" iconClass="dijitEditorIcon dijitEditorIconCopy"
,[ l('Invoice Payment Method'), "./admin/acq/conify/invoice_payment_method" ]
,[ l('Line Item Alerts'), "./admin/acq/conify/lineitem_alert" ]
,[ l('Line Item MARC Attribute Definitions'), "./admin/acq/conify/lineitem_marc_attr_def" ]
- ,[ l('Providers'), "./admin/acq/conify/provider" ]
+ ,[ l('Providers'), "/eg2/admin/acq/provider" ]
];
USE table(interfaces, cols=3);
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
[% l('General Search') %]
</a>
+ <li class="divider"></li>
+ <li>
+ <a href="/eg2/staff/acq/provider" target="_self">
+ <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
+ [% l('Provider Search') %]
+ </a>
</li>
<li class="divider"></li>
<li>