funds: finish funding sources tab
authorGalen Charlton <gmc@equinoxinitiative.org>
Sun, 28 Mar 2021 18:05:09 +0000 (14:05 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Sun, 28 Mar 2021 18:05:09 +0000 (14:05 -0400)
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-source-transactions-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-source-transactions-dialog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-sources.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-sources.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funds.component.html
Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funds.module.ts

diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-source-transactions-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-source-transactions-dialog.component.html
new file mode 100644 (file)
index 0000000..a48042c
--- /dev/null
@@ -0,0 +1,80 @@
+<eg-string #successString i18n-text text="Funding Source Update Succeeded"></eg-string>
+<eg-string #updateFailedString i18n-text text="Funding Source Update Failed"></eg-string>
+
+<ng-template #summaryField let-field="field" let-value="value">
+  <div class="col-2">
+    <label style="font-weight: bold" for="fund-{{field}}">{{idlDef.field_map[field].label}}</label>
+  </div>
+  <div class="col-2">
+    <span id="fund-{{field}}">
+      {{value}}
+    </span>
+  </div>
+</ng-template>
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h3 class="modal-title" i18n>Funding Source: {{fundingSource?.name()}}</h3>
+    <button type="button" class="close"
+      i18n-aria-label aria-label="Close" (click)="close()">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body" *ngIf="fundingSource">
+    <ul ngbNav #fundingSourceTransactionsNav="ngbNav" [(activeId)]="activeTab" class="nav-tabs">
+      <li [ngbNavItem]="'credits'">
+        <a ngbNavLink i18n>Credits</a>
+        <ng-template ngbNavContent>
+           <div class="mt-2">
+            <eg-grid #creditsGrid idlClass="acqfscred" [dataSource]="acqfscredDataSource"
+                [sortable]="true" persistKey="acq.funding_source.credit"
+                [filterable]="true" [stickyHeader]="true">
+
+              <eg-grid-toolbar-button label="Apply Credits" i18n-label (onClick)="createCredit(creditsGrid)">
+              </eg-grid-toolbar-button>
+              <eg-grid-column path="amount"></eg-grid-column>
+              <eg-grid-column path="effective_date"></eg-grid-column>
+              <eg-grid-column path="deadline_date"></eg-grid-column>
+              <eg-grid-column path="note"></eg-grid-column>
+              <eg-grid-column path="id" [hidden]="true"></eg-grid-column>
+              <eg-grid-column path="funding_source" [hidden]="true" [filterable]="false"></eg-grid-column>
+            </eg-grid>
+          </div>
+        </ng-template>
+      </li>
+      <li [ngbNavItem]="'allocations'">
+        <a ngbNavLink i18n>Allocations</a>
+        <ng-template ngbNavContent>
+          <div class="mt-2">
+            <eg-grid #allocationsGrid idlClass="acqfa" [dataSource]="acqfaDataSource"
+                [sortable]="true" persistKey="acq.funding_source.fund_allocation"
+                [filterable]="true" [stickyHeader]="true" [cellTextGenerator]="cellTextGenerator">
+
+              <eg-grid-toolbar-button label="Allocate to Fund" i18n-label (onClick)="allocateToFund(allocationsGrid)">
+              </eg-grid-toolbar-button>
+              <ng-template #fundTmpl let-row="row">
+                {{row.fund().code()}} ({{row.fund().year()}}) ({{getOrgShortname(row.fund().org())}})
+              </ng-template>
+              <eg-grid-column path="fund" [cellTemplate]="fundTmpl"></eg-grid-column>
+              <eg-grid-column path="amount"></eg-grid-column>
+              <eg-grid-column path="create_time"></eg-grid-column>
+              <eg-grid-column path="allocator"></eg-grid-column>
+              <eg-grid-column path="note"></eg-grid-column>
+              <eg-grid-column path="id" [hidden]="true"></eg-grid-column>
+              <eg-grid-column path="funding_source" [hidden]="true" [filterable]="false"></eg-grid-column>
+            </eg-grid>
+          </div>
+        </ng-template>
+      </li>
+    </ul>
+    <div [ngbNavOutlet]="fundingSourceTransactionsNav"></div>
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="btn btn-warning"
+      (click)="close()" i18n>Close</button>
+  </div>
+</ng-template>
+
+<eg-fm-record-editor #applyCreditDialog idlClass="acqfscred">
+</eg-fm-record-editor>
+<eg-fm-record-editor #allocateToFundDialog idlClass="acqfa">
+</eg-fm-record-editor>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-source-transactions-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-source-transactions-dialog.component.ts
new file mode 100644 (file)
index 0000000..bce4994
--- /dev/null
@@ -0,0 +1,168 @@
+import {Component, Input, ViewChild, TemplateRef, OnInit} from '@angular/core';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {FormatService} from '@eg/core/format.service';
+import {EventService} from '@eg/core/event.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {GridDataSource, GridCellTextGenerator} from '@eg/share/grid/grid';
+import {Pager} from '@eg/share/util/pager';
+import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import {StringComponent} from '@eg/share/string/string.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {OrgService} from '@eg/core/org.service';
+
+@Component({
+  selector: 'eg-funding-source-transactions-dialog',
+  templateUrl: './funding-source-transactions-dialog.component.html'
+})
+
+export class FundingSourceTransactionsDialogComponent
+  extends DialogComponent implements OnInit {
+
+    @Input() fundingSourceId: number;
+    @Input() activeTab = 'credits';
+    fundingSource: IdlObject;
+    idlDef: any;
+    fieldOrder: any;
+    acqfaDataSource: GridDataSource
+    acqfscredDataSource: GridDataSource
+    cellTextGenerator: GridCellTextGenerator;
+
+    @ViewChild('applyCreditDialog', { static: true }) applyCreditDialog: FmRecordEditorComponent;
+    @ViewChild('allocateToFundDialog', { static: true }) allocateToFundDialog: FmRecordEditorComponent;
+    @ViewChild('successString', { static: true }) successString: StringComponent;
+    @ViewChild('updateFailedString', { static: false }) updateFailedString: StringComponent;
+    constructor(
+        private idl: IdlService,
+        private evt: EventService,
+        private net: NetService,
+        private auth: AuthService,
+        private pcrud: PcrudService,
+        private org: OrgService,
+        private format: FormatService,
+        private toast: ToastService,
+        private modal: NgbModal
+    ) {
+        super(modal);
+    }
+    
+    ngOnInit() {
+        this.cellTextGenerator = {
+            fund: row => {
+                return row.code() + ' (' + row.year() + ') (' +
+                    this.getOrgShortname(row.org()) + ')';
+            }
+        };
+        this.fundingSource = null;
+        this.onOpen$.subscribe(() => this._initRecord());
+        this.idlDef = this.idl.classes['acqfs']
+        this.fieldOrder = 'name,code,year,org,active,currency_type,balance_stop_percentage,balance_warning_percentage,propagate,rollover';
+    }
+
+    private _initRecord() {
+        this.acqfaDataSource = this._getDataSource('acqfa', 'create_time DESC');
+        this.acqfscredDataSource = this._getDataSource('acqfscred', 'effective_date DESC');
+        this.pcrud.retrieve('acqfs', this.fundingSourceId, {}
+        ).subscribe(res => this.fundingSource = res);
+    }
+
+    _getDataSource(idlClass: string, sortField: string): GridDataSource {
+        const gridSource = new GridDataSource();
+
+        gridSource.getRows = (pager: Pager, sort: any[]) => {
+            const orderBy: any = {};
+            if (sort.length) {
+                // Sort specified from grid
+                orderBy[idlClass] = sort[0].name + ' ' + sort[0].dir;
+            } else if (sortField) {
+                // Default sort field
+                orderBy[idlClass] = sortField;
+            }
+
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy,
+            };
+            const reqOps = {
+                fleshSelectors: true,
+            };
+
+            const search: any = new Array();
+            search.push({ funding_source: this.fundingSourceId });
+
+            Object.keys(gridSource.filters).forEach(key => {
+                Object.keys(gridSource.filters[key]).forEach(key2 => {
+                    search.push(gridSource.filters[key][key2]);
+                });
+            });
+
+            return this.pcrud.search(
+                idlClass, search, searchOps, reqOps);
+        };
+
+        return gridSource;
+    }
+
+    formatCurrency(value: any) {
+        return this.format.transform({
+            value: value,
+            datatype: 'money'
+        });
+    }
+
+    createCredit(grid: GridComponent) {
+        const credit = this.idl.create('acqfscred');
+        credit.funding_source(this.fundingSourceId);
+        this.applyCreditDialog.defaultNewRecord = credit;
+        this.applyCreditDialog.mode = 'create';
+        this.applyCreditDialog.hiddenFieldsList = ['id', 'funding_source'];
+        this.applyCreditDialog.fieldOrder = 'amount,note,effective_date,deadline_date';
+        this.applyCreditDialog.open().subscribe(
+             result => {
+                this.successString.current()
+                    .then(str => this.toast.success(str));
+                grid.reload();
+            },
+            error => {
+                this.updateFailedString.current()
+                    .then(str => this.toast.danger(str));
+            }
+        );
+    }
+
+    allocateToFund(grid: GridComponent) {
+        const allocation = this.idl.create('acqfa');
+        allocation.funding_source(this.fundingSourceId);
+        allocation.allocator(this.auth.user().id());
+        this.allocateToFundDialog.defaultNewRecord = allocation;
+        this.allocateToFundDialog.mode = 'create';
+
+        this.allocateToFundDialog.hiddenFieldsList = ['id', 'funding_source', 'allocator', 'create_time'];
+        this.allocateToFundDialog.fieldOrder = 'fund,amount,note';
+        this.allocateToFundDialog.open().subscribe(
+             result => {
+                this.successString.current()
+                    .then(str => this.toast.success(str));
+                grid.reload();
+            },
+            error => {
+                this.updateFailedString.current()
+                    .then(str => this.toast.danger(str));
+            }
+        );
+    }
+
+    getOrgShortname(ou: any) {
+        if (typeof ou === 'object') {
+            return ou.shortname();
+        } else {
+            return this.org.get(ou).shortname();
+        }
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-sources.component.html b/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-sources.component.html
new file mode 100644 (file)
index 0000000..76833a8
--- /dev/null
@@ -0,0 +1,111 @@
+<ng-template #successStrTmpl i18n>{{idlClassDef.label}} Update Succeeded</ng-template>
+<eg-string #successString [template]="successStrTmpl"></eg-string>
+
+<ng-template #updateFailedStrTmpl i18n>Update of {{idlClassDef.label}} failed</ng-template>
+<eg-string #updateFailedString [template]="updateFailedStrTmpl"></eg-string>
+
+<ng-template #deleteFailedStrTmpl i18n>Delete of {{idlClassDef.label}} failed or was not allowed</ng-template>
+<eg-string #deleteFailedString [template]="deleteFailedStrTmpl"></eg-string>
+
+<ng-template #deleteSuccessStrTmpl i18n>{{idlClassDef.label}} Successfully Deleted</ng-template>
+<eg-string #deleteSuccessString [template]="deleteSuccessStrTmpl"></eg-string>
+
+<ng-template #createStrTmpl i18n>{{idlClassDef.label}} Successfully Created</ng-template>
+<eg-string #createString [template]="createStrTmpl"></eg-string>
+
+<ng-template #createErrStrTmpl i18n>Failed to create new {{idlClassDef.label}}</ng-template>
+<eg-string #createErrString [template]="createErrStrTmpl"></eg-string>
+
+<ng-container *ngIf="orgField">
+  <div class="row">
+    <div class="col-lg-6">
+      <ng-container *ngIf="orgField">
+        <eg-org-family-select
+          [limitPerms]="viewPerms" 
+          [selectedOrgId]="contextOrg.id()"
+          [(ngModel)]="searchOrgs"
+          (ngModelChange)="grid.reload()">
+        </eg-org-family-select>
+      </ng-container>
+    </div>
+  </div>
+  <hr/>
+</ng-container>
+
+<!-- idlObject and fieldName applied programmatically -->
+<eg-translate #translator></eg-translate>
+
+<ng-container *ngIf="helpTemplate">
+  <ng-container *ngTemplateOutlet="helpTemplate"></ng-container>
+</ng-container>
+
+<ng-template #configFieldLink let-row="row" let-col="col">
+  <a i18n-title title="Link To {{col.label}}"
+    [attr.href]="configFieldLinkUrl(row, col)">{{configLinkLabel(row, col)}}</a>
+</ng-template>
+
+<eg-grid #grid idlClass="{{idlClass}}" [dataSource]="dataSource" hideFields="{{hideGridFields}}"
+    [sortable]="true" persistKey="{{persistKey}}" autoGeneratedColumnOrder="{{fieldOrder}}"
+    [filterable]="true" [stickyHeader]="true"
+    [cellTextGenerator]="cellTextGenerator">
+  <eg-grid-toolbar-button [disabled]="!canCreate" 
+    label="New {{idlClassDef.label}}" i18n-label (onClick)="createNew()">
+  </eg-grid-toolbar-button>
+  <eg-grid-toolbar-action label="Edit Selected" i18n-label (onClick)="editSelected($event)"
+    [disableOnRows]="notOneSelectedRow">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="Delete Selected" i18n-label (onClick)="deleteSelected($event)"
+    [disableOnRows]="notOneSelectedRow">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="Apply Credit" i18n-label (onClick)="createCredit($event)"
+    [disableOnRows]="notOneSelectedRow">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="Allocate to Fund" i18n-label (onClick)="allocateToFund($event)"
+    [disableOnRows]="notOneSelectedRow">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="View Credits" i18n-label (onClick)="openTransactionsDialog($event, 'credits')"
+    [disableOnRows]="notOneSelectedRow">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="View Allocations" i18n-label (onClick)="openTransactionsDialog($event, 'allocations')"
+    [disableOnRows]="notOneSelectedRow">
+  </eg-grid-toolbar-action>
+
+  <eg-grid-column path="name"></eg-grid-column>
+  <eg-grid-column path="owner"></eg-grid-column>
+  <eg-grid-column path="code"></eg-grid-column>
+  <eg-grid-column path="currency_type"></eg-grid-column>
+  <eg-grid-column path="balance" datatype="money" [filterable]="false" [sortable]="false"
+    i18n-label label="Balance"></eg-grid-column>
+  <eg-grid-column path="total_credits" datatype="money" [filterable]="false" [sortable]="false"
+    i18n-label label="Total Credits"></eg-grid-column>
+  <eg-grid-column path="total_allocations" datatype="money" [filterable]="false" [sortable]="false"
+    i18n-label label="Total Allocations"></eg-grid-column>
+  <eg-grid-column path="id" [hidden]="true"></eg-grid-column>
+
+
+</eg-grid>
+
+<eg-fm-record-editor #editDialog idlClass="{{idlClass}}" 
+    [fieldOptions]="fieldOptions"
+    [fieldOrder]="fieldOrder"
+    [defaultNewRecord]="defaultNewRecord"
+    [preloadLinkedValues]="true"
+    [readonlyFields]="readonlyFields">
+</eg-fm-record-editor>
+
+<eg-funding-source-transactions-dialog #fundingSourceTransactionsDialog></eg-funding-source-transactions-dialog>
+
+<eg-confirm-dialog #confirmDel
+  dialogTitle="Delete?" i18n-dialogTitle
+  dialogBody="Delete funding source?" i18n-dialogBody>
+</eg-confirm-dialog>
+<eg-alert-dialog #alertDialog
+ i18n-dialogBody
+  dialogBody="Funding source cannot deleted as it is in use">
+</eg-alert-dialog>
+
+
+<eg-fm-record-editor #applyCreditDialog idlClass="acqfscred">
+</eg-fm-record-editor>
+<eg-fm-record-editor #allocateToFundDialog idlClass="acqfa">
+</eg-fm-record-editor>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-sources.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/acq/funds/funding-sources.component.ts
new file mode 100644 (file)
index 0000000..4d31a41
--- /dev/null
@@ -0,0 +1,221 @@
+import {Component, Input, ViewChild, OnInit} from '@angular/core';
+import {Location} from '@angular/common';
+import {FormatService} from '@eg/core/format.service';
+import {GridDataSource, GridCellTextGenerator} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {AdminPageComponent} from '@eg/staff/share/admin-page/admin-page.component';
+import {Pager} from '@eg/share/util/pager';
+import {ActivatedRoute} from '@angular/router';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {OrgService} from '@eg/core/org.service';
+import {PermService} from '@eg/core/perm.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {map, mergeMap} from 'rxjs/operators';
+import {StringComponent} from '@eg/share/string/string.component';
+import {Observable, forkJoin, of} from 'rxjs';
+import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+import {FundingSourceTransactionsDialogComponent} from './funding-source-transactions-dialog.component';
+
+@Component({
+    selector: 'eg-funding-sources',
+    templateUrl: './funding-sources.component.html'
+})
+
+export class FundingSourcesComponent extends AdminPageComponent implements OnInit {
+    idlClass = 'acqfs';
+    classLabel: string;
+
+    @ViewChild('grid', { static: true }) grid: GridComponent;
+    @ViewChild('fundingSourceTransactionsDialog', { static: false }) fundingSourceTransactionsDialog: FundingSourceTransactionsDialogComponent;
+    @ViewChild('applyCreditDialog', { static: true }) applyCreditDialog: FmRecordEditorComponent;
+    @ViewChild('allocateToFundDialog', { static: true }) allocateToFundDialog: FmRecordEditorComponent;
+    @ViewChild('alertDialog', {static: false}) private alertDialog: AlertDialogComponent;
+    @ViewChild('confirmDel', { static: true }) confirmDel: ConfirmDialogComponent;
+
+    cellTextGenerator: GridCellTextGenerator;
+    notOneSelectedRow: (rows: IdlObject[]) => boolean;
+
+    constructor(
+        route: ActivatedRoute,
+        ngLocation: Location,
+        format: FormatService,
+        idl: IdlService,
+        org: OrgService,
+        auth: AuthService,
+        pcrud: PcrudService,
+        perm: PermService,
+        toast: ToastService,
+        private net: NetService
+    ) {
+        super(route, ngLocation, format, idl, org, auth, pcrud, perm, toast);
+        this.dataSource = new GridDataSource();
+    }
+
+    ngOnInit() {
+        this.cellTextGenerator = {
+            name: row => row.name()
+        };
+        this.notOneSelectedRow = (rows: IdlObject[]) => (rows.length !== 1);
+        this.fieldOrder = 'name,code,year,org,active,currency_type,balance_stop_percentage,balance_warning_percentage,propagate,rollover';
+        this.defaultNewRecord = this.idl.create('acqfs');
+        this.defaultNewRecord.owner(this.auth.user().ws_ou());
+
+        this.dataSource.getRows = (pager: Pager, sort: any[]) => {
+            const orderBy: any = {};
+            if (sort.length) {
+                // Sort specified from grid
+                orderBy[this.idlClass] = sort[0].name + ' ' + sort[0].dir;
+            } else if (this.sortField) {
+                // Default sort field
+                orderBy[this.idlClass] = this.sortField;
+            }
+
+            const searchOps = {
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy,
+                flesh: 1,
+                flesh_fields: {
+                    acqfs: ['credits','allocations']
+                }
+            };
+            const reqOps = {
+                fleshSelectors: true,
+            };
+
+            if (!this.contextOrg && !Object.keys(this.dataSource.filters).length) {
+                // No org filter -- fetch all rows
+                return this.pcrud.retrieveAll(
+                    this.idlClass, searchOps, reqOps)
+                    .pipe(mergeMap((row) => this.calculateSummary(row)));
+            }
+
+            const search: any = new Array();
+            const orgFilter: any = {};
+
+            if (this.orgField && (this.searchOrgs || this.contextOrg)) {
+                orgFilter[this.orgField] =
+                    this.searchOrgs.orgIds || [this.contextOrg.id()];
+                search.push(orgFilter);
+            }
+
+            Object.keys(this.dataSource.filters).forEach(key => {
+                Object.keys(this.dataSource.filters[key]).forEach(key2 => {
+                    search.push(this.dataSource.filters[key][key2]);
+                });
+            });
+
+            return this.pcrud.search(
+                this.idlClass, search, searchOps, reqOps)
+                .pipe(mergeMap((row) => this.calculateSummary(row)));
+        };
+
+        super.ngOnInit();
+
+        this.classLabel = this.idlClassDef.label;
+        this.includeOrgDescendants = true;
+    }
+
+    calculateSummary(row: IdlObject): Observable<IdlObject> {
+        row['balance'] = 0;
+        row['total_credits'] = 0;
+        row['total_allocations'] = 0;
+
+        row.credits().forEach((c) => row['total_credits'] += Number(c.amount()));
+        row.allocations().forEach((a) => row['total_allocations'] += Number(a.amount()));
+        row['balance'] = row['total_credits'] - row['total_allocations'];
+        return of(row);
+    }
+
+    deleteSelected(rows: IdlObject[]) {
+        if (rows.length > 0) {
+            const id = rows[0].id();
+            let can: boolean = true;
+            forkJoin([
+                this.pcrud.search('acqfa',      { funding_source: id }, { limit: 1 }, { atomic: true }),
+                this.pcrud.search('acqfscred',  { funding_source: id }, { limit: 1 }, { atomic: true }),
+            ]).subscribe(
+                results => {
+                    results.forEach((res) => {
+                        if (res.length > 0) {
+                            can = false;
+                        }
+                    });
+                },
+                err => {},
+                () => {
+                    if (can) {
+                        this.confirmDel.open().subscribe(confirmed => {
+                            if (!confirmed) { return; }
+                            super.deleteSelected([ rows[0] ]);
+                        });
+                    } else {
+                        this.alertDialog.open();
+                    }
+                }
+            );
+        }
+    }
+
+    openTransactionsDialog(rows: IdlObject[], tab: string) {
+        if (rows.length !== 1) { return; }
+        this.fundingSourceTransactionsDialog.fundingSourceId = rows[0].id();
+        this.fundingSourceTransactionsDialog.activeTab = tab;
+        this.fundingSourceTransactionsDialog.open({size: 'xl'}).subscribe(
+            res => {},
+            err => {},
+            () => this.grid.reload()
+        );
+    }
+
+    createCredit(rows: IdlObject[]) {
+        if (rows.length !== 1) { return; }
+        const fundingSourceId = rows[0].id();
+        const credit = this.idl.create('acqfscred');
+        credit.funding_source(fundingSourceId);
+        this.applyCreditDialog.defaultNewRecord = credit;
+        this.applyCreditDialog.mode = 'create';
+        this.applyCreditDialog.hiddenFieldsList = ['id', 'funding_source'];
+        this.applyCreditDialog.fieldOrder = 'amount,note,effective_date,deadline_date';
+        this.applyCreditDialog.open().subscribe(
+             result => {
+                this.successString.current()
+                    .then(str => this.toast.success(str));
+                this.grid.reload();
+            },
+            error => {
+                this.updateFailedString.current()
+                    .then(str => this.toast.danger(str));
+            }
+        );
+    }
+
+    allocateToFund(rows: IdlObject[]) {
+        if (rows.length !== 1) { return; }
+        const fundingSourceId = rows[0].id();
+        const allocation = this.idl.create('acqfa');
+        allocation.funding_source(fundingSourceId);
+        allocation.allocator(this.auth.user().id());
+        this.allocateToFundDialog.defaultNewRecord = allocation;
+        this.allocateToFundDialog.mode = 'create';
+
+        this.allocateToFundDialog.hiddenFieldsList = ['id', 'funding_source', 'allocator', 'create_time'];
+        this.allocateToFundDialog.fieldOrder = 'fund,amount,note';
+        this.allocateToFundDialog.open().subscribe(
+             result => {
+                this.successString.current()
+                    .then(str => this.toast.success(str));
+                this.grid.reload();
+            },
+            error => {
+                this.updateFailedString.current()
+                    .then(str => this.toast.danger(str));
+            }
+        );
+    }
+}
index c4f3f8f..0023e57 100644 (file)
@@ -16,7 +16,7 @@
     <a ngbNavLink i18n>Funding Sources</a>
     <ng-template ngbNavContent>
       <div class="mt-2">
-        <eg-admin-page idlClass="acqfs"></eg-admin-page>
+        <eg-funding-sources></eg-funding-sources>
        </div>
     </ng-template>
   </li>
index cf371e0..a74dadd 100644 (file)
@@ -5,12 +5,16 @@ import {FundsComponent} from './funds.component';
 import {FundsRoutingModule} from './routing.module';
 import {FundsManagerComponent} from './funds-manager.component';
 import {FundDetailsDialogComponent} from './fund-details-dialog.component';
+import {FundingSourcesComponent} from './funding-sources.component';
+import {FundingSourceTransactionsDialogComponent} from './funding-source-transactions-dialog.component';
 
 @NgModule({
   declarations: [
     FundsComponent,
     FundsManagerComponent,
-    FundDetailsDialogComponent
+    FundDetailsDialogComponent,
+    FundingSourcesComponent,
+    FundingSourceTransactionsDialogComponent
   ],
   imports: [
     StaffCommonModule,