diff --git a/src/app/loans/loans-view/general-tab/general-tab.component.html b/src/app/loans/loans-view/general-tab/general-tab.component.html index 5913c1a03..873f0450c 100644 --- a/src/app/loans/loans-view/general-tab/general-tab.component.html +++ b/src/app/loans/loans-view/general-tab/general-tab.component.html @@ -5,6 +5,7 @@

# of Repayments :{{loanDetails?.numberOfRepayments}} +

Maturity Date :{{loanDetails?.timeline.expectedMaturityDate | dateFormat}}

@@ -69,7 +70,7 @@ Value - + {{loanDetails.timeline.actualDisbursementDate | dateFormat}} Not Available @@ -124,7 +125,7 @@ Value - + {{loanDetails.timeline.actualDisbursementDate | dateFormat}} Not Available diff --git a/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.html b/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.html index ff00de384..39526b7cb 100644 --- a/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.html +++ b/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.html @@ -1,5 +1,9 @@
+ +

Loan Account Classification : {{loanDetails?.delinquencyRange.classification}}

+
+ @@ -29,7 +33,7 @@ @@ -135,4 +139,4 @@ - \ No newline at end of file + diff --git a/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.scss b/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.scss index 6a37a3d69..e38332f65 100644 --- a/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.scss +++ b/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.scss @@ -9,4 +9,4 @@ table { .check { padding-left: 15px; -} \ No newline at end of file +} diff --git a/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.ts b/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.ts index 97ec0bf74..12ed05da8 100644 --- a/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.ts +++ b/src/app/loans/loans-view/repayment-schedule-tab/repayment-schedule-tab.component.ts @@ -8,6 +8,7 @@ import { ActivatedRoute } from '@angular/router'; }) export class RepaymentScheduleTabComponent implements OnInit { + loanDetails: any; /** Loan Repayment Schedule Details Data */ repaymentScheduleDetails: any; /** Stores if there is any waived amount */ @@ -21,6 +22,7 @@ export class RepaymentScheduleTabComponent implements OnInit { */ constructor(private route: ActivatedRoute) { this.route.parent.data.subscribe((data: { loanDetailsData: any }) => { + this.loanDetails = data.loanDetailsData; this.repaymentScheduleDetails = data.loanDetailsData.repaymentSchedule; }); } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html index bf38f9dc3..d3b4f79ff 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html @@ -346,6 +346,11 @@ {{ loanProduct.overdueDaysForNPA }} +
+ Delinquency Bucket: + {{ loanProduct.delinquencyBucketId ? 'Yes' : 'No' }} +
+
Account moves out of NPA only after all arrears have been cleared: {{ loanProduct.accountMovesOutOfNPAOnlyOnArrearsCompletion ? 'Yes' : 'No' }} diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html index 815b04c48..61c698984 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html @@ -72,6 +72,15 @@ + + Delinquency Bucket + + + {{ delinquencyBucket.name }} + + + + diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts index 2f82c9940..b86a19273 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts @@ -26,6 +26,7 @@ export class LoanProductSettingsStepComponent implements OnInit { interestRecalculationNthDayTypeData: any; interestRecalculationDayOfWeekTypeData: any; interestRecalculationOnDayTypeData: any; + delinquencyBucketData: any; constructor(private formBuilder: FormBuilder) { this.createLoanProductSettingsForm(); @@ -54,6 +55,7 @@ export class LoanProductSettingsStepComponent implements OnInit { this.interestRecalculationNthDayTypeData.push({ 'id': -2, 'code': 'onDay', 'value': 'on day' }); this.interestRecalculationDayOfWeekTypeData = this.loanProductsTemplate.interestRecalculationDayOfWeekTypeOptions; this.interestRecalculationOnDayTypeData = Array.from({ length: 28 }, (_, index) => index + 1); + this.delinquencyBucketData = this.loanProductsTemplate.delinquencyBucketOptions; this.loanProductSettingsForm.patchValue({ 'amortizationType': this.loanProductsTemplate.amortizationType.id, @@ -82,7 +84,8 @@ export class LoanProductSettingsStepComponent implements OnInit { 'holdGuaranteeFunds': this.loanProductsTemplate.holdGuaranteeFunds, 'multiDisburseLoan': this.loanProductsTemplate.multiDisburseLoan, 'maxTrancheCount': this.loanProductsTemplate.maxTrancheCount, - 'outstandingLoanBalance': this.loanProductsTemplate.outstandingLoanBalance + 'outstandingLoanBalance': this.loanProductsTemplate.outstandingLoanBalance, + 'delinquencyBucketId': this.loanProductsTemplate.delinquencyBucket.id }); if (this.loanProductsTemplate.isInterestRecalculationEnabled) { @@ -170,7 +173,8 @@ export class LoanProductSettingsStepComponent implements OnInit { 'repaymentEvery': [true], 'graceOnPrincipalAndInterestPayment': [true], 'graceOnArrearsAgeing': [true] - }) + }), + 'delinquencyBucketId': [''] }); } diff --git a/src/app/products/loan-products/view-loan-product/view-loan-product.component.html b/src/app/products/loan-products/view-loan-product/view-loan-product.component.html index 7fdee8d37..f5a104875 100644 --- a/src/app/products/loan-products/view-loan-product/view-loan-product.component.html +++ b/src/app/products/loan-products/view-loan-product/view-loan-product.component.html @@ -506,6 +506,8 @@ {{ loanProduct.allowAttributeOverrides.graceOnPrincipalAndInterestPayment ? 'Yes' : 'No' }} Number of days a loan may be overdue before moving into arrears: {{ loanProduct.allowAttributeOverrides.graceOnArrearsAgeing ? 'Yes' : 'No' }} + Delinquency Bucket: + {{ loanProduct.delinquencyBucket.id ? 'Yes' : 'No' }}
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.html b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.html new file mode 100644 index 000000000..49f501592 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.html @@ -0,0 +1,77 @@ +
+ + + +
+ + + +
+ + + Name + + + Name is required + + + +

Delinquency Ranges

+ +
+ +
+ +
  - +
+ + + + + + + + + + + + + + + + + + + + + + + + +
Classification + {{ row.rangeId | find: delinquencyRangesData:'id':'classification'}} Days From + {{ row.rangeId | find: delinquencyRangesData:'id':'minimumAgeDays'}} Days Till + {{ row.rangeId | find: delinquencyRangesData:'id':'maximumAgeDays'}} Actions + +
+ +
+ + + + + + + + + + + + +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.scss new file mode 100644 index 000000000..b99efaa71 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.scss @@ -0,0 +1,10 @@ +.container { + max-width: 37rem; + + .content { + div { + margin: 0.8rem 0; + word-wrap: break-word; + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.spec.ts new file mode 100644 index 000000000..da9ee7ab6 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateBucketComponent } from './create-bucket.component'; + +describe('CreateBucketComponent', () => { + let component: CreateBucketComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CreateBucketComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateBucketComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.ts new file mode 100644 index 000000000..6334f365f --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component.ts @@ -0,0 +1,124 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProductsService } from 'app/products/products.service'; +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; +import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component'; +import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base'; +import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base'; + +@Component({ + selector: 'mifosx-create-bucket', + templateUrl: './create-bucket.component.html', + styleUrls: ['./create-bucket.component.scss'] +}) +export class CreateBucketComponent implements OnInit { + /** Delinquency Bucket form. */ + bucketForm: FormGroup; + /** Delinquency Bucket template data. */ + bucketTemplateData: any; + /** Delinquency Range Data Source */ + rangesDataSource: {}[]; + /** Delinquency Range Options */ + delinquencyRangesData: any; + delinquencyRangesIds: any; + + /** Delinquency Range Displayed Columns */ + displayedColumns: string[] = ['classification', 'minimumAgeDays', 'maximumAgeDays', 'actions']; + + constructor(private formBuilder: FormBuilder, + private productsService: ProductsService, + private router: Router, + private route: ActivatedRoute, + public dialog: MatDialog) { + this.route.data.subscribe((data: { delinquencyRanges: any }) => { + this.delinquencyRangesData = data.delinquencyRanges; + this.delinquencyRangesData = this.delinquencyRangesData.sort( + (objA: { minimumAgeDays: number; }, objB: { minimumAgeDays: number; }) => objA.minimumAgeDays - objB.minimumAgeDays, + ); + }); + } + + ngOnInit(): void { + this.setupForm(); + this.rangesDataSource = []; + this.delinquencyRangesIds = []; + } + + /** + * Creates the Delinquency Bucket form + */ + setupForm(): void { + this.bucketForm = this.formBuilder.group({ + 'name': ['', Validators.required] + }); + } + + /** + * Add Delinquency Range for a Delinquency Bucket + */ + addDelinquencyRange() { + let delinquencyRanges = this.delinquencyRangesData; + if (this.delinquencyRangesIds.length > 0) { + delinquencyRanges = this.delinquencyRangesData.filter((item: any) => { + return (this.delinquencyRangesIds.indexOf(item.id) < 0); + }); + } + const formfields: FormfieldBase[] = [ + new SelectBase({ + controlName: 'rangeId', + label: 'Delinquency Range', + options: { label: 'classification', value: 'id', data: delinquencyRanges }, + order: 1 + }) + ]; + const data = { + title: 'Add Delinquency Range', + layout: { addButtonText: 'Add' }, + formfields: formfields + }; + const rangeDialogRef = this.dialog.open(FormDialogComponent, { data }); + rangeDialogRef.afterClosed().subscribe((response: any) => { + if (response.data) { + this.rangesDataSource = this.rangesDataSource.concat(response.data.value); + this.delinquencyRangesIds.push(response.data.value.rangeId); + } + }); + } + + /** + * Delete particular Delinquency Range in Delinquency Bucket + */ + deleteDelinquencyRange(index: number) { + const dialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: `this` } + }); + dialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.delinquencyRangesIds.splice(index, 1); + this.rangesDataSource.splice(index, 1); + this.rangesDataSource = this.rangesDataSource.concat([]); + } + }); + } + + /** + * Submits the Delinquency Bucket form and creates the Delinquency Bucket, + * if successful redirects to Delinquency Buckets. + */ + submit() { + const ranges: any = []; + this.rangesDataSource.forEach((item: any) => { ranges.push(item.rangeId); }); + if (ranges.length > 0) { + const data = { + ...this.bucketForm.value, + ranges: ranges + }; + + this.productsService.createDelinquencyBucket(data).subscribe((response: any) => { + this.router.navigate(['../', response.resourceId], { relativeTo: this.route }); + }); + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.html b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.html new file mode 100644 index 000000000..92c835353 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.html @@ -0,0 +1,35 @@ +
+ +
+ +
+ +
+ + Filter + + +
+ +
+ + + + + + + + + + + +
Name {{ delinquencyBucket.name }}
+ + + +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.resolver.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.resolver.ts new file mode 100644 index 000000000..c1994c1b9 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.resolver.ts @@ -0,0 +1,35 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { ProductsService } from '../../products.service'; + +/** + * Delinquency Bucket Component data resolver. + */ +@Injectable() +export class DelinquencyBucketComponentsResolver implements Resolve { + + /** + * @param {ProductsService} productsService Products service. + */ + constructor(private productsService: ProductsService) {} + + /** + * Returns the delinquency buckets data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const delinquentcyBucketId = route.paramMap.get('bucketId'); + if (delinquentcyBucketId === null) { + return this.productsService.getDelinquencyBuckets(); + } else { + return this.productsService.getDelinquencyBucket(delinquentcyBucketId); + } + } + +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.scss new file mode 100644 index 000000000..1d6a876d2 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.scss @@ -0,0 +1,18 @@ +.container { + .action-button { + margin-left: auto; + } + .search-button { + min-width: 30px; + margin: 4px; + } +} + +table { + tr.select-row:hover { + cursor: pointer; + } + tr:nth-child(odd).td{ + color : black; + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.spec.ts new file mode 100644 index 000000000..cde9014fd --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DelinquencyBucketComponent } from './delinquency-bucket.component'; + +describe('DelinquencyBucketComponent', () => { + let component: DelinquencyBucketComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DelinquencyBucketComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DelinquencyBucketComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.ts new file mode 100644 index 000000000..119a309ec --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'mifosx-delinquency-bucket', + templateUrl: './delinquency-bucket.component.html', + styleUrls: ['./delinquency-bucket.component.scss'] +}) +export class DelinquencyBucketComponent implements OnInit { + + delinquencyBucketData: any; + /** Columns to be displayed in delinquency bucket table. */ + displayedColumns: string[] = ['name']; + /** Data source for delinquency bucket table. */ + dataSource: MatTableDataSource; + + /** Paginator for delinquency bucket table. */ + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + /** Sorter for delinquency bucket table. */ + @ViewChild(MatSort, { static: true }) sort: MatSort; + + constructor(private route: ActivatedRoute) { + this.route.data.subscribe(( data: { delinquencyBuckets: any }) => { + this.delinquencyBucketData = data.delinquencyBuckets; + }); + } + + ngOnInit(): void { + this.setDatasource(); + } + + /** + * Filters data in delinquency bucket table based on passed value. + * @param {string} filterValue Value to filter data. + */ + applyFilter(filterValue: string) { + this.dataSource.filter = filterValue.trim().toLowerCase(); + } + + /** + * Initializes the data source, paginator and sorter for delinquency bucket table. + */ + setDatasource() { + this.dataSource = new MatTableDataSource(this.delinquencyBucketData); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.html b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.html new file mode 100644 index 000000000..0cf690edf --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.html @@ -0,0 +1,80 @@ +
+ + + +
+ + + +
+ + + Name + + + Name is required + + + +

Delinquency Ranges

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Classification {{ row.classification }} + {{ row.rangeId | find: delinquencyRangesData:'id':'classification'}} Days From {{ row.minimumAgeDays }} + {{ row.rangeId | find: delinquencyRangesData:'id':'minimumAgeDays'}} Days Till {{ row.maximumAgeDays }} + {{ row.rangeId | find: delinquencyRangesData:'id':'maximumAgeDays'}} Actions + +
+ +
+ +
+ + + + + + +
+ +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.scss new file mode 100644 index 000000000..b99efaa71 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.scss @@ -0,0 +1,10 @@ +.container { + max-width: 37rem; + + .content { + div { + margin: 0.8rem 0; + word-wrap: break-word; + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.spec.ts new file mode 100644 index 000000000..626bdc5a1 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditBucketComponent } from './edit-bucket.component'; + +describe('EditBucketComponent', () => { + let component: EditBucketComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ EditBucketComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EditBucketComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.ts new file mode 100644 index 000000000..fd1e071e3 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component.ts @@ -0,0 +1,140 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProductsService } from 'app/products/products.service'; +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; +import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component'; +import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base'; +import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base'; + +@Component({ + selector: 'mifosx-edit-bucket', + templateUrl: './edit-bucket.component.html', + styleUrls: ['./edit-bucket.component.scss'] +}) +export class EditBucketComponent implements OnInit { + /** Delinquency Bucket form. */ + bucketForm: FormGroup; + /** Delinquency Bucket template data. */ + bucketTemplateData: any; + /** Delinquency Range Data Source */ + rangesDataSource: {}[]; + /** Delinquency Range Options */ + delinquencyRangesData: any; + /** Delinquency Bucket data */ + delinquencyBucketData: any; + delinquencyRangesIds: any; + /** Delinquency Bucket Id */ + delinquencyBucketId: any; + /** Data was changed */ + dataWasChanged = false; + + /** Delinquency Range Displayed Columns */ + displayedColumns: string[] = ['classification', 'minimumAgeDays', 'maximumAgeDays', 'actions']; + + constructor(private formBuilder: FormBuilder, + private productsService: ProductsService, + private router: Router, + private route: ActivatedRoute, + public dialog: MatDialog) { + this.route.data.subscribe((data: { delinquencyBucket: any, delinquencyRanges: any }) => { + this.delinquencyRangesData = data.delinquencyRanges; + this.rangesDataSource = []; + this.delinquencyRangesIds = []; + this.delinquencyRangesData = this.delinquencyRangesData.sort( + (objA: { minimumAgeDays: number; }, objB: { minimumAgeDays: number; }) => objA.minimumAgeDays - objB.minimumAgeDays, + ); + this.delinquencyBucketData = data.delinquencyBucket; + this.delinquencyBucketId = data.delinquencyBucket.id; + this.rangesDataSource = this.delinquencyBucketData.ranges; + this.rangesDataSource.forEach((item: any) => { + this.delinquencyRangesIds.push(item.id); + }); + }); + } + + ngOnInit(): void { + this.setupForm(); + } + + /** + * Creates the Delinquency Bucket form + */ + setupForm(): void { + this.bucketForm = this.formBuilder.group({ + 'name': [{value: this.delinquencyBucketData.name, disabled: true}, Validators.required] + }); + } + + /** + * Add Delinquency Range for a Delinquency Bucket + */ + addDelinquencyRange() { + let delinquencyRanges = this.delinquencyRangesData; + if (this.delinquencyRangesIds.length > 0) { + delinquencyRanges = this.delinquencyRangesData.filter((item: any) => { + return (this.delinquencyRangesIds.indexOf(item.id) < 0); + }); + } + const formfields: FormfieldBase[] = [ + new SelectBase({ + controlName: 'rangeId', + label: 'Delinquency Range', + options: { label: 'classification', value: 'id', data: delinquencyRanges }, + order: 1 + }) + ]; + const data = { + title: 'Add Delinquency Range', + layout: { addButtonText: 'Add' }, + formfields: formfields + }; + const rangeDialogRef = this.dialog.open(FormDialogComponent, { data }); + rangeDialogRef.afterClosed().subscribe((response: any) => { + if (response.data) { + const item = response.data.value; + item.id = item.rangeId; + this.rangesDataSource = this.rangesDataSource.concat(item); + this.delinquencyRangesIds.push(item.id); + this.dataWasChanged = true; + } + }); + } + + /** + * Delete particular Delinquency Range in Delinquency Bucket + */ + deleteDelinquencyRange(index: number) { + const dialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: `this` } + }); + dialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.delinquencyRangesIds.splice(index, 1); + this.rangesDataSource.splice(index, 1); + this.rangesDataSource = this.rangesDataSource.concat([]); + this.dataWasChanged = true; + } + }); + } + + /** + * Submits the Delinquency Bucket form and creates the Delinquency Bucket, + * if successful redirects to Delinquency Buckets. + */ + submit() { + const ranges: any = []; + this.rangesDataSource.forEach((item: any) => { ranges.push(item.id); }); + if (ranges.length > 0) { + const data = { + ...this.bucketForm.value, + ranges: ranges + }; + + this.productsService.updateDelinquencyBucket(this.delinquencyBucketId, data).subscribe(() => { + this.router.navigate(['../'], { relativeTo: this.route }); + }); + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.html b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.html new file mode 100644 index 000000000..cb4b92173 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.html @@ -0,0 +1,62 @@ +
+ + +
+ +
+ + + + + +
+ +
+ Name +
+ +
+ {{ delinquencyBucketData.name }} +
+ +
+ Classification Name +
+ +
+ Days From +
+ +
+ Days Till +
+
+ + +
+ +
+ {{ range.classification }} +
+ +
+ {{ range.minimumAgeDays }} +
+ +
+ {{ range.maximumAgeDays }} +
+ +
+ +
+ +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.scss new file mode 100644 index 000000000..b99efaa71 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.scss @@ -0,0 +1,10 @@ +.container { + max-width: 37rem; + + .content { + div { + margin: 0.8rem 0; + word-wrap: break-word; + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.spec.ts new file mode 100644 index 000000000..e8ca3c81c --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewBucketComponent } from './view-bucket.component'; + +describe('ViewBucketComponent', () => { + let component: ViewBucketComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ViewBucketComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewBucketComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.ts new file mode 100644 index 000000000..de90aae31 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component.ts @@ -0,0 +1,45 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProductsService } from 'app/products/products.service'; +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; + +@Component({ + selector: 'mifosx-view-bucket', + templateUrl: './view-bucket.component.html', + styleUrls: ['./view-bucket.component.scss'] +}) +export class ViewBucketComponent implements OnInit { + + /** Delinquency Bucket Data. */ + delinquencyBucketData: any; + + constructor(private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + private productsService: ProductsService) { + this.route.data.subscribe((data: { delinquencyBucket: any }) => { + this.delinquencyBucketData = data.delinquencyBucket; + this.delinquencyBucketData.ranges = this.delinquencyBucketData.ranges.sort( + (objA: { minimumAge: number; }, objB: { minimumAge: number; }) => objA.minimumAge - objB.minimumAge, + ); + }); + } + + ngOnInit(): void { + } + + deleteDelinquencyBucket() { + const dialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: this.delinquencyBucketData.name } + }); + dialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.productsService.deleteDelinquencyBucket(this.delinquencyBucketData.id).subscribe(() => { + this.router.navigate(['../'], { relativeTo: this.route }); + }); + } + }); + } + +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.html b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.html new file mode 100644 index 000000000..50c5e9370 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.html @@ -0,0 +1,45 @@ +
+ + + +
+ + + +
+ + + Classification + + + Classification is required + + + + + Days From + + + Days From is required + + + + + Days Till + + + +
+ +
+ + + + + + +
+ +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.scss new file mode 100644 index 000000000..b99efaa71 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.scss @@ -0,0 +1,10 @@ +.container { + max-width: 37rem; + + .content { + div { + margin: 0.8rem 0; + word-wrap: break-word; + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.spec.ts new file mode 100644 index 000000000..7e7119c9a --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateRangeComponent } from './create-range.component'; + +describe('CreateRangeComponent', () => { + let component: CreateRangeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CreateRangeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateRangeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.ts new file mode 100644 index 000000000..7b7a76493 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/create-range/create-range.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProductsService } from 'app/products/products.service'; +import { SettingsService } from 'app/settings/settings.service'; + +@Component({ + selector: 'mifosx-create-range', + templateUrl: './create-range.component.html', + styleUrls: ['./create-range.component.scss'] +}) +export class CreateRangeComponent implements OnInit { + + /** Delinquency Range form. */ + delinquencyRangeForm: FormGroup; + + constructor(private formBuilder: FormBuilder, + private productsService: ProductsService, + private route: ActivatedRoute, + private router: Router, + private settingsService: SettingsService) { + } + + ngOnInit(): void { + this.setInputForm(); + } + + /** + * Create Input form. + */ + setInputForm(): void { + this.delinquencyRangeForm = this.formBuilder.group({ + 'classification': ['', [Validators.required]], + 'minimumAgeDays': [0, [Validators.required, Validators.pattern('^(0*[1-9][0-9]*?)$'), Validators.max(1000)]], + 'maximumAgeDays': ['', [Validators.pattern('^(0*[1-9][0-9]*?)$'), Validators.max(10000)]], + }); + } + + submit(): void { + const delinquencyRangeFormData = this.delinquencyRangeForm.value; + const locale = this.settingsService.language.code; + const data = { + ...delinquencyRangeFormData, + locale + }; + this.productsService.createDelinquencyRange(data).subscribe((response: any) => { + this.router.navigate(['../', response.resourceId], { relativeTo: this.route }); + }); + } + +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.html b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.html new file mode 100644 index 000000000..bdd94ddc3 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.html @@ -0,0 +1,45 @@ +
+ +
+ +
+ +
+ + Filter + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
Classification {{ delinquencyRange.classification }} Days From {{ delinquencyRange.minimumAgeDays }} Days Till {{ delinquencyRange.maximumAgeDays }}
+ + + +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.resolver.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.resolver.ts new file mode 100644 index 000000000..90aa71974 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.resolver.ts @@ -0,0 +1,35 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { ProductsService } from '../../products.service'; + +/** + * Delinquency Range Component data resolver. + */ +@Injectable() +export class DelinquencyRangeComponentsResolver implements Resolve { + + /** + * @param {ProductsService} productsService Products service. + */ + constructor(private productsService: ProductsService) {} + + /** + * Returns the delinquency ranges data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const delinquentcyRangeId = route.paramMap.get('rangeId'); + if (delinquentcyRangeId === null) { + return this.productsService.getDelinquencyRanges(); + } else { + return this.productsService.getDelinquencyRange(delinquentcyRangeId); + } + } + +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.scss new file mode 100644 index 000000000..1d6a876d2 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.scss @@ -0,0 +1,18 @@ +.container { + .action-button { + margin-left: auto; + } + .search-button { + min-width: 30px; + margin: 4px; + } +} + +table { + tr.select-row:hover { + cursor: pointer; + } + tr:nth-child(odd).td{ + color : black; + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.spec.ts new file mode 100644 index 000000000..07582e9c5 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DelinquencyRangeComponent } from './delinquency-range.component'; + +describe('DelinquencyRangeComponent', () => { + let component: DelinquencyRangeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DelinquencyRangeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DelinquencyRangeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.ts new file mode 100644 index 000000000..c3b4f944e --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/delinquency-range.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'mifosx-delinquency-range', + templateUrl: './delinquency-range.component.html', + styleUrls: ['./delinquency-range.component.scss'] +}) +export class DelinquencyRangeComponent implements OnInit { + + delinquencyRangeData: any; + /** Columns to be displayed in delinquency range table. */ + displayedColumns: string[] = ['classification', 'minimumAgeDays', 'maximumAgeDays']; + /** Data source for delinquency range table. */ + dataSource: MatTableDataSource; + + /** Paginator for delinquency range table. */ + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + /** Sorter for delinquency range table. */ + @ViewChild(MatSort, { static: true }) sort: MatSort; + + constructor(private route: ActivatedRoute) { + this.route.data.subscribe(( data: { delinquencyRanges: any }) => { + this.delinquencyRangeData = data.delinquencyRanges; + }); + } + + ngOnInit(): void { + this.setDatasource(); + } + + /** + * Filters data in delinquency range table based on passed value. + * @param {string} filterValue Value to filter data. + */ + applyFilter(filterValue: string) { + this.dataSource.filter = filterValue.trim().toLowerCase(); + } + + /** + * Initializes the data source, paginator and sorter for delinquency range table. + */ + setDatasource() { + this.dataSource = new MatTableDataSource(this.delinquencyRangeData); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.html b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.html new file mode 100644 index 000000000..af99eea8a --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.html @@ -0,0 +1,45 @@ +
+ + + +
+ + + +
+ + + Classification + + + Classification is required + + + + + Days From + + + Days From is required + + + + + Days Till + + + +
+ +
+ + + + + + +
+ +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.scss new file mode 100644 index 000000000..b99efaa71 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.scss @@ -0,0 +1,10 @@ +.container { + max-width: 37rem; + + .content { + div { + margin: 0.8rem 0; + word-wrap: break-word; + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.spec.ts new file mode 100644 index 000000000..180bc935a --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditRangeComponent } from './edit-range.component'; + +describe('EditRangeComponent', () => { + let component: EditRangeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ EditRangeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EditRangeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.ts new file mode 100644 index 000000000..40a79f8a5 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component.ts @@ -0,0 +1,56 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProductsService } from 'app/products/products.service'; +import { SettingsService } from 'app/settings/settings.service'; + +@Component({ + selector: 'mifosx-edit-range', + templateUrl: './edit-range.component.html', + styleUrls: ['./edit-range.component.scss'] +}) +export class EditRangeComponent implements OnInit { + + /** Delinquency Range Data. */ + delinquencyRangeData: any; + /** Delinquency Range form. */ + delinquencyRangeForm: FormGroup; + + constructor(private formBuilder: FormBuilder, + private productsService: ProductsService, + private route: ActivatedRoute, + private router: Router, + private settingsService: SettingsService) { + this.route.data.subscribe((data: { delinquencyRange: any }) => { + this.delinquencyRangeData = data.delinquencyRange; + }); + } + + ngOnInit(): void { + this.setInputForm(); + } + + /** + * Create Input form. + */ + setInputForm(): void { + this.delinquencyRangeForm = this.formBuilder.group({ + 'classification': [this.delinquencyRangeData.classification, [Validators.required]], + 'minimumAgeDays': [this.delinquencyRangeData.minimumAgeDays, [Validators.required, Validators.pattern('^(0*[1-9][0-9]*?)$'), Validators.max(1000)]], + 'maximumAgeDays': [this.delinquencyRangeData.maximumAgeDays, [Validators.pattern('^(0*[1-9][0-9]*?)$'), Validators.max(10000)]], + }); + } + + submit(): void { + const delinquencyRangeFormData = this.delinquencyRangeForm.value; + const locale = this.settingsService.language.code; + const data = { + ...delinquencyRangeFormData, + locale + }; + this.productsService.updateDelinquencyRange(this.delinquencyRangeData.id, data).subscribe((response: any) => { + this.router.navigate(['../../', response.resourceId], { relativeTo: this.route }); + }); + } + +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.html b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.html new file mode 100644 index 000000000..6f98a7118 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.html @@ -0,0 +1,50 @@ +
+ + +
+ +
+ + + + + +
+ +
+ Classification +
+ +
+ {{ delinquencyRangeData.classification }} +
+ +
+ Days From +
+ +
+ {{ delinquencyRangeData.minimumAgeDays }} +
+ +
+ Days Till +
+ +
+ {{ delinquencyRangeData.maximumAgeDays }} +
+ +
+ +
+ +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.scss b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.scss new file mode 100644 index 000000000..b99efaa71 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.scss @@ -0,0 +1,10 @@ +.container { + max-width: 37rem; + + .content { + div { + margin: 0.8rem 0; + word-wrap: break-word; + } + } +} diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.spec.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.spec.ts new file mode 100644 index 000000000..8ec4925c0 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewRangeComponent } from './view-range.component'; + +describe('ViewRangeComponent', () => { + let component: ViewRangeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ViewRangeComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewRangeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.ts b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.ts new file mode 100644 index 000000000..0de045c5f --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/delinquency-range/view-range/view-range.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProductsService } from 'app/products/products.service'; +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; + +@Component({ + selector: 'mifosx-view-range', + templateUrl: './view-range.component.html', + styleUrls: ['./view-range.component.scss'] +}) +export class ViewRangeComponent implements OnInit { + + /** Delinquency Range Data. */ + delinquencyRangeData: any; + + constructor(private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + private productsService: ProductsService) { + this.route.data.subscribe((data: { delinquencyRange: any }) => { + this.delinquencyRangeData = data.delinquencyRange; + }); + } + + ngOnInit(): void { + } + + deleteDelinquencyRange() { + const dialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: this.delinquencyRangeData.classification } + }); + dialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.productsService.deleteDelinquencyRange(this.delinquencyRangeData.id).subscribe(() => { + this.router.navigate(['../'], { relativeTo: this.route }); + }); + } + }); + } + +} diff --git a/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.html b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.html new file mode 100644 index 000000000..95b1a2417 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.html @@ -0,0 +1,43 @@ +
+ + + +
+ +
+ + + + + + + +

Manage Delinquency Ranges

+

Define delinquency day ranges

+
+ +
+ +
+ +
+ + + + + + + +

Manage Delinquency Buckets

+

Define delinquency bucket as set of ranges

+
+ +
+ +
+ +
+ +
+ +
diff --git a/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.scss b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.spec.ts b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.spec.ts new file mode 100644 index 000000000..aa068bfc3 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ManageDelinquencyBucketsComponent } from './manage-delinquency-buckets.component'; + +describe('ManageDelinquencyBucketsComponent', () => { + let component: ManageDelinquencyBucketsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ManageDelinquencyBucketsComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ManageDelinquencyBucketsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.ts b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.ts new file mode 100644 index 000000000..02cc93b72 --- /dev/null +++ b/src/app/products/manage-delinquency-buckets/manage-delinquency-buckets.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'mifosx-manage-delinquency-buckets', + templateUrl: './manage-delinquency-buckets.component.html', + styleUrls: ['./manage-delinquency-buckets.component.scss'] +}) +export class ManageDelinquencyBucketsComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/products/products-routing.module.ts b/src/app/products/products-routing.module.ts index 9ab3c244e..579bb63e0 100644 --- a/src/app/products/products-routing.module.ts +++ b/src/app/products/products-routing.module.ts @@ -101,6 +101,17 @@ import { FixedDepositProductAndTemplateResolver } from './fixed-deposit-products import { CollateralResolver } from './collaterals/collateral.resolver'; import { CollateralsResolver } from './collaterals/collaterals.resolver'; import { CollateralTemplateResolver } from './collaterals/collaterals-template.resolver'; +import { ManageDelinquencyBucketsComponent } from './manage-delinquency-buckets/manage-delinquency-buckets.component'; +import { DelinquencyBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component'; +import { DelinquencyRangeComponent } from './manage-delinquency-buckets/delinquency-range/delinquency-range.component'; +import { DelinquencyBucketComponentsResolver } from './manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component.resolver'; +import { DelinquencyRangeComponentsResolver } from './manage-delinquency-buckets/delinquency-range/delinquency-range.component.resolver'; +import { ViewRangeComponent } from './manage-delinquency-buckets/delinquency-range/view-range/view-range.component'; +import { EditRangeComponent } from './manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component'; +import { CreateRangeComponent } from './manage-delinquency-buckets/delinquency-range/create-range/create-range.component'; +import { CreateBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component'; +import { EditBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component'; +import { ViewBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component'; /** Products Routes */ const routes: Routes = [ @@ -377,6 +388,104 @@ const routes: Routes = [ }, ] }, + { + path: 'delinquency-bucket-configurations', + data: { title: extract('Manage Delinquency Bucket Configurations'), breadcrumb: 'Manage Delinquency Bucket Configurations' }, + children: [ + { + path: '', + component: ManageDelinquencyBucketsComponent, + }, + { + path: 'ranges', + data: { title: extract('Manage Delinquency Ranges'), breadcrumb: 'Delinquency Ranges'}, + children: [ + { + path: '', + component: DelinquencyRangeComponent, + resolve: { + delinquencyRanges: DelinquencyRangeComponentsResolver + } + }, + { + path: 'create', + component: CreateRangeComponent, + data: { title: extract('Create Delinquency Range'), breadcrumb: 'Create' } + }, + { + path: ':rangeId', + data: { title: extract('View Delinquency Range'), routeParamBreadcrumb: 'id' }, + resolve: { + delinquencyRange: DelinquencyRangeComponentsResolver + }, + children: [ + { + path: '', + component: ViewRangeComponent, + resolve: { + delinquencyRange: DelinquencyRangeComponentsResolver + }, + }, + { + path: 'edit', + data: { title: extract('Edit Delinquency Range'), breadcrumb: 'Edit', routeParamBreadcrumb: false }, + component: EditRangeComponent, + resolve: { + delinquencyRange: DelinquencyRangeComponentsResolver + } + }, + ] + } + ] + }, + { + path: 'buckets', + data: { title: extract('Manage Delinquency Bucket'), breadcrumb: 'Delinquency Buckets'}, + children: [ + { + path: '', + component: DelinquencyBucketComponent, + resolve: { + delinquencyBuckets: DelinquencyBucketComponentsResolver + } + }, + { + path: 'create', + component: CreateBucketComponent, + data: { title: extract('Create Delinquency Bucket'), breadcrumb: 'Create' }, + resolve: { + delinquencyRanges: DelinquencyRangeComponentsResolver + } + }, + { + path: ':bucketId', + data: { title: extract('View Delinquency Bucket'), routeParamBreadcrumb: 'id' }, + resolve: { + delinquencyBucket: DelinquencyBucketComponentsResolver + }, + children: [ + { + path: '', + component: ViewBucketComponent, + resolve: { + delinquencyBucket: DelinquencyBucketComponentsResolver + }, + }, + { + path: 'edit', + data: { title: extract('Edit Delinquency Bucket'), breadcrumb: 'Edit', routeParamBreadcrumb: false }, + component: EditBucketComponent, + resolve: { + delinquencyBucket: DelinquencyBucketComponentsResolver, + delinquencyRanges: DelinquencyRangeComponentsResolver + } + }, + ] + } + ] + }, + ] + }, { path: 'recurring-deposit-products', data: { title: extract('Recurring Deposit Products'), breadcrumb: 'Recurring Deposit Products' }, @@ -703,7 +812,9 @@ const routes: Routes = [ FloatingRatesResolver, CollateralResolver, CollateralsResolver, - CollateralTemplateResolver + CollateralTemplateResolver, + DelinquencyRangeComponentsResolver, + DelinquencyBucketComponentsResolver ] }) export class ProductsRoutingModule { } diff --git a/src/app/products/products.component.html b/src/app/products/products.component.html index efc2e578a..c79b835e3 100644 --- a/src/app/products/products.component.html +++ b/src/app/products/products.component.html @@ -48,6 +48,14 @@

Define collaterals for Collateral Management

+ + + + +

Delinquency Buckets

+

Define delinquency day ranges and bucket set for loan products

+
+ diff --git a/src/app/products/products.module.ts b/src/app/products/products.module.ts index 049af78b6..7872fef26 100644 --- a/src/app/products/products.module.ts +++ b/src/app/products/products.module.ts @@ -97,6 +97,15 @@ import { CollateralsComponent } from './collaterals/collaterals.component'; import { CreateCollateralComponent } from './collaterals/create-collateral/create-collateral.component'; import { EditCollateralComponent } from './collaterals/edit-collateral/edit-collateral.component'; import { ViewCollateralComponent } from './collaterals/view-collateral/view-collateral.component'; +import { ManageDelinquencyBucketsComponent } from './manage-delinquency-buckets/manage-delinquency-buckets.component'; +import { DelinquencyBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/delinquency-bucket.component'; +import { DelinquencyRangeComponent } from './manage-delinquency-buckets/delinquency-range/delinquency-range.component'; +import { CreateRangeComponent } from './manage-delinquency-buckets/delinquency-range/create-range/create-range.component'; +import { ViewRangeComponent } from './manage-delinquency-buckets/delinquency-range/view-range/view-range.component'; +import { EditRangeComponent } from './manage-delinquency-buckets/delinquency-range/edit-range/edit-range.component'; +import { ViewBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/view-bucket/view-bucket.component'; +import { CreateBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/create-bucket/create-bucket.component'; +import { EditBucketComponent } from './manage-delinquency-buckets/delinquency-bucket/edit-bucket/edit-bucket.component'; /** * Products Module @@ -201,7 +210,16 @@ import { ViewCollateralComponent } from './collaterals/view-collateral/view-coll CollateralsComponent, CreateCollateralComponent, EditCollateralComponent, - ViewCollateralComponent + ViewCollateralComponent, + ManageDelinquencyBucketsComponent, + DelinquencyBucketComponent, + DelinquencyRangeComponent, + CreateRangeComponent, + ViewRangeComponent, + EditRangeComponent, + ViewBucketComponent, + CreateBucketComponent, + EditBucketComponent ], providers: [ ] }) diff --git a/src/app/products/products.service.ts b/src/app/products/products.service.ts index 7fb060fab..d60627239 100644 --- a/src/app/products/products.service.ts +++ b/src/app/products/products.service.ts @@ -299,6 +299,86 @@ export class ProductsService { return this.http.put(`/taxes/group/${taxGroupId}`, taxGroup); } + /** + * @returns {Observable} Delinquency Range data + */ + getDelinquencyRanges(): Observable { + return this.http.get('/delinquency/ranges'); + } + + /** + * @param {any} delinquencyRateId Delinquency Range Id. + * @returns {Observable} Delinquency Range data. + */ + getDelinquencyRange(delinquencyRateId: any): Observable { + return this.http.get(`/delinquency/ranges/${delinquencyRateId}`); + } + + /** + * @param {any} payload Delinquency Range data + * @returns {Observable} Delinquency Range Resource Id + */ + createDelinquencyRange(payload: any): Observable { + return this.http.post('/delinquency/ranges', payload); + } + + /** + * @param {any} delinquencyRateId Delinquency Range ID + * @param {any} payload Delinquency Range data + * @returns {Observable} Changes in the Delinquency Range + */ + updateDelinquencyRange(delinquencyRateId: any, payload: any): Observable { + return this.http.put(`/delinquency/ranges/${delinquencyRateId}`, payload); + } + + /** + * @param {any} delinquencyRateId Delinquency Range ID + * @returns {Observable} Changes in the Delinquency Range + */ + deleteDelinquencyRange(delinquencyRateId: any): Observable { + return this.http.delete(`/delinquency/ranges/${delinquencyRateId}`); + } + + /** + * @returns {Observable} Delinquency Bucket data + */ + getDelinquencyBuckets(): Observable { + return this.http.get('/delinquency/buckets'); + } + + /** + * @param {any} delinquencyBucketId Delinquency Bucket Id. + * @returns {Observable} Delinquency Bucket data. + */ + getDelinquencyBucket(delinquencyBucketId: any): Observable { + return this.http.get(`/delinquency/buckets/${delinquencyBucketId}`); + } + + /** + * @param {any} payload Delinquency Bucket data + * @returns {Observable} Delinquency Bucket Resource Id + */ + createDelinquencyBucket(payload: any): Observable { + return this.http.post('/delinquency/buckets', payload); + } + + /** + * @param {any} delinquencyBucketId Delinquency Bucket ID + * @param {any} payload Delinquency Bucket data + * @returns {Observable} Changes in the Delinquency Bucket + */ + updateDelinquencyBucket(delinquencyBucketId: any, payload: any): Observable { + return this.http.put(`/delinquency/buckets/${delinquencyBucketId}`, payload); + } + + /** + * @param {any} delinquencyBucketId Delinquency Bucket ID + * @returns {Observable} Changes in the Delinquency Bucket + */ + deleteDelinquencyBucket(delinquencyBucketId: any): Observable { + return this.http.delete(`/delinquency/buckets/${delinquencyBucketId}`); + } + /** * @returns {Observable} Product mixes data */