From 9944dcce93c27dd0ec8b2e0cf048d2d9c98afce6 Mon Sep 17 00:00:00 2001 From: Jivjyot Singh Date: Sat, 2 Feb 2019 22:06:56 +0530 Subject: [PATCH] feat: add loan charge to existing loan accounts (#370) - Add Loans Module. - Add add-loan-charge component. - Add loans-routing.module.ts - Add loan-charge-template.resolver.ts Fixes: #247 --- src/app/app.module.ts | 4 +- .../add-loan-charge.component.html | 62 ++++++++++ .../add-loan-charge.component.scss | 3 + .../add-loan-charge.component.spec.ts | 25 ++++ .../add-loan-charge.component.ts | 108 ++++++++++++++++++ .../loan-charge-template.resolver.ts | 31 +++++ src/app/loans/loans-routing.module.spec.ts | 13 +++ src/app/loans/loans-routing.module.ts | 65 +++++++++++ src/app/loans/loans.module.spec.ts | 13 +++ src/app/loans/loans.module.ts | 26 +++++ src/app/loans/loans.service.spec.ts | 12 ++ src/app/loans/loans.service.ts | 32 ++++++ 12 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 src/app/loans/add-loan-charge/add-loan-charge.component.html create mode 100644 src/app/loans/add-loan-charge/add-loan-charge.component.scss create mode 100644 src/app/loans/add-loan-charge/add-loan-charge.component.spec.ts create mode 100644 src/app/loans/add-loan-charge/add-loan-charge.component.ts create mode 100644 src/app/loans/common-resolvers/loan-charge-template.resolver.ts create mode 100644 src/app/loans/loans-routing.module.spec.ts create mode 100644 src/app/loans/loans-routing.module.ts create mode 100644 src/app/loans/loans.module.spec.ts create mode 100644 src/app/loans/loans.module.ts create mode 100644 src/app/loans/loans.service.spec.ts create mode 100644 src/app/loans/loans.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d3d0097d8..97c78a636 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -26,13 +26,14 @@ import { HomeModule } from './home/home.module'; import { LoginModule } from './login/login.module'; import { SettingsModule } from './settings/settings.module'; import { NavigationModule } from './navigation/navigation.module'; -import { ClientsModule} from './clients/clients.module'; +import { ClientsModule } from './clients/clients.module'; import { GroupsModule } from './groups/groups.module'; import { CentersModule } from './centers/centers.module'; import { AccountingModule } from './accounting/accounting.module'; import { SelfServiceModule } from './self-service/self-service.module'; import { SystemModule } from './system/system.module'; import { ProductsModule } from './products/products.module'; +import { LoansModule } from './loans/loans.module'; /** Main Routing Module */ import { AppRoutingModule } from './app-routing.module'; @@ -60,6 +61,7 @@ import { AppRoutingModule } from './app-routing.module'; CentersModule, AccountingModule, SelfServiceModule, + LoansModule, SystemModule, ProductsModule, AppRoutingModule diff --git a/src/app/loans/add-loan-charge/add-loan-charge.component.html b/src/app/loans/add-loan-charge/add-loan-charge.component.html new file mode 100644 index 000000000..365db7f98 --- /dev/null +++ b/src/app/loans/add-loan-charge/add-loan-charge.component.html @@ -0,0 +1,62 @@ +
+ + + +
+ + + +
+ + Charge + + + {{ loanCharge.name + ' (' + loanCharge.currency.name + ')' }} + + + + Charge is required + + + + + Amount + + + Amount is required + + + + + Charge Calculation + + + + + Charge Time + + + + + Due On + + + + + Due Date is required + + + +
+ + + + + + +
+
+ +
+ +
diff --git a/src/app/loans/add-loan-charge/add-loan-charge.component.scss b/src/app/loans/add-loan-charge/add-loan-charge.component.scss new file mode 100644 index 000000000..c5d1982b3 --- /dev/null +++ b/src/app/loans/add-loan-charge/add-loan-charge.component.scss @@ -0,0 +1,3 @@ +.container{ + width: 37rem; +} diff --git a/src/app/loans/add-loan-charge/add-loan-charge.component.spec.ts b/src/app/loans/add-loan-charge/add-loan-charge.component.spec.ts new file mode 100644 index 000000000..26b206bf2 --- /dev/null +++ b/src/app/loans/add-loan-charge/add-loan-charge.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddLoanChargeComponent } from './add-loan-charge.component'; + +describe('AddloanchargeComponent', () => { + let component: AddLoanChargeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AddLoanChargeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddLoanChargeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/add-loan-charge/add-loan-charge.component.ts b/src/app/loans/add-loan-charge/add-loan-charge.component.ts new file mode 100644 index 000000000..4e55a5a1a --- /dev/null +++ b/src/app/loans/add-loan-charge/add-loan-charge.component.ts @@ -0,0 +1,108 @@ +/** Angular Imports */ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; +import { Router, ActivatedRoute } from '@angular/router'; +import { DatePipe } from '@angular/common'; + +/** Custom Services */ +import { LoansService } from '../loans.service'; + +/** + * Create Add Loan Charge component. + */ +@Component({ + selector: 'mifosx-addloancharge', + templateUrl: './add-loan-charge.component.html', + styleUrls: ['./add-loan-charge.component.scss'] +}) +export class AddLoanChargeComponent implements OnInit { + + /** Minimum Due Date allowed. */ + minDate = new Date(2000, 0, 1); + /** Maximum Due Date allowed. */ + maxDate = new Date(); + /** Add Loan Charge form. */ + loanChargeForm: FormGroup; + /** loan charge options. */ + loanChargeOptions: { + id: number; + name: string; + amount: number; + chargeCalculationType: { + value: any; + }; + chargeTimeType: { + id: number; + value: any; + }; + }[]; + /** loan Id of the loan account. */ + loanId: string; + + /** + * Retrieves the loan charge template data from `resolve`. + * @param {FormBuilder} formBuilder Form Builder. + * @param {AccountingService} accountingService Accounting Service. + * @param {ActivatedRoute} route Activated Route. + * @param {Router} router Router for navigation. + */ + constructor(private formBuilder: FormBuilder, + private route: ActivatedRoute, + private router: Router, + private datePipe: DatePipe, + private loansService: LoansService) { + this.route.data.subscribe((data: { loanChargeTemplate: any }) => { + this.loanChargeOptions = data.loanChargeTemplate.chargeOptions; + }); + this.loanId = this.route.snapshot.params['loanId']; + } + + /** + * Creates the Loan Charge form. + */ + ngOnInit() { + this.createLoanChargeForm(); + this.loanChargeForm.controls.chargeId.valueChanges.subscribe(chargeId => { + const chargeDetails = this.loanChargeOptions.find(option => { + return option.id === chargeId; + }); + if (chargeDetails.chargeTimeType.id === 2) { + this.loanChargeForm.addControl('dueDate', new FormControl('', Validators.required)); + } else { + this.loanChargeForm.removeControl('dueDate'); + } + this.loanChargeForm.patchValue({ + 'amount': chargeDetails.amount, + 'chargeCalculation': chargeDetails.chargeCalculationType.value, + 'chargeTime': chargeDetails.chargeTimeType.value + }); + }); + } + + /** + * Creates the Loan Charge form. + */ + createLoanChargeForm() { + this.loanChargeForm = this.formBuilder.group({ + 'chargeId': ['', Validators.required], + 'amount': ['', Validators.required], + 'chargeCalculation': [{ value: '', disabled: true }], + 'chargeTime': [{ value: '', disabled: true }] + }); + } + + submit() { + const prevDueDate: Date = this.loanChargeForm.value.dueDate; + // TODO: Update once language and date settings are setup + const dateFormat = 'yyyy-MM-dd'; + this.loanChargeForm.patchValue({ + dueDate: this.datePipe.transform(prevDueDate, dateFormat) + }); + const loanCharge = this.loanChargeForm.value; + loanCharge.locale = 'en'; + loanCharge.dateFormat = dateFormat; + this.loansService.createLoanCharge(this.loanId, 'charges', loanCharge).subscribe(res => { + this.router.navigate(['../'], { relativeTo: this.route }); + }); + } +} diff --git a/src/app/loans/common-resolvers/loan-charge-template.resolver.ts b/src/app/loans/common-resolvers/loan-charge-template.resolver.ts new file mode 100644 index 000000000..0af6438eb --- /dev/null +++ b/src/app/loans/common-resolvers/loan-charge-template.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'; + +/** + * Loan Charge Template data resolver. + */ +@Injectable() +export class LoanChargeTemplateResolver implements Resolve { + + /** + * @param {LoansService} LoansService loans service. + */ + constructor(private loansService: LoansService) {} + + /** + * Returns the Loan Charge Template. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const loanId = route.paramMap.get('loanId'); + return this.loansService.getLoanChargeTemplateResource(loanId); + } + +} diff --git a/src/app/loans/loans-routing.module.spec.ts b/src/app/loans/loans-routing.module.spec.ts new file mode 100644 index 000000000..86dabec87 --- /dev/null +++ b/src/app/loans/loans-routing.module.spec.ts @@ -0,0 +1,13 @@ +import { LoansRoutingModule } from './loans-routing.module'; + +describe('LoansRoutingModule', () => { + let loansRoutingModule: LoansRoutingModule; + + beforeEach(() => { + loansRoutingModule = new LoansRoutingModule(); + }); + + it('should create an instance', () => { + expect(loansRoutingModule).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans-routing.module.ts b/src/app/loans/loans-routing.module.ts new file mode 100644 index 000000000..b2d0f3df0 --- /dev/null +++ b/src/app/loans/loans-routing.module.ts @@ -0,0 +1,65 @@ +/** Angular Imports */ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +/** Routing Imports */ +import { Route } from '../core/route/route.service'; + +/** Translation Imports */ +import { extract } from '../core/i18n/i18n.service'; + +/** Custom Components */ +import { AddLoanChargeComponent } from './add-loan-charge/add-loan-charge.component'; + +/** Custom Resolvers */ +import {LoanChargeTemplateResolver} from './common-resolvers/loan-charge-template.resolver'; + +const routes: Routes = [ + Route.withShell([ + { + path: ':clientType', + data: { title: extract('Clients'), routeParamBreadcrumb: 'clientType' }, + children: [ + { + path: 'view', + children: [ + { + path: ':clientId', + data: { title: extract('Client View'), routeParamBreadcrumb: 'clientId' }, + children: [ + { + path: 'loans', + data: { title: extract('All Loans'), breadcrumb: 'Loans', routeParamBreadcrumb: false }, + children: [ + { + path: ':loanId', + data: { title: extract('Loan View'), routeParamBreadcrumb: 'loanId' }, + // Component For Loan View Comes Here + children: [ + { + path: 'add-loan-charge', + component: AddLoanChargeComponent, + data: {title: extract('Add Loan Charge'), breadcrumb: 'Add Loan Charge', routeParamBreadcrumb: false}, + resolve: { + loanChargeTemplate: LoanChargeTemplateResolver + } + } + ] + } + ] + } + ] + } + ] + } + ] + } + ]) +]; +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + declarations: [], + providers: [LoanChargeTemplateResolver] +}) +export class LoansRoutingModule {} diff --git a/src/app/loans/loans.module.spec.ts b/src/app/loans/loans.module.spec.ts new file mode 100644 index 000000000..945e1d9de --- /dev/null +++ b/src/app/loans/loans.module.spec.ts @@ -0,0 +1,13 @@ +import { LoansModule } from './loans.module'; + +describe('LoansModule', () => { + let loansModule: LoansModule; + + beforeEach(() => { + loansModule = new LoansModule(); + }); + + it('should create an instance', () => { + expect(loansModule).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans.module.ts b/src/app/loans/loans.module.ts new file mode 100644 index 000000000..ec8baa558 --- /dev/null +++ b/src/app/loans/loans.module.ts @@ -0,0 +1,26 @@ +/** Angular Imports */ +import { NgModule } from '@angular/core'; +import { DatePipe} from '@angular/common'; + +/** Custom Modules */ +import { LoansRoutingModule } from './loans-routing.module'; +import { SharedModule } from 'app/shared/shared.module'; + +/** Custom Components */ +import { AddLoanChargeComponent } from './add-loan-charge/add-loan-charge.component'; + +/** + * Loans Module + * + * All components related to loan functions should be declared here. + */ +@NgModule({ + imports: [ + SharedModule, + LoansRoutingModule + ], + declarations: [AddLoanChargeComponent], + providers: [DatePipe] + +}) +export class LoansModule { } diff --git a/src/app/loans/loans.service.spec.ts b/src/app/loans/loans.service.spec.ts new file mode 100644 index 000000000..e3a6ac482 --- /dev/null +++ b/src/app/loans/loans.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { LoansService } from './loans.service'; + +describe('LoansService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: LoansService = TestBed.get(LoansService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans.service.ts b/src/app/loans/loans.service.ts new file mode 100644 index 000000000..c33fac775 --- /dev/null +++ b/src/app/loans/loans.service.ts @@ -0,0 +1,32 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** + * Loans service. + */ +@Injectable({ + providedIn: 'root' +}) +export class LoansService { + constructor(private http: HttpClient) {} + /** + * @param {string} loanId loanId of the loan. + * @returns {Observable} + */ + getLoanChargeTemplateResource(loanId: string): Observable { + return this.http.get(`/loans/${loanId}/charges/template`); + } + + /** + * @param {any} loanCharge to apply on a Loan Account. + * @returns {Observable} + */ + createLoanCharge(loanId: string, resourceType: string, loanCharge: any): Observable { + return this.http.post(`/loans/${loanId}/${resourceType}`, loanCharge); + } + +}