WEB-642: Add Originators tab on loan details view (#3064)

Co-authored-by: Jose Alberto Hernandez <alberto@black-box.local>
This commit is contained in:
Jose Alberto Hernandez
2026-02-02 09:16:25 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent 53427418c7
commit 1577e9dd8e
21 changed files with 1583 additions and 1328 deletions
@@ -0,0 +1,31 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Injectable, inject } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router';
import { LoansService } from 'app/loans/loans.service';
import { Observable, throwError } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LoanOriginatorsResolver {
private loansService = inject(LoansService);
/**
* Returns the Loans data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const loanId = route.paramMap.get('loanId') ?? route.parent?.paramMap.get('loanId');
if (!loanId) {
return throwError(() => new Error('Missing loanId route param'));
}
return this.loansService.getLoanOriginators(loanId);
}
}
+16 -4
View File
@@ -74,6 +74,8 @@ import { LoanTermVariationsResolver } from './common-resolvers/loan-term-variati
import { LoanDeferredIncomeTabComponent } from './loans-view/loan-deferred-income-tab/loan-deferred-income-tab.component';
import { LoanDeferredIncomeDataResolver } from './common-resolvers/loan-deferred-income-data.resolver';
import { LoanBuyDownFeesDataResolver } from './common-resolvers/loan-buy-down-fees-data.resolver';
import { LoanOriginatorsTabComponent } from './loans-view/loan-originators-tab/loan-originators-tab.component';
import { LoanOriginatorsResolver } from './common-resolvers/loan-originators.resolver';
/** Loans Route. */
const routes: Routes = [
@@ -108,10 +110,7 @@ const routes: Routes = [
path: 'general',
component: GeneralTabComponent,
data: { title: 'General', breadcrumb: 'General', routeParamBreadcrumb: false },
resolve: {
loanDetailsData: LoanDetailsResolver,
loanDatatables: LoanDatatablesResolver
}
resolve: {}
},
{
path: 'accountdetail',
@@ -271,6 +270,19 @@ const routes: Routes = [
}
]
},
{
path: 'originators',
data: { title: 'Loans Originators', breadcrumb: 'Originators', routeParamBreadcrumb: false },
resolve: {
loanOriginatorsData: LoanOriginatorsResolver
},
children: [
{
path: '',
component: LoanOriginatorsTabComponent
}
]
},
{
path: 'buy-down-fees',
component: LoanBuyDownFeesTabComponent,
@@ -0,0 +1,52 @@
<!--
Copyright since 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="container">
<h3>{{ 'labels.heading.Loan Originators' | translate }}</h3>
<table mat-table [dataSource]="loanOriginatorsData">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Id' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.id }}
</td>
</ng-container>
<ng-container matColumnDef="externalId">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.External Id' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.externalId }}
</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.name }}
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Status' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.status }}
</td>
</ng-container>
<ng-container matColumnDef="originatorTypeId">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Originator Type' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.originatorTypeId }}
</td>
</ng-container>
<ng-container matColumnDef="channelTypeId">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Channel Type' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.channelTypeId }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="loanoriginatorsColumns"></tr>
<tr mat-row *matRowDef="let row; columns: loanoriginatorsColumns"></tr>
</table>
</div>
@@ -0,0 +1,16 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
table {
width: 100%;
}
.container {
padding-top: 1%;
padding-bottom: 2%;
}
@@ -0,0 +1,65 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, inject } from '@angular/core';
import {
MatCell,
MatCellDef,
MatColumnDef,
MatHeaderCell,
MatHeaderCellDef,
MatHeaderRow,
MatHeaderRowDef,
MatRow,
MatRowDef,
MatTable
} from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { LoanOriginator } from 'app/loans/models/loan-account.model';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
selector: 'mifosx-loan-originators-tab',
templateUrl: './loan-originators-tab.component.html',
styleUrl: './loan-originators-tab.component.scss',
imports: [
...STANDALONE_SHARED_IMPORTS,
MatTable,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow
]
})
export class LoanOriginatorsTabComponent {
private route = inject(ActivatedRoute);
loanOriginatorsData: LoanOriginator[] = [];
loanId: number | null = null;
loanoriginatorsColumns: string[] = [
'id',
'externalId',
'name',
'status',
'originatorTypeId',
'channelTypeId'
];
constructor() {
const loanIdParam = this.route.parent?.parent?.snapshot.paramMap.get('loanId');
this.loanId = loanIdParam ? Number(loanIdParam) : null;
this.route.parent.data.subscribe((data: { loanOriginatorsData: any }) => {
this.loanOriginatorsData = data.loanOriginatorsData.originators;
});
}
}
@@ -278,6 +278,15 @@
{{ 'labels.inputs.Delinquency Tags' | translate }}
</a>
}
<a
mat-tab-link
[routerLink]="['./originators']"
routerLinkActive
#originators="routerLinkActive"
[active]="originators.isActive"
>
{{ 'labels.inputs.Originators' | translate }}
</a>
@if (loanDetailsData.collateral) {
<a
mat-tab-link
+7
View File
@@ -771,4 +771,11 @@ export class LoansService {
getLoanDisbursementDetailsData(): DisbursementData[] {
return JSON.parse(localStorage.getItem('disbursementData'));
}
/**
* Returns the Loan Originators data
*/
getLoanOriginators(loanId: any) {
return this.http.get(`/loans/${loanId}/originators`);
}
}
@@ -172,3 +172,12 @@ export interface ScheduleChangeRecord {
dueDate: string;
installmentAmount: number;
}
export interface LoanOriginator {
id: number;
externalId: string;
name: string;
status: string;
originatorTypeId: number;
channelTypeId: number;
}