From 48709edba013467ff394ef7fde3695b02d2ce902 Mon Sep 17 00:00:00 2001 From: Muskan Khedia <36980003+muskankhedia@users.noreply.github.com> Date: Fri, 17 Jul 2020 21:07:11 +0530 Subject: [PATCH] feat: add view loan (loan documents tab) (#1126) Fixes: #818 --- .../loan-action-button.resolver.ts | 2 +- .../loan-documents.resolver.ts | 31 +++++ ...tes-resolver.ts => loan-notes.resolver.ts} | 0 ...count-load-documents-dialog.component.html | 28 +++++ ...count-load-documents-dialog.component.scss | 0 ...nt-load-documents-dialog.component.spec.ts | 25 ++++ ...account-load-documents-dialog.component.ts | 51 ++++++++ src/app/loans/loans-routing.module.ts | 17 ++- .../loan-documents-tab.component.html | 59 ++++++++++ .../loan-documents-tab.component.scss | 15 +++ .../loan-documents-tab.component.spec.ts | 25 ++++ .../loan-documents-tab.component.ts | 110 ++++++++++++++++++ .../loans-view/loans-view.component.html | 3 + .../loans/loans-view/loans-view.component.ts | 2 - src/app/loans/loans.module.ts | 9 +- src/app/loans/loans.service.ts | 12 ++ 16 files changed, 381 insertions(+), 8 deletions(-) create mode 100644 src/app/loans/common-resolvers/loan-documents.resolver.ts rename src/app/loans/common-resolvers/{loan-notes-resolver.ts => loan-notes.resolver.ts} (100%) create mode 100644 src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.html create mode 100644 src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.scss create mode 100644 src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.spec.ts create mode 100644 src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.ts create mode 100644 src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.html create mode 100644 src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.scss create mode 100644 src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.spec.ts create mode 100644 src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.ts diff --git a/src/app/loans/common-resolvers/loan-action-button.resolver.ts b/src/app/loans/common-resolvers/loan-action-button.resolver.ts index f7146beb5..ccaa4d36a 100644 --- a/src/app/loans/common-resolvers/loan-action-button.resolver.ts +++ b/src/app/loans/common-resolvers/loan-action-button.resolver.ts @@ -26,7 +26,7 @@ export class LoanActionButtonResolver implements Resolve { resolve(route: ActivatedRouteSnapshot): Observable { const loanId = route.parent.paramMap.get('loanId'); const loanActionButton = route.paramMap.get('action'); - if (loanActionButton === 'assign-loan-officer') { + if (loanActionButton === 'assign-loan-officer' || loanActionButton === 'change-loan-officer') { return this.loansService.getLoanTemplate(loanId); // } else if (loanActionButton === 'make-repayment') { // return this.loansService.getLoanActionTemplate(loanId, 'repayment'); diff --git a/src/app/loans/common-resolvers/loan-documents.resolver.ts b/src/app/loans/common-resolvers/loan-documents.resolver.ts new file mode 100644 index 000000000..6e768f671 --- /dev/null +++ b/src/app/loans/common-resolvers/loan-documents.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { LoansService } from '../loans.service'; + +/** + * Loans notes resolver. + */ +@Injectable() +export class LoanDocumentsResolver implements Resolve { + + /** + * @param {LoansService} LoansService Loans service. + */ + constructor(private loansService: LoansService) { } + + /** + * Returns the Loans data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const loanId = route.parent.paramMap.get('loanId'); + return this.loansService.getLoanDocuments(loanId); + } + +} diff --git a/src/app/loans/common-resolvers/loan-notes-resolver.ts b/src/app/loans/common-resolvers/loan-notes.resolver.ts similarity index 100% rename from src/app/loans/common-resolvers/loan-notes-resolver.ts rename to src/app/loans/common-resolvers/loan-notes.resolver.ts diff --git a/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.html b/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.html new file mode 100644 index 000000000..a63e520e8 --- /dev/null +++ b/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.html @@ -0,0 +1,28 @@ +

Load Loan Document

+ +
+
+ + + File Name + + + File Name is required + + + + + Description + + + + + + + + + + +
+
diff --git a/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.scss b/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.spec.ts b/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.spec.ts new file mode 100644 index 000000000..2c4e23bb8 --- /dev/null +++ b/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoanAccountLoadDocumentsDialogComponent } from './loan-account-load-documents-dialog.component'; + +describe('LoanAccountLoadDocumentsDialogComponent', () => { + let component: LoanAccountLoadDocumentsDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoanAccountLoadDocumentsDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoanAccountLoadDocumentsDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.ts b/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.ts new file mode 100644 index 000000000..70f955460 --- /dev/null +++ b/src/app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; + +@Component({ + selector: 'mifosx-loan-account-load-documents-dialog', + templateUrl: './loan-account-load-documents-dialog.component.html', + styleUrls: ['./loan-account-load-documents-dialog.component.scss'] +}) +export class LoanAccountLoadDocumentsDialogComponent implements OnInit { + + /** Upload Document form. */ + uploadDocumentForm: FormGroup; + + /** + * @param {MatDialogRef} dialogRef Dialog reference element + * @param {FormBuilder} formBuilder Form Builder + * @param {any} data Dialog Data + */ + constructor(public dialogRef: MatDialogRef, + private formBuilder: FormBuilder, + @Inject(MAT_DIALOG_DATA) public data: any) { + } + + ngOnInit() { + this.createUploadDocumentForm(); + } + + /** + * Creates the upload Document form. + */ + createUploadDocumentForm() { + this.uploadDocumentForm = this.formBuilder.group({ + 'name': ['', Validators.required], + 'description': [''], + 'file': [''] + }); + } + + /** + * Sets file form control value. + * @param {any} $event file change event. + */ + onFileSelect($event: any) { + if ($event.target.files.length > 0) { + const file = $event.target.files[0]; + this.uploadDocumentForm.get('file').setValue(file); + } + } + +} diff --git a/src/app/loans/loans-routing.module.ts b/src/app/loans/loans-routing.module.ts index b66d18e4c..1fca68d3b 100644 --- a/src/app/loans/loans-routing.module.ts +++ b/src/app/loans/loans-routing.module.ts @@ -22,18 +22,19 @@ import { FloatingInterestRatesComponent } from './loans-view/floating-interest-r import { LoanTrancheDetailsComponent } from './loans-view/loan-tranche-details/loan-tranche-details.component'; import { LoanCollateralTabComponent } from './loans-view/loan-collateral-tab/loan-collateral-tab.component'; import { CreateLoansAccountComponent } from './create-loans-account/create-loans-account.component'; -import { RejectLoanComponent } from './loans-view/loan-account-actions/reject-loan/reject-loan.component'; +import { LoanDocumentsTabComponent } from './loans-view/loan-documents-tab/loan-documents-tab.component'; /** Custom Resolvers */ import { LoanChargeTemplateResolver } from './common-resolvers/loan-charge-template.resolver'; import { LoanDetailsResolver } from './common-resolvers/loan-details.resolver'; import { LoanDetailsGeneralResolver } from './common-resolvers/loan-details-general.resolver'; -import { LoanNotesResolver } from './common-resolvers/loan-notes-resolver'; +import { LoanNotesResolver } from './common-resolvers/loan-notes.resolver'; import { LoanDetailsChargesResolver } from './common-resolvers/loan-details-charges.resolver'; import { LoanDatatablesResolver } from './common-resolvers/loan-datatables.resolver'; import { LoanDatatableResolver } from './common-resolvers/loan-datatable.resolver'; import { LoanActionButtonResolver } from './common-resolvers/loan-action-button.resolver'; import { LoansAccountTemplateResolver } from './common-resolvers/loans-account-template.resolver'; +import { LoanDocumentsResolver } from './common-resolvers/loan-documents.resolver'; /** Loans Route. */ const routes: Routes = [ @@ -139,6 +140,15 @@ const routes: Routes = [ loanDetailsAssociationData: LoanDetailsChargesResolver } }, + { + path: 'loan-documents', + component: LoanDocumentsTabComponent, + data: { title: extract('Loan Documents'), breadcrumb: 'Loan Documents', routeParamBreadcrumb: false }, + resolve: { + loanDocuments: LoanDocumentsResolver, + loanDetailsData: LoanDetailsGeneralResolver + }, + }, { path: 'notes', component: NotesTabComponent, @@ -191,7 +201,8 @@ const routes: Routes = [ LoanDatatablesResolver, LoanDatatableResolver, LoanActionButtonResolver, - LoansAccountTemplateResolver + LoansAccountTemplateResolver, + LoanDocumentsResolver ] }) diff --git a/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.html b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.html new file mode 100644 index 000000000..e6c772ffa --- /dev/null +++ b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.html @@ -0,0 +1,59 @@ +
+ +
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name {{ document.name }} Description {{ document.description }} File Name {{ document.fileName }} Actions + + + + + + +
+ + + +
+ +
diff --git a/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.scss b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.scss new file mode 100644 index 000000000..a833a3030 --- /dev/null +++ b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.scss @@ -0,0 +1,15 @@ +table { + width: 100%; + margin-top: 3%; + .account-action-button{ + min-width: 26px; + padding: 0 6px; + margin: 4px; + line-height: 25px; + } +} + +.tab-container { + padding: 1%; + margin: 1%; +} diff --git a/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.spec.ts b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.spec.ts new file mode 100644 index 000000000..331d0c510 --- /dev/null +++ b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoanDocumentsTabComponent } from './loan-documents-tab.component'; + +describe('LoanDocumentsTabComponent', () => { + let component: LoanDocumentsTabComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoanDocumentsTabComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoanDocumentsTabComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.ts b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.ts new file mode 100644 index 000000000..d2deaf16c --- /dev/null +++ b/src/app/loans/loans-view/loan-documents-tab/loan-documents-tab.component.ts @@ -0,0 +1,110 @@ +/** Angular Imports */ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatTableDataSource, MatPaginator, MatSort, MatDialog, MatTable } from '@angular/material'; +import { ActivatedRoute } from '@angular/router'; + +/** Custom Services */ +import { environment } from 'environments/environment'; +import { LoansService } from 'app/loans/loans.service'; + +/** Dialog Components */ +import { LoanAccountLoadDocumentsDialogComponent } from 'app/loans/custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component'; +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; + +/** + * Overdue charges tab component + */ +@Component({ + selector: 'mifosx-loan-documents-tab', + templateUrl: './loan-documents-tab.component.html', + styleUrls: ['./loan-documents-tab.component.scss'] +}) +export class LoanDocumentsTabComponent implements OnInit { + @ViewChild('documentsTable') documentsTable: MatTable; + + /** Stores the resolved loan documents data */ + loanDocuments: any; + /** Stores the resolved loan details data */ + loanDetailsData: any; + /** Status of the loan account */ + status: any; + /** Choice */ + choice: boolean; + + /** Columns to be displayed in loan documents table. */ + displayedColumns: string[] = ['name', 'description', 'filename', 'actions']; + /** Data source for loan documents table. */ + dataSource: MatTableDataSource; + + /** Paginator for codes table. */ + @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for codes table. */ + @ViewChild(MatSort) sort: MatSort; + + /** + * Retrieves the loans data from `resolve`. + * @param {ActivatedRoute} route Activated Route. + */ + constructor(private route: ActivatedRoute, + private loansService: LoansService, + public dialog: MatDialog) { + this.route.data.subscribe((data: { loanDocuments: any, loanDetailsData: any }) => { + this.getLoanDocumentsData(data.loanDocuments); + this.loanDetailsData = data.loanDetailsData; + }); + } + + ngOnInit() { + this.status = this.loanDetailsData.status.value; + if (this.status === 'Submitted and pending approval' || this.status === 'Active' || this.status === 'Approved') { + this.choice = true; + } + this.dataSource = new MatTableDataSource(this.loanDocuments); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } + + getLoanDocumentsData(data: any) { + data.forEach((ele: any) => { + let loandocs = {}; + loandocs = environment.serverUrl + '/loans/' + ele.parentEntityId + '/documents/' + ele.id + '/attachment?tenantIdentifier=' + environment.fineractPlatformTenantId; + ele.docUrl = loandocs; + if (ele.fileName) { + if (ele.fileName.toLowerCase().indexOf('.jpg') !== -1 || ele.fileName.toLowerCase().indexOf('.jpeg') !== -1 || ele.fileName.toLowerCase().indexOf('.png') !== -1) { + ele.fileIsImage = true; + } + } + if (ele.type) { + if (ele.type.toLowerCase().indexOf('image') !== -1) { + ele.fileIsImage = true; + } + } + }); + this.loanDocuments = data; + } + + uploadDocument() { + const uploadLoanDocumentDialogRef = this.dialog.open(LoanAccountLoadDocumentsDialogComponent); + uploadLoanDocumentDialogRef.afterClosed().subscribe((data: any) => { + if (data) { + this.loansService.loadLoanDocument(this.loanDetailsData.id, data) + .subscribe(() => {}); + } + }); + } + + deleteDocument(documentId: any, index: any) { + const deleteDocumentDialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: `document id:${documentId}` } + }); + deleteDocumentDialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.loansService.deleteLoanDocument(this.loanDetailsData.id, documentId).subscribe((res: any) => { + this.loanDocuments.splice(index, 1); + this.documentsTable.renderRows(); + }); + } + }); + } + +} diff --git a/src/app/loans/loans-view/loans-view.component.html b/src/app/loans/loans-view/loans-view.component.html index f8d85746d..82815ad37 100644 --- a/src/app/loans/loans-view/loans-view.component.html +++ b/src/app/loans/loans-view/loans-view.component.html @@ -97,6 +97,9 @@ Charges + + Loan Documents + Notes diff --git a/src/app/loans/loans-view/loans-view.component.ts b/src/app/loans/loans-view/loans-view.component.ts index e81c6b60c..444cbd063 100644 --- a/src/app/loans/loans-view/loans-view.component.ts +++ b/src/app/loans/loans-view/loans-view.component.ts @@ -123,8 +123,6 @@ export class LoansViewComponent implements OnInit { } loanAction(button: string) { - console.log('button: ', button); - switch (button) { case 'Recover From Guarantor': this.recoverFromGuarantor(); diff --git a/src/app/loans/loans.module.ts b/src/app/loans/loans.module.ts index b906e17ea..f431a8f3f 100644 --- a/src/app/loans/loans.module.ts +++ b/src/app/loans/loans.module.ts @@ -49,12 +49,14 @@ import { DisburseComponent } from './loans-view/loan-account-actions/disburse/di import { RejectLoanComponent } from './loans-view/loan-account-actions/reject-loan/reject-loan.component'; import { WithdrawnByClientComponent } from './loans-view/loan-account-actions/withdrawn-by-client/withdrawn-by-client.component'; import { UndoDisbursalComponent } from './loans-view/loan-account-actions/undo-disbursal/undo-disbursal.component'; +import { AddCollateralComponent } from './loans-view/loan-account-actions/add-collateral/add-collateral.component'; +import { LoanDocumentsTabComponent } from './loans-view/loan-documents-tab/loan-documents-tab.component'; /** Dialog Components */ import { LoansAccountViewGuarantorDetailsDialogComponent } from './custom-dialog/loans-account-view-guarantor-details-dialog/loans-account-view-guarantor-details-dialog.component'; import { LoansAccountAddCollateralDialogComponent } from './custom-dialog/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component'; import { LoansConfirmationDialogBoxComponent } from './custom-dialog/loans-confirmation-dialog-box/loans-confirmation-dialog-box.component'; -import { AddCollateralComponent } from './loans-view/loan-account-actions/add-collateral/add-collateral.component'; +import { LoanAccountLoadDocumentsDialogComponent } from './custom-dialog/loan-account-load-documents-dialog/loan-account-load-documents-dialog.component'; /** * Loans Module @@ -108,11 +110,14 @@ import { AddCollateralComponent } from './loans-view/loan-account-actions/add-co WithdrawnByClientComponent, AddCollateralComponent, UndoDisbursalComponent, + LoanDocumentsTabComponent, + LoanAccountLoadDocumentsDialogComponent, ], entryComponents: [ LoansAccountAddCollateralDialogComponent, LoansAccountViewGuarantorDetailsDialogComponent, - LoansConfirmationDialogBoxComponent + LoansConfirmationDialogBoxComponent, + LoanAccountLoadDocumentsDialogComponent ], providers: [DatePipe], }) diff --git a/src/app/loans/loans.service.ts b/src/app/loans/loans.service.ts index 15fc16e1b..e30719aa3 100644 --- a/src/app/loans/loans.service.ts +++ b/src/app/loans/loans.service.ts @@ -285,4 +285,16 @@ export class LoansService { return this.http.post('/loans', loanAccount); } + getLoanDocuments(loanId: any): Observable { + return this.http.get(`/loans/${loanId}/documents`); + } + + deleteLoanDocument(loanId: any, documentId: any): Observable { + return this.http.delete(`/loans/${loanId}/documents/${documentId}`); + } + + loadLoanDocument(loanId: any, data: any): Observable { + return this.http.post(`/loans/${loanId}/documents`, data); + } + }