From 5786e6c3d29f4a90002a5f4489883c7ad3b1193d Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Fri, 4 Nov 2022 00:22:36 -0600 Subject: [PATCH] Loan account locked management --- .../error-dialog/error-dialog.component.html | 9 +- .../error-dialog/error-dialog.component.ts | 5 +- src/app/system/system.service.ts | 7 + .../loan-locked/loan-locked.component.html | 81 ++++++++++ .../loan-locked/loan-locked.component.scss | 29 ++++ .../loan-locked/loan-locked.component.spec.ts | 25 ++++ .../loan-locked/loan-locked.component.ts | 139 ++++++++++++++++++ .../loan-locked/loan-locked.resolver.spec.ts | 16 ++ .../loan-locked/loan-locked.resolver.ts | 27 ++++ .../checker-inbox-and-tasks.component.html | 4 + src/app/tasks/tasks-routing.module.ts | 10 ++ src/app/tasks/tasks.module.ts | 4 +- src/app/tasks/tasks.service.ts | 8 + 13 files changed, 357 insertions(+), 7 deletions(-) create mode 100644 src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.html create mode 100644 src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.scss create mode 100644 src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.spec.ts create mode 100644 src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.ts create mode 100644 src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.spec.ts create mode 100644 src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.ts diff --git a/src/app/shared/error-dialog/error-dialog.component.html b/src/app/shared/error-dialog/error-dialog.component.html index b2dd0944d..261710a03 100644 --- a/src/app/shared/error-dialog/error-dialog.component.html +++ b/src/app/shared/error-dialog/error-dialog.component.html @@ -1,11 +1,10 @@ -

Error Log

+

{{'Error Log' | translate}}

- -

{{ data }}

- +

{{ data }}

