LP1859701 Cash Reports
authorZavier Banks <zbanks@catalyte.io>
Thu, 9 Jan 2020 18:45:26 +0000 (18:45 +0000)
committerMichele Morgan <mmorgan@noblenet.org>
Fri, 28 Oct 2022 19:44:38 +0000 (15:44 -0400)
Migrating the DOJO UI for Cash Reports into
Angular. The disabling of the different orgs
is dependent on bug #1863168.

Signed-off-by: Zavier Banks <zbanks@catalyte.io>
Signed-off-by: Jason Etheridge <jason@EquinoxOLI.org>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Michele Morgan <mmorgan@noblenet.org>
Open-ILS/src/eg2/src/app/staff/admin/local/admin-local-splash.component.html
Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/routing.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/user-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/user-dialog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/local/routing.module.ts

index e527e3b..92d22f0 100644 (file)
@@ -15,8 +15,8 @@
       routerLink="/staff/admin/local/container/carousel_org_unit"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Carousels"
       routerLink="/staff/admin/local/container/carousel"></eg-link-table-link>
-    <eg-link-table-link i18n-label label="Cash Reports" 
-      url="/eg/staff/admin/local/money/cash_reports"></eg-link-table-link>
+    <eg-link-table-link i18n-label label="Cash Reports"
+      routerLink="/staff/admin/local/money/cash_reports"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Circulation Limit Sets" 
       routerLink="/staff/admin/local/config/circ_limit_set"></eg-link-table-link>
     <eg-link-table-link i18n-label label="Circulation Policies" 
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.component.html
new file mode 100644 (file)
index 0000000..3a44eb8
--- /dev/null
@@ -0,0 +1,81 @@
+<eg-title i18n-prefix prefix="Cash Reports"></eg-title>
+<eg-staff-banner bannerText="Cash Reports" i18n-bannerText>
+</eg-staff-banner>
+
+<div class="mb-5 text-center">
+    <div>
+        <div class="row col-lg-12 ml-auto mr-auto">
+            <div class="input-group col-lg-3 ml-auto mr-auto">
+                <div class="input-group-prepend col-lg-12">
+                    <div class="input-group-text" i18n>Start Date</div> 
+                    <eg-date-select [initialDate]="today" (onChangeAsYmd)="onStartDateChange($event)"></eg-date-select>
+                </div>
+            </div>
+            <div class="input-group col-lg-3 ml-auto mr-auto">
+                <div class="input-group-prepend col-lg-12">
+                    <div class="input-group-text" i18n>End Date</div> 
+                    <eg-date-select [initialDate]="today" (onChangeAsYmd)="onEndDateChange($event)"></eg-date-select>
+                </div>
+            </div>
+            <div class="input-group col-lg-4">
+                <div class="input-group-prepend col-lg-6 ml-auto mr-auto">
+                    <div class="input-group-text" i18n>View reports for</div>
+                    <eg-org-select [applyDefault]="true" [disableOrgs]="disabledOrgs" (onChange)="onOrgChange($event)"></eg-org-select>
+                </div>
+                <button class="btn btn-primary col-lg-2" (click)="searchForData(startDate, endDate)">Submit</button>
+            </div>
+        </div>
+    </div>
+</div>
+<ngb-tabset [destroyOnHide]="false" (tabChange)="eraseUserGrid()">
+    <ngb-tab title="Desk Payments">
+      <ng-template ngbTabContent>
+        <div class="mb-5">
+            <eg-grid #deskPaymentGrid
+                [disableSelect]="true"
+                [disablePaging]="true"
+                [dataSource]="deskPaymentDataSource"
+                [sortable]="false">
+                <eg-grid-column label="Workstation" name="workstation" [index]="true" i18n-label></eg-grid-column>
+                <eg-grid-column label="Cash Payment" name="cash_payment" i18n-label></eg-grid-column>
+                <eg-grid-column label="Check Payment" name="check_payment" i18n-label></eg-grid-column>
+                <eg-grid-column label="Credit Card Payment" name="credit_card_payment" i18n-label></eg-grid-column>
+            </eg-grid>
+        </div>
+      </ng-template>
+    </ngb-tab>
+    <ngb-tab title="User Payments">
+        <ng-template ngbTabContent>
+            <div class="mb-3">
+                <eg-grid #userPaymentGrid
+                    (onRowActivate) = "onRowActivate($event)"
+                    [disableSelect]="true"
+                    [disablePaging]="true"
+                    [dataSource]="userPaymentDataSource"
+                    [sortable]="false">
+                    <eg-grid-column label="User" name="usr" [index]="true" i18n-label></eg-grid-column>
+                    <eg-grid-column label="Credit Payment" name="credit_payment" i18n-label></eg-grid-column>
+                    <eg-grid-column label="Forgive Payment" name="forgive_payment" i18n-label></eg-grid-column>
+                    <eg-grid-column label="Work Payment" name="work_payment" i18n-label></eg-grid-column>
+                    <eg-grid-column label="Goods Payment" name="goods_payment" i18n-label></eg-grid-column>
+                </eg-grid>
+            </div>
+        </ng-template>
+      </ngb-tab>
+</ngb-tabset>
+
+<eg-user-dialog #userDialog>
+    <ng-container *ngIf="userDataSource.data && userDataSource.data.length>0">
+        <eg-grid #userGrid
+            [disableSelect]="true"
+            [disablePaging]="true"
+            [dataSource]="userDataSource"
+            [sortable]="false">
+            <eg-grid-column label="Card" name="card" [index]="true" i18n-label></eg-grid-column>
+            <eg-grid-column label="Email" name="email" i18n-label></eg-grid-column>
+            <eg-grid-column label="First Name" name="first_given_name" i18n-label></eg-grid-column>
+            <eg-grid-column label="Family Name" name="family_name" i18n-label></eg-grid-column>
+            <eg-grid-column label="Home OU" name="home_ou" i18n-label></eg-grid-column>
+        </eg-grid>
+    </ng-container>
+</eg-user-dialog>
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.component.ts
new file mode 100644 (file)
index 0000000..065a928
--- /dev/null
@@ -0,0 +1,178 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridDataSource, GridColumn, GridRowFlairEntry} from '@eg/share/grid/grid';
+import {IdlService} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {OrgService} from '@eg/core/org.service';
+import {UserDialogComponent} from './user-dialog.component';
+
+class DeskTotals {
+    cash_payment = 0;
+    check_payment = 0;
+    credit_card_payment = 0;
+};
+
+class UserTotals {
+    forgive_payment = 0;
+    work_payment = 0;
+    credit_payment = 0;
+    goods_payment = 0;
+};
+
+@Component({
+    templateUrl: './cash-reports.component.html'
+})
+export class CashReportsComponent implements OnInit {
+    initDone = false;
+    deskPaymentDataSource: GridDataSource = new GridDataSource();
+    userPaymentDataSource: GridDataSource = new GridDataSource();
+    userDataSource: GridDataSource = new GridDataSource();
+    deskIdlClass = 'mwps';
+    userIdlClass = 'mups';
+    selectedOrg = this.org.get(this.auth.user().ws_ou());
+    today = new Date();
+    startDate = `${this.today.getFullYear()}-${String(this.today.getMonth() + 1).padStart(2, '0')}-${String(this.today.getDate()).padStart(2, '0')}`;
+    endDate = `${this.today.getFullYear()}-${String(this.today.getMonth() + 1).padStart(2, '0')}-${String(this.today.getDate()).padStart(2, '0')}`;
+    deskTotals = new DeskTotals();
+    userTotals = new UserTotals();
+    disabledOrgs = [];
+
+    // Default sort field, used when no grid sorting is applied.
+    @Input() sortField: string;
+    @ViewChild('userDialog', { static: true }) userDialog: UserDialogComponent;
+    @ViewChild('deskPaymentGrid', { static: true }) deskPaymentGrid: GridComponent;
+    @ViewChild('userPaymentGrid', { static: true }) userPaymentGrid: GridComponent;
+    @ViewChild('userGrid', { static: true }) userGrid: GridComponent;
+
+    constructor(
+        private idl: IdlService,
+        private net: NetService,
+        private org: OrgService,
+        private auth: AuthService){}
+
+    ngOnInit() {
+        this.disabledOrgs = this.getFilteredOrgList();
+        this.searchForData(this.startDate, this.endDate);
+    }
+
+    onRowActivate(userObject) {
+        if(userObject.user && this.userDataSource.data.length === 0) {
+            this.userDataSource.data = [userObject.user];
+            this.showUserInformation();
+        } else {
+            this.eraseUserGrid();
+        }
+    }
+
+    showUserInformation() {
+        return new Promise((resolve, reject) => {
+            this.userDialog.open({size: 'lg'}).subscribe(
+                result => {
+                    resolve(result);
+                },
+                error => {
+                    reject(error);
+                }
+            );
+        });
+    }
+
+    searchForData(start, end) {
+        this.userDataSource.data = [];
+        this.fillGridData(this.deskIdlClass, 'deskPaymentDataSource',
+            this.net.request(
+                'open-ils.circ',
+                'open-ils.circ.money.org_unit.desk_payments',
+                this.auth.token(), this.selectedOrg.id(), start, end));
+        this.fillGridData(this.userIdlClass,'userPaymentDataSource',
+            this.net.request(
+                'open-ils.circ',
+                'open-ils.circ.money.org_unit.user_payments',
+                this.auth.token(), this.selectedOrg.id(), start, end));
+    }
+
+    fillGridData(idlClass, dataSource, data) {
+        data.subscribe((result) => {
+            let dataForTotal;
+            if(idlClass === this.deskIdlClass) {
+                dataForTotal = this.getDeskTotal(result);
+            } else if(idlClass === this.userIdlClass) {
+                dataForTotal = this.getUserTotal(result);
+                result.forEach((userObject, index) => {
+                    result[index].user = userObject.usr();
+                    result[index].usr(userObject.usr().usrname())
+                });
+            }
+            if(result.length > 0) {
+                result.push(dataForTotal);
+            }
+            this[dataSource].data = result;
+            this.eraseUserGrid();
+        });
+    }
+
+    eraseUserGrid() {
+        this.userDataSource.data = [];
+    }
+
+    getDeskTotal(idlObjects) {
+        this.deskTotals = new DeskTotals();
+        if(idlObjects.length > 0) {
+            let idlObjectFormat = this.idl.create("mwps")
+            idlObjects.forEach((idlObject) => {
+                this.deskTotals['cash_payment'] += parseFloat(idlObject.cash_payment());
+                this.deskTotals['check_payment'] += parseFloat(idlObject.check_payment());
+                this.deskTotals['credit_card_payment'] += parseFloat(idlObject.credit_card_payment());
+            });
+            idlObjectFormat.cash_payment(this.deskTotals['cash_payment']);
+            idlObjectFormat.check_payment(this.deskTotals['check_payment']);
+            idlObjectFormat.credit_card_payment(this.deskTotals['credit_card_payment']);
+            return idlObjectFormat;
+        }
+    }
+
+    getUserTotal(idlObjects) {
+        this.userTotals = new UserTotals();
+        if(idlObjects.length > 0) {
+            let idlObjectFormat = this.idl.create("mups")
+            idlObjects.forEach((idlObject, index) => {
+                this.userTotals['forgive_payment'] += parseFloat(idlObject.forgive_payment());
+                this.userTotals['work_payment'] += parseFloat(idlObject.work_payment());
+                this.userTotals['credit_payment'] += parseFloat(idlObject.credit_payment());
+                this.userTotals['goods_payment'] += parseFloat(idlObject.goods_payment());
+                this.userDataSource.data = idlObjects[index].usr();
+            });
+            idlObjectFormat.forgive_payment(this.userTotals['forgive_payment']);
+            idlObjectFormat.work_payment(this.userTotals['work_payment']);
+            idlObjectFormat.credit_payment(this.userTotals['credit_payment']);
+            idlObjectFormat.goods_payment(this.userTotals['goods_payment']);
+            return idlObjectFormat;
+        }
+    }
+
+    getFilteredOrgList() {
+        let orgFilter = {canHaveUsers:false}
+        return this.org.filterList(orgFilter, true);
+    }
+
+    // getOrgIds(orgs) {
+    //     orgs.forEach((element) => {
+    //         this.disabledOrgs.push(element.id());
+    //     });
+    //     return orgs;
+    // }
+
+    onStartDateChange(date) {
+        this.startDate = date;
+    }
+
+    onEndDateChange(date) {
+        this.endDate = date;
+    }
+
+    onOrgChange(org) {
+        this.selectedOrg = org;
+        this.searchForData(this.startDate, this.endDate);
+    }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/cash-reports.module.ts
new file mode 100644 (file)
index 0000000..dec49bb
--- /dev/null
@@ -0,0 +1,25 @@
+import {NgModule} from '@angular/core';
+import {TreeModule} from '@eg/share/tree/tree.module';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {CashReportsComponent} from './cash-reports.component';
+import {UserDialogComponent} from './user-dialog.component'
+import {CashReportsRoutingModule} from './routing.module'
+
+@NgModule({
+  declarations: [
+    CashReportsComponent,
+    UserDialogComponent
+  ],
+  imports: [
+    StaffCommonModule,
+    TreeModule,
+    CashReportsRoutingModule
+  ],
+  exports: [
+  ],
+  providers: [
+  ]
+})
+
+export class CashReportsModule {
+}
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/routing.module.ts
new file mode 100644 (file)
index 0000000..65f7620
--- /dev/null
@@ -0,0 +1,15 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {CashReportsComponent} from './cash-reports.component';
+
+const routes: Routes = [{
+    path: '',
+    component: CashReportsComponent
+}];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class CashReportsRoutingModule {}
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/user-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/user-dialog.component.html
new file mode 100644 (file)
index 0000000..764b4a6
--- /dev/null
@@ -0,0 +1,13 @@
+
+<ng-template #dialogContent>
+    <div class="modal-header bg-info">
+        <h4 class="modal-title" i18n>User Information</h4>
+
+    </div>
+    <div class="modal-body">
+        <ng-content></ng-content>
+    </div>
+    <div class="modal-footer">
+        <button class="btn btn-success" (click)="closeEditor()" i18n>Close</button>
+    </div>
+</ng-template>
\ No newline at end of file
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/user-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/cash-reports/user-dialog.component.ts
new file mode 100644 (file)
index 0000000..480e720
--- /dev/null
@@ -0,0 +1,29 @@
+import {Component, OnInit, Input, ViewChild} from '@angular/core';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {Observable} from 'rxjs';
+import { DialogComponent } from '@eg/share/dialog/dialog.component';
+
+@Component({
+    selector:'eg-user-dialog',
+    templateUrl: './user-dialog.component.html'
+})
+export class UserDialogComponent extends DialogComponent implements OnInit {
+
+    ngOnInit() {}
+
+    constructor(
+        private modal: NgbModal) {
+        super(modal);
+      }
+
+      open(args?: NgbModalOptions): Observable<any> {
+          if (!args) {
+              args = {};
+          }
+          return super.open(args);
+      }
+
+      closeEditor() {
+          this.close();
+      }
+}
index f5bfd13..179817a 100644 (file)
@@ -90,13 +90,18 @@ const routes: Routes = [{
     loadChildren: () => import('./field-documentation/field-documentation.module')
       .then(m => m.FieldDocumentationModule)
 }, {
+    path: 'money/cash_reports',
+    loadChildren: '@eg/staff/admin/local/cash-reports/cash-reports.module#CashReportsModule'
+}, {
     path: 'negative-balances',
     loadChildren: () =>
       import('./negative-balances/negative-balances.module').then(m => m.NegativeBalancesModule)
 }, {
     path: ':schema/:table',
     component: BasicAdminPageComponent
-}];
+}
+
+];
 
 @NgModule({
   imports: [RouterModule.forChild(routes)],