+
- + diff --git a/src/app/shared/error-dialog/error-dialog.component.ts b/src/app/shared/error-dialog/error-dialog.component.ts index 3502ff0e9..800e48608 100644 --- a/src/app/shared/error-dialog/error-dialog.component.ts +++ b/src/app/shared/error-dialog/error-dialog.component.ts @@ -9,11 +9,14 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; }) export class ErrorDialogComponent { + showAsCode = false; /** * @param {MatDialogRef} dialogRef Component reference to dialog. * @param {any} data Provides any data. */ constructor(public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any) { } + @Inject(MAT_DIALOG_DATA) public data: string) { + this.showAsCode = (data.startsWith('
'));
+  }
 
 }
diff --git a/src/app/system/system.service.ts b/src/app/system/system.service.ts
index 775ed0e41..aa9361054 100644
--- a/src/app/system/system.service.ts
+++ b/src/app/system/system.service.ts
@@ -268,6 +268,13 @@ export class SystemService {
     return this.http.put(`/jobs/${jobName}/steps`, payload);
   }
 
+  /**
+   * @returns {Observable} Updates Jobs.
+   */
+  runInlineCOB(jobName: String, payload: any): Observable {
+    return this.http.post(`/jobs/${jobName}/inline`, payload);
+  }
+
   /**
    * @returns {Observable} Fetches Steps.
    */
diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.html b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.html
new file mode 100644
index 000000000..f266e9db8
--- /dev/null
+++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.html
@@ -0,0 +1,81 @@
+
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + Loan Id {{loan.loanId}} Lock Placed On {{loan.lockPlacedOn | datetimeFormat}} Lock Owner {{loan.lockOwner}} Error {{loan.error}} Details + + +
+ + + + +
+ +
+ +
+ + No loan locked available. +
+ +
diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.scss b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.scss new file mode 100644 index 000000000..9d6a1c24c --- /dev/null +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.scss @@ -0,0 +1,29 @@ +.tab-container { + padding: 1%; + margin: 1%; + + .mat-raised-button.mat-success { + color: #fff; + background-color: #32cd32; + } + .mat-raised-button.mat-reject { + color: #fff; + background-color: #ffa726; + } + #search-button { + height: 2.5rem; + margin-top: 1rem; + } + .view-details{ + cursor: pointer; + } + table { + tr.select-row:hover { + cursor: pointer; + } + } +} + +.error-log { + color: #ffa726; +} diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.spec.ts b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.spec.ts new file mode 100644 index 000000000..205e57217 --- /dev/null +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoanLockedComponent } from './loan-locked.component'; + +describe('LoanLockedComponent', () => { + let component: LoanLockedComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ LoanLockedComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(LoanLockedComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.ts b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.ts new file mode 100644 index 000000000..7910f8455 --- /dev/null +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component.ts @@ -0,0 +1,139 @@ +import { SelectionModel } from '@angular/cdk/collections'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatTableDataSource } from '@angular/material/table'; +import { ActivatedRoute, Router } from '@angular/router'; +import { LoansService } from 'app/loans/loans.service'; +import { ErrorDialogComponent } from 'app/shared/error-dialog/error-dialog.component'; +import { SystemService } from 'app/system/system.service'; +import { TasksService } from 'app/tasks/tasks.service'; + +@Component({ + selector: 'mifosx-loan-locked', + templateUrl: './loan-locked.component.html', + styleUrls: ['./loan-locked.component.scss'] +}) +export class LoanLockedComponent implements OnInit { + + /** Loans Data */ + loans: any[]; + /** Batch Requests */ + batchRequests: any[]; + /** Datasource for loans disbursal table */ + dataSource: MatTableDataSource; + /** Row Selection */ + selection: SelectionModel; + /** Displayed Columns for loan disbursal data */ + displayedColumns: string[] = ['select', 'loanId', 'lockPlacedOn', 'lockOwner', 'error', 'details']; + /** Paginator for the table */ + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + currentPage = 0; + pageSize = 10; + /** Control for enable/disable button */ + allowRunInlineJob = false; + /** Const for the jobName */ + jobName: String = 'LOAN_COB'; + + /** + * @param {LoansService} loansService Loans Service + * @param {Router} router Router for navigation. + * @param {MatDialog} dialog Dialog reference. + */ + constructor(private route: ActivatedRoute, + private router: Router, + private loansService: LoansService, + private systemService: SystemService, + private tasksService: TasksService, + private dialog: MatDialog) { + this.route.data.subscribe((data: { loansData: any }) => { + this.loans = data.loansData.content; + }); + } + + ngOnInit(): void { + this.dataSource = new MatTableDataSource(this.loans); + this.dataSource.paginator = this.paginator; + this.selection = new SelectionModel(true, []); + this.allowRunInlineJob = false; + } + + applyFilter(filterValue: string = '') { + this.dataSource.filter = filterValue.trim().toLowerCase(); + } + + handlePage(e: any) { + if (this.currentPage !== e.pageIndex) { + this.currentPage = e.pageIndex; + this.pageSize = e.pageSize; + this.getLoansLocked(this.currentPage); + } + } + + getLoansLocked(page: number) { + this.tasksService.getAllLoansLocked(page, this.pageSize).subscribe((data: any) => { + this.loans = data.content; + this.allowRunInlineJob = false; + this.selection = new SelectionModel(true, []); + }); + } + + /** Whether the number of selected elements matches the total number of rows. */ + isAllSelected() { + const numSelected = this.selection.selected.length; + if (numSelected === 0) { + this.allowRunInlineJob = false; + } else { + this.allowRunInlineJob = true; + } + const numRows = this.dataSource.data.length; + return numSelected === numRows; + } + + /** Selects all rows if they are not all selected; otherwise clear selection. */ + masterToggle() { + this.isAllSelected() ? + this.selection.clear() : + this.dataSource.data.forEach((row: any) => this.selection.select(row)); + } + + /** The label for the checkbox on the passed row */ + checkboxLabel(row?: any): string { + if (!row) { + return `${this.isAllSelected() ? 'select' : 'deselect'} all`; + } + return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.position + 1}`; + } + + showDetails(loan: any) { + this.dialog.open(ErrorDialogComponent, { + width: '960px', + height: '400px', + data: '
' + loan.stacktrace + '
' + }); + } + + viewLoanAccount(loan: any) { + const loanId = loan.loanId; + this.loansService.getLoanAccountDetails(loanId).subscribe((loanData: any) => { + const clientId = loanData.clientId; + this.router.navigateByUrl(`/clients/${clientId}/loans-accounts/${loanId}/general`); + }); + } + + runInlineCOB(): void { + const loanIds: any = []; + this.selection.selected.forEach((row: any) => { + loanIds.push(row.loanId); + }); + if (loanIds.length > 0) { + const payload = { + loanIds: loanIds + }; + this.systemService.runInlineCOB(this.jobName, payload).subscribe((data: any) => { + this.getLoansLocked(0); + }); + } + } + +} diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.spec.ts b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.spec.ts new file mode 100644 index 000000000..2ab4734e4 --- /dev/null +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { LoanLockedResolver } from './loan-locked.resolver'; + +describe('LoanLockedResolver', () => { + let resolver: LoanLockedResolver; + + beforeEach(() => { + TestBed.configureTestingModule({}); + resolver = TestBed.inject(LoanLockedResolver); + }); + + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); +}); diff --git a/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.ts b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.ts new file mode 100644 index 000000000..2ab530435 --- /dev/null +++ b/src/app/tasks/checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { + Router, Resolve, + RouterStateSnapshot, + ActivatedRouteSnapshot +} from '@angular/router'; +import { TasksService } from 'app/tasks/tasks.service'; +import { Observable, of } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class LoanLockedResolver implements Resolve { + + /** + * @param {TasksService} tasksService Tasks service. + */ + constructor(private tasksService: TasksService) { } + + /** + * Returns all the loans data. + * @returns {Observable} + */ + resolve(): Observable { + return this.tasksService.getAllLoansLocked(0, 200); + } +} diff --git a/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html b/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html index 100e45fc3..0ba1cacae 100644 --- a/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html +++ b/src/app/tasks/checker-inbox-and-tasks/checker-inbox-and-tasks.component.html @@ -25,6 +25,10 @@ [active]="rescheduleLoan.isActive" *mifosxHasPermission="'RESCHEDULE_LOAN'"> Reschedule Loan + + Loan Locked + diff --git a/src/app/tasks/tasks-routing.module.ts b/src/app/tasks/tasks-routing.module.ts index 39f17900c..544e054f1 100644 --- a/src/app/tasks/tasks-routing.module.ts +++ b/src/app/tasks/tasks-routing.module.ts @@ -25,6 +25,8 @@ import { GetLoans } from './common-resolvers/getLoans.resolver'; import { GetRescheduleLoans } from './common-resolvers/getRescheduleLoans.resolver'; import { MakerCheckerTemplate } from './common-resolvers/makerCheckerTemplate.resolver'; import { GetCheckerInboxDetailResolver } from './common-resolvers/getCheckerInboxDetail.resolver'; +import { LoanLockedComponent } from './checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component'; +import { LoanLockedResolver } from './checker-inbox-and-tasks-tabs/loan-locked/loan-locked.resolver'; /** Tasks Routes */ const routes: Routes = [ @@ -68,6 +70,14 @@ const routes: Routes = [ loansData: GetLoans } }, + { + path: 'loan-locked', + component: LoanLockedComponent, + data: { title: extract('Loan Locked') }, + resolve: { + loansData: LoanLockedResolver + } + }, { path: 'reschedule-loan', component: RescheduleLoanComponent, diff --git a/src/app/tasks/tasks.module.ts b/src/app/tasks/tasks.module.ts index b6db52531..872d74b06 100644 --- a/src/app/tasks/tasks.module.ts +++ b/src/app/tasks/tasks.module.ts @@ -14,6 +14,7 @@ import { LoanApprovalComponent } from './checker-inbox-and-tasks-tabs/loan-appro import { LoanDisbursalComponent } from './checker-inbox-and-tasks-tabs/loan-disbursal/loan-disbursal.component'; import { RescheduleLoanComponent } from './checker-inbox-and-tasks-tabs/reschedule-loan/reschedule-loan.component'; import { ViewCheckerInboxComponent } from './view-checker-inbox/view-checker-inbox.component'; +import { LoanLockedComponent } from './checker-inbox-and-tasks-tabs/loan-locked/loan-locked.component'; /** * Tasks Module @@ -32,7 +33,8 @@ import { ViewCheckerInboxComponent } from './view-checker-inbox/view-checker-inb LoanApprovalComponent, LoanDisbursalComponent, RescheduleLoanComponent, - ViewCheckerInboxComponent + ViewCheckerInboxComponent, + LoanLockedComponent ], providers: [ ] }) diff --git a/src/app/tasks/tasks.service.ts b/src/app/tasks/tasks.service.ts index 083b5c04f..2c010925c 100644 --- a/src/app/tasks/tasks.service.ts +++ b/src/app/tasks/tasks.service.ts @@ -68,6 +68,14 @@ export class TasksService { return this.http.get('/loans', { params: httpParams }); } + /** + * Get Loans Locked Data using pages and limit + */ + getAllLoansLocked(page: number, limit: number): Observable { + const httpParams = new HttpParams().set('page', page).set('limit', limit); + return this.http.get('/loans/locked', { params: httpParams }); + } + /** * Get Pending Rescheduled Loans */