WEB-642: Add Loan Originators management (#3095)

Co-authored-by: Jose Alberto Hernandez <alberto@black-box.local>
This commit is contained in:
Jose Alberto Hernandez
2026-02-06 12:15:52 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent bd2408fcc4
commit 0bc1926199
44 changed files with 1405 additions and 43 deletions
@@ -15,6 +15,7 @@ import { Observable } from 'rxjs';
/** Custom Services */
import { LoansService } from '../loans.service';
import { OrganizationService } from 'app/organization/organization.service';
/**
* Loans notes data resolver.
@@ -22,6 +23,7 @@ import { LoansService } from '../loans.service';
@Injectable()
export class LoanActionButtonResolver {
private loansService = inject(LoansService);
private organizationService = inject(OrganizationService);
/**
* Returns the Loans Notes Data.
@@ -88,6 +90,8 @@ export class LoanActionButtonResolver {
return this.loansService.getLoanActionTemplate(loanId, 'reAge');
} else if (loanActionButton === 'Re-Amortize') {
return this.loansService.getLoanActionTemplate(loanId, 'reAmortization');
} else if (loanActionButton === 'Attach Loan Originator') {
return this.organizationService.getLoanOriginators();
} else {
return undefined;
}
@@ -56,17 +56,6 @@
<input matInput [value]="loanData.availableDisbursementAmountWithOverApplied | formatNumber" readonly />
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Transaction Amount' | translate }}</mat-label>
<input matInput type="number" required formControlName="approvedLoanAmount" />
@if (approveLoanForm.controls.approvedLoanAmount.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Transaction Amount' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Note' | translate }}</mat-label>
<textarea matInput formControlName="note" cdkTextareaAutosize cdkAutosizeMinRows="2"></textarea>
@@ -0,0 +1,42 @@
<!--
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 mat-elevation-z8">
<mat-card>
<form [formGroup]="attachLoanOriginatorForm" (ngSubmit)="submit()">
<mat-card-content>
<div class="layout-column">
<mat-form-field>
<mat-label>{{ 'labels.inputs.Loan Originator' | translate }}</mat-label>
<mat-select formControlName="originatorId">
@for (loanOriginator of loanOriginators; track loanOriginator) {
<mat-option [value]="loanOriginator.id">
{{ loanOriginator.name }}
</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<mat-card-actions class="layout-row align-center gap-5px responsive-column">
<button type="button" mat-raised-button [routerLink]="['../../general']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button
mat-raised-button
color="primary"
[disabled]="!attachLoanOriginatorForm.valid"
*mifosxHasPermission="'ATTACH_LOAN_ORIGINATOR'"
>
{{ 'labels.buttons.Submit' | translate }}
</button>
</mat-card-actions>
</mat-card-content>
</form>
</mat-card>
</div>
@@ -0,0 +1,11 @@
/**
* 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/.
*/
.container {
max-width: 37rem;
}
@@ -0,0 +1,81 @@
/**
* 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/.
*/
/** Angular Imports. */
import { Component, Input, OnInit, inject } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
/** Custom Services. */
import { LoansService } from 'app/loans/loans.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { LoanOriginator } from 'app/loans/models/loan-account.model';
/**
* Attach Loan Originator component.
*/
@Component({
selector: 'mifosx-attach-originator',
templateUrl: './attach-originator.component.html',
styleUrl: './attach-originator.component.scss',
imports: [
...STANDALONE_SHARED_IMPORTS
]
})
export class AttachOriginatorComponent implements OnInit {
private formBuilder = inject(UntypedFormBuilder);
private route = inject(ActivatedRoute);
private loanService = inject(LoansService);
private router = inject(Router);
/** Attach Loan Originator Loan form. */
attachLoanOriginatorForm: UntypedFormGroup;
/** Loan data. */
loanOriginators: LoanOriginator[] = [];
@Input() dataObject: any;
/** Loan Id */
loanId: string | null = null;
constructor() {
this.loanId = this.route.snapshot.params['loanId'];
}
ngOnInit() {
this.setAttachLoanOriginatorForm();
console.log(this.dataObject);
this.loanOriginators = [];
this.dataObject.forEach((loanOriginator: LoanOriginator) => {
if (loanOriginator.status === 'ACTIVE') {
this.loanOriginators.push(loanOriginator);
}
});
}
/**
* Set Attach Loan Originator form.
*/
setAttachLoanOriginatorForm() {
this.attachLoanOriginatorForm = this.formBuilder.group({
originatorId: [
'',
Validators.required
]
});
}
/**
* Submits Approve form.
*/
submit() {
const approveLoanFormData = this.attachLoanOriginatorForm.value;
this.loanService.attachLoanOriginator(this.loanId, approveLoanFormData.originatorId).subscribe((response: any) => {
this.router.navigate(['../../general'], { relativeTo: this.route });
});
}
}
@@ -114,3 +114,6 @@
@if (actions['Undo Write-off']) {
<mifosx-undo-write-off [dataObject]="actionButtonData"></mifosx-undo-write-off>
}
@if (actions['Attach Loan Originator']) {
<mifosx-attach-originator [dataObject]="actionButtonData"></mifosx-attach-originator>
}
@@ -40,6 +40,7 @@ import { LoanReamortizeComponent } from './loan-reamortize/loan-reamortize.compo
import { AddInterestPauseComponent } from './add-interest-pause/add-interest-pause.component';
import { UndoWriteOffComponent } from './undo-write-off/undo-write-off.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { AttachOriginatorComponent } from './attach-originator/attach-originator.component';
/**
* Loan Account Actions component.
@@ -79,7 +80,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
LoanReagingComponent,
LoanReamortizeComponent,
AddInterestPauseComponent,
UndoWriteOffComponent
UndoWriteOffComponent,
AttachOriginatorComponent
]
})
export class LoanAccountActionsComponent {
@@ -130,6 +132,7 @@ export class LoanAccountActionsComponent {
'Contract Termination': boolean;
'Buy Down Fee': boolean;
'Undo Write-off': boolean;
'Attach Loan Originator': boolean;
} = {
Close: false,
'Undo Approval': false,
@@ -169,7 +172,8 @@ export class LoanAccountActionsComponent {
'Capitalized Income': false,
'Contract Termination': false,
'Buy Down Fee': false,
'Undo Write-off': false
'Undo Write-off': false,
'Attach Loan Originator': false
};
actionButtonData: any;
@@ -90,6 +90,11 @@ export class LoansAccountButtonConfiguration {
name: 'Reject',
icon: 'times',
taskPermissionName: 'REJECT_LOAN'
},
{
name: 'Attach Loan Originator',
icon: 'edit',
taskPermissionName: 'ATTACH_LOAN_ORIGINATOR'
}
];
break;
@@ -34,16 +34,35 @@
{{ item.status }}
</td>
</ng-container>
<ng-container matColumnDef="originatorTypeId">
<ng-container matColumnDef="originatorType">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Originator Type' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.originatorTypeId }}
{{ item.originatorType.name }}
</td>
</ng-container>
<ng-container matColumnDef="channelTypeId">
<ng-container matColumnDef="channelType">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Channel Type' | translate }}</th>
<td mat-cell *matCellDef="let item">
{{ item.channelTypeId }}
{{ item.channelType.name }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
<td mat-cell *matCellDef="let loanOriginator">
@if (loanStatus.pendingApproval) {
<button
type="button"
color="warn"
title="{{ 'tooltips.Dettach' | translate }}"
mat-icon-button
matTooltip="{{ 'tooltips.Delete' | translate }}"
*mifosxHasPermission="'DETTACH_LOAN_ORIGINATOR'"
matTooltipPosition="above"
(click)="dettachLoanOriginator(loanOriginator)"
>
<fa-icon icon="trash" size="lg"></fa-icon>
</button>
}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="loanoriginatorsColumns"></tr>
@@ -6,6 +6,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, inject } from '@angular/core';
import { MatIconButton } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import {
MatCell,
MatCellDef,
@@ -18,8 +20,13 @@ import {
MatRowDef,
MatTable
} from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { TranslateService } from '@ngx-translate/core';
import { LoansService } from 'app/loans/loans.service';
import { LoanOriginator } from 'app/loans/models/loan-account.model';
import { LoanStatus } from 'app/loans/models/loan-status.model';
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
@@ -37,29 +44,73 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow
MatRow,
MatIconButton,
FaIconComponent
]
})
export class LoanOriginatorsTabComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
private loansService = inject(LoansService);
private translateService = inject(TranslateService);
private dialog = inject(MatDialog);
loanOriginatorsData: LoanOriginator[] = [];
loanId: number | null = null;
loanId: string | null = null;
clientId: string | null = null;
loanStatus: LoanStatus | null = null;
loanoriginatorsColumns: string[] = [
'id',
'externalId',
'name',
'status',
'originatorTypeId',
'channelTypeId'
'originatorType',
'channelType',
'actions'
];
constructor() {
const loanIdParam = this.route.parent?.parent?.snapshot.paramMap.get('loanId');
this.loanId = loanIdParam ? Number(loanIdParam) : null;
this.clientId = this.route.parent.parent.snapshot.paramMap.get('clientId');
this.loanId = this.route.parent?.parent?.snapshot.paramMap.get('loanId');
this.route.parent.parent.data.subscribe((data: { loanDetailsData: any }) => {
this.loanStatus = data.loanDetailsData.status;
});
this.route.parent.data.subscribe((data: { loanOriginatorsData: any }) => {
this.loanOriginatorsData = data.loanOriginatorsData.originators;
});
}
dettachLoanOriginator(loanOriginator: LoanOriginator): void {
const dettachCodeDialogRef = this.dialog.open(ConfirmationDialogComponent, {
data: {
heading: this.translateService.instant('labels.heading.Loan Originators'),
dialogContext:
this.translateService.instant('labels.buttons.Delete') +
' ' +
this.translateService.instant('labels.inputs.Loan Originator') +
' ' +
loanOriginator.name
}
});
dettachCodeDialogRef.afterClosed().subscribe((response: any) => {
if (response.confirm) {
this.loansService.dettachLoanOriginator(this.loanId, String(loanOriginator.id)).subscribe((response) => {
this.reload();
});
}
});
}
/**
* Refetches data fot the component
*/
private reload() {
const url: string = this.router.url;
this.router
.navigateByUrl(`/clients/${this.clientId}/loans-accounts`, { skipLocationChange: true })
.then(() => this.router.navigate([url]));
}
}
+10
View File
@@ -658,6 +658,16 @@ export class LoansService {
return this.http.post('/loans?command=calculateLoanSchedule', payload);
}
attachLoanOriginator(loanId: string, originatorId: string): Observable<any> {
const emptyBody = {};
return this.http.post(`/loans/${loanId}/originators/${originatorId}`, emptyBody);
}
dettachLoanOriginator(loanId: string, originatorId: string): Observable<any> {
const emptyBody = {};
return this.http.delete(`/loans/${loanId}/originators/${originatorId}`, emptyBody);
}
/**
* @param loansAccount Loan account data used for the request
* @param loansAccountTemplate Loan account template for getting product default values
+3 -3
View File
@@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Currency } from 'app/shared/models/general.model';
import { CodeValue, Currency } from 'app/shared/models/general.model';
export interface DelinquencyRange {
id: number;
@@ -178,6 +178,6 @@ export interface LoanOriginator {
externalId: string;
name: string;
status: string;
originatorTypeId: number;
channelTypeId: number;
originatorType: CodeValue;
channelType: CodeValue;
}
@@ -0,0 +1,116 @@
<!--
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">
<mat-card>
<form [formGroup]="loanOriginatorForm" (ngSubmit)="submit()">
<mat-card-content>
<div #createLoanOriginatorFormRef class="layout-column">
<mat-form-field>
<mat-label>{{ 'labels.inputs.Name' | translate }}</mat-label>
<input matInput required formControlName="name" />
@if (loanOriginatorForm.controls.name.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Name' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
@if (loanOriginatorForm.controls.name.hasError('pattern')) {
<mat-error>
{{ 'labels.inputs.Name' | translate }} <strong>{{ 'labels.inputs.cannot' | translate }}</strong>
{{ 'labels.inputs.begin with a special character or number' | translate }}
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.External Id' | translate }}</mat-label>
<input matInput required formControlName="externalId" />
@if (loanOriginatorForm.controls.externalId.hasError('required')) {
<mat-error>
{{ 'labels.inputs.External Id' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
@if (loanOriginatorForm.controls.externalId.hasError('pattern')) {
<mat-error>
{{ 'labels.inputs.External Id' | translate }} <strong>{{ 'labels.inputs.cannot' | translate }}</strong>
{{ 'labels.inputs.begin with a special character or number' | translate }}
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Status' | translate }}</mat-label>
<mat-select required formControlName="status">
@for (status of statusOptions; track status) {
<mat-option [value]="status">
{{ 'tooltips.' + status | translate }}
</mat-option>
}
</mat-select>
@if (loanOriginatorForm.controls.status.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Status' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Originator Type' | translate }}</mat-label>
<mat-select formControlName="originatorTypeId">
@for (originatorType of originatorTypeOptions; track originatorType) {
<mat-option [value]="originatorType.id">
{{ originatorType.name }}
</mat-option>
}
</mat-select>
@if (loanOriginatorForm.controls.originatorTypeId.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Originator Type' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Channel Type' | translate }}</mat-label>
<mat-select formControlName="channelTypeId">
@for (channelTypeI of channelTypeOptions; track channelTypeI) {
<mat-option [value]="channelTypeI.id">
{{ channelTypeI.name }}
</mat-option>
}
</mat-select>
@if (loanOriginatorForm.controls.channelTypeId.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Channel Type' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
</div>
</mat-card-content>
<mat-card-actions class="layout-row align-center gap-5px responsive-column">
<button type="button" mat-raised-button [routerLink]="['../']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button
mat-raised-button
color="primary"
[disabled]="!loanOriginatorForm.valid"
*mifosxHasPermission="'CREATE_LOAN_ORIGINATOR'"
>
{{ 'labels.buttons.Submit' | translate }}
</button>
</mat-card-actions>
</form>
</mat-card>
</div>
@@ -0,0 +1,11 @@
/**
* 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/.
*/
.container {
max-width: 37rem;
}
@@ -0,0 +1,107 @@
/**
* 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/.
*/
/** Angular Imports */
import { Component, OnInit, TemplateRef, ElementRef, ViewChild, inject } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
/** Custom Services */
import { OrganizationService } from '../../organization.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { CodeValue } from 'app/shared/models/general.model';
/**
* Create Loan Originator component.
*/
@Component({
selector: 'mifosx-create-loan-originator',
templateUrl: './create-loan-originator.component.html',
styleUrl: './create-loan-originator.component.scss',
imports: [
...STANDALONE_SHARED_IMPORTS
]
})
export class CreateLoanOriginatorComponent implements OnInit {
private formBuilder = inject(UntypedFormBuilder);
private organizationService = inject(OrganizationService);
private route = inject(ActivatedRoute);
private router = inject(Router);
/** Loan Originator form. */
loanOriginatorForm: UntypedFormGroup;
/** Form data. */
loanOriginatorsTemplateData: any;
statusOptions: string[] = [];
originatorTypeOptions: CodeValue[] = [];
channelTypeOptions: CodeValue[] = [];
/* Reference of Loan Originator form */
@ViewChild('createLoanOriginatorFormRef') createLoanOriginatorFormRef: ElementRef<any>;
/* Template for popover on Loan Originator form */
@ViewChild('templateCreateLoanOriginatorForm') templateCreateLoanOriginatorForm: TemplateRef<any>;
constructor() {
this.route.data.subscribe((data: { loanOriginatorsTemplateData: any }) => {
this.loanOriginatorsTemplateData = data.loanOriginatorsTemplateData;
this.statusOptions = data.loanOriginatorsTemplateData.statusOptions;
this.originatorTypeOptions = data.loanOriginatorsTemplateData.originatorTypeOptions;
this.channelTypeOptions = data.loanOriginatorsTemplateData.channelTypeOptions;
});
}
/**
* Creates the Loan Originator form.
*/
ngOnInit() {
this.createLoanOriginatorForm();
}
/**
* Creates the Loan Originator form.
*/
createLoanOriginatorForm() {
this.loanOriginatorForm = this.formBuilder.group({
externalId: [
this.loanOriginatorsTemplateData.externalId,
Validators.required
],
name: [
'',
[
Validators.required,
Validators.pattern('(^[A-z]).*')
]
],
status: [
'',
Validators.required
],
originatorTypeId: [
''
],
channelTypeId: [
''
]
});
}
/**
* Submits the Loan Originator form and creates Loan Originator,
* if successful redirects to Loan Originators.
*/
submit() {
const loanOriginatorFormData = this.loanOriginatorForm.value;
const data = {
...loanOriginatorFormData
};
this.organizationService.createLoanOriginator(data).subscribe((response: any) => {
this.router.navigate(['../'], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,116 @@
<!--
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">
<mat-card>
<form [formGroup]="loanOriginatorForm" (ngSubmit)="submit()">
<mat-card-content>
<div #editEmployeeFormRef class="layout-column">
<mat-form-field>
<mat-label>{{ 'labels.inputs.Name' | translate }}</mat-label>
<input matInput required formControlName="name" />
@if (loanOriginatorForm.controls.name.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Name' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
@if (loanOriginatorForm.controls.name.hasError('pattern')) {
<mat-error>
{{ 'labels.inputs.Name' | translate }} <strong>{{ 'labels.inputs.cannot' | translate }}</strong>
{{ 'labels.inputs.begin with a special character or number' | translate }}
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.External Id' | translate }}</mat-label>
<input matInput formControlName="externalId" readonly />
@if (loanOriginatorForm.controls.externalId.hasError('required')) {
<mat-error>
{{ 'labels.inputs.External Id' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
@if (loanOriginatorForm.controls.externalId.hasError('pattern')) {
<mat-error>
{{ 'labels.inputs.External Id' | translate }} <strong>{{ 'labels.inputs.cannot' | translate }}</strong>
{{ 'labels.inputs.begin with a special character or number' | translate }}
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Status' | translate }}</mat-label>
<mat-select required formControlName="status">
@for (status of statusOptions; track status) {
<mat-option [value]="status">
{{ 'tooltips.' + status | translate }}
</mat-option>
}
</mat-select>
@if (loanOriginatorForm.controls.status.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Status' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Originator Type' | translate }}</mat-label>
<mat-select formControlName="originatorTypeId">
@for (originatorType of originatorTypeOptions; track originatorType) {
<mat-option [value]="originatorType.id">
{{ originatorType.name }}
</mat-option>
}
</mat-select>
@if (loanOriginatorForm.controls.originatorTypeId.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Originator Type' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Channel Type' | translate }}</mat-label>
<mat-select formControlName="channelTypeId">
@for (channelTypeI of channelTypeOptions; track channelTypeI) {
<mat-option [value]="channelTypeI.id">
{{ channelTypeI.name }}
</mat-option>
}
</mat-select>
@if (loanOriginatorForm.controls.channelTypeId.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Channel Type' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
</div>
</mat-card-content>
<mat-card-actions class="layout-row align-center gap-5px responsive-column">
<button type="button" mat-raised-button [routerLink]="['../']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button
mat-raised-button
color="primary"
[disabled]="!loanOriginatorForm.valid"
*mifosxHasPermission="'UPDATE_LOAN_ORIGINATOR'"
>
{{ 'labels.buttons.Submit' | translate }}
</button>
</mat-card-actions>
</form>
</mat-card>
</div>
@@ -0,0 +1,11 @@
/**
* 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/.
*/
.container {
max-width: 37rem;
}
@@ -0,0 +1,113 @@
/**
* 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/.
*/
/** Angular Imports */
import { Component, OnInit, TemplateRef, ElementRef, ViewChild, inject } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
/** Custom Services */
import { OrganizationService } from '../../organization.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { CodeValue } from 'app/shared/models/general.model';
import { LoanOriginator } from 'app/loans/models/loan-account.model';
/**
* Create Loan Originator component.
*/
@Component({
selector: 'mifosx-edit-loan-originator',
templateUrl: './edit-loan-originator.component.html',
styleUrl: './edit-loan-originator.component.scss',
imports: [
...STANDALONE_SHARED_IMPORTS
]
})
export class EditLoanOriginatorComponent implements OnInit {
private formBuilder = inject(UntypedFormBuilder);
private organizationService = inject(OrganizationService);
private route = inject(ActivatedRoute);
private router = inject(Router);
/** Loan Originator form. */
loanOriginatorForm: UntypedFormGroup;
/** Form data. */
loanOriginatorsData: LoanOriginator;
loanOriginatorsTemplateData: any;
statusOptions: string[] = [];
originatorTypeOptions: CodeValue[] = [];
channelTypeOptions: CodeValue[] = [];
/* Reference of Loan Originator form */
@ViewChild('editLoanOriginatorFormRef') editLoanOriginatorFormRef: ElementRef<any>;
/* Template for popover on Loan Originator form */
@ViewChild('templateCreateLoanOriginatorForm') templateCreateLoanOriginatorForm: TemplateRef<any>;
constructor() {
this.route.data.subscribe((data: { loanOriginatorData: LoanOriginator; loanOriginatorsTemplateData: any }) => {
this.loanOriginatorsData = data.loanOriginatorData;
this.loanOriginatorsTemplateData = data.loanOriginatorsTemplateData;
this.statusOptions = data.loanOriginatorsTemplateData.statusOptions;
this.originatorTypeOptions = data.loanOriginatorsTemplateData.originatorTypeOptions;
this.channelTypeOptions = data.loanOriginatorsTemplateData.channelTypeOptions;
});
}
/**
* Creates the Loan Originator form.
*/
ngOnInit() {
this.createLoanOriginatorForm();
}
/**
* Creates the Loan Originator form.
*/
createLoanOriginatorForm() {
this.loanOriginatorForm = this.formBuilder.group({
externalId: [
{
value: this.loanOriginatorsTemplateData.externalId,
disabled: true
}
],
name: [
this.loanOriginatorsData.name,
[
Validators.required,
Validators.pattern('^[A-Za-z].*')
]
],
status: [
this.loanOriginatorsData.status,
Validators.required
],
originatorTypeId: [
this.loanOriginatorsData.originatorType.id
],
channelTypeId: [
this.loanOriginatorsData.channelType.id
]
});
}
/**
* Submits the Loan Originator form and creates Loan Originator,
* if successful redirects to Loan Originators.
*/
submit() {
const loanOriginatorFormData = this.loanOriginatorForm.value;
delete loanOriginatorFormData.externalId;
const data = {
...loanOriginatorFormData
};
this.organizationService.updateLoanOriginator(this.loanOriginatorsData.id, data).subscribe((response: any) => {
this.router.navigate(['../..'], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,32 @@
/**
* 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/.
*/
/** Angular Imports */
import { Injectable, inject } from '@angular/core';
/** rxjs Imports */
import { Observable } from 'rxjs';
/** Custom Services */
import { OrganizationService } from '../organization.service';
/**
* Loan Originators data resolver.
*/
@Injectable()
export class LoanOriginatorsTemplateResolver {
private organizationService = inject(OrganizationService);
/**
* Returns the Loan Originators data.
* @returns {Observable<any>}
*/
resolve(): Observable<any> {
return this.organizationService.getLoanOriginatorsTemplate();
}
}
@@ -0,0 +1,91 @@
<!--
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 m-b-20 layout-row align-end gap-20px">
<div #buttonCreateLoanOriginator>
<button mat-raised-button color="primary" [routerLink]="['create']" *mifosxHasPermission="'CREATE_LOAN_ORIGINATOR'">
<fa-icon icon="plus" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Create' | translate }} {{ 'labels.inputs.Loan Originator' | translate }}
</button>
</div>
</div>
<div class="container">
<div #filter class="layout-row gap-20px">
<mat-form-field class="flex-fill">
<mat-label>{{ 'labels.inputs.Filter' | translate }}</mat-label>
<input matInput (keyup)="applyFilter($event.target.value)" />
</mat-form-field>
</div>
<div #tableLoanOriginators class="mat-elevation-z8">
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Id' | translate }}</th>
<td mat-cell *matCellDef="let loanOriginator">{{ loanOriginator.id }}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Name' | translate }}</th>
<td mat-cell class="wrap-content" *matCellDef="let loanOriginator">{{ loanOriginator.name }}</td>
</ng-container>
<ng-container matColumnDef="externalId">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.External Id' | translate }}</th>
<td mat-cell *matCellDef="let loanOriginator">
<mifosx-external-identifier externalId="{{ loanOriginator.externalId }}"></mifosx-external-identifier>
</td>
</ng-container>
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Status' | translate }}</th>
<td mat-cell *matCellDef="let loanOriginator">
<div [className]="classStatus(loanOriginator.status)">
<fa-icon
icon="circle"
size="lg"
matTooltip="{{ 'tooltips.' + loanOriginator.status | translate }}"
matTooltipPosition="right"
></fa-icon>
</div>
</td>
</ng-container>
<ng-container matColumnDef="originatorType">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Originator Type' | translate }}</th>
<td mat-cell *matCellDef="let loanOriginator">{{ loanOriginator.originatorType?.name }}</td>
</ng-container>
<ng-container matColumnDef="channelType">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Channel Type' | translate }}</th>
<td mat-cell *matCellDef="let loanOriginator">{{ loanOriginator.channelType?.name }}</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
<td mat-cell *matCellDef="let loanOriginator">
<button
type="button"
color="warn"
title="{{ 'tooltips.Delete' | translate }}"
mat-icon-button
matTooltip="{{ 'tooltips.Delete' | translate }}"
*mifosxHasPermission="'DELETE_LOAN_ORIGINATOR'"
matTooltipPosition="above"
(click)="deleteLoanOriginator(loanOriginator)"
>
<fa-icon icon="trash" size="lg"></fa-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns" [routerLink]="[row.id]" class="select-row"></tr>
</table>
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
</div>
</div>
@@ -0,0 +1,15 @@
/**
* 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%;
.select-row:hover {
cursor: pointer;
}
}
@@ -0,0 +1,155 @@
/**
* 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/.
*/
/** Angular Imports */
import { Component, OnInit, TemplateRef, ElementRef, ViewChild, inject } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort, MatSortHeader } from '@angular/material/sort';
import {
MatTableDataSource,
MatTable,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatCellDef,
MatCell,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow
} from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
/** Custom Services */
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MatTooltip } from '@angular/material/tooltip';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { LoanOriginator } from 'app/loans/models/loan-account.model';
import { OrganizationService } from '../organization.service';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog';
import { MatIconButton } from '@angular/material/button';
import { ExternalIdentifierComponent } from 'app/shared/external-identifier/external-identifier.component';
/**
* Loan Originators component.
*/
@Component({
selector: 'mifosx-loan-originators',
templateUrl: './loan-originators.component.html',
styleUrl: './loan-originators.component.scss',
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent,
MatTable,
MatSort,
MatColumnDef,
MatHeaderCellDef,
MatHeaderCell,
MatSortHeader,
MatCellDef,
MatCell,
MatTooltip,
MatHeaderRowDef,
MatHeaderRow,
MatRowDef,
MatRow,
MatPaginator,
MatIconButton,
ExternalIdentifierComponent
]
})
export class LoanOriginatorsComponent implements OnInit {
private route = inject(ActivatedRoute);
private router = inject(Router);
private organizationService = inject(OrganizationService);
private translateService = inject(TranslateService);
private dialog = inject(MatDialog);
/** Loan Originators data. */
loanOriginatorsData: LoanOriginator[] = [];
/** Columns to be displayed in Loan Originators table. */
displayedColumns: string[] = [
'id',
'name',
'externalId',
'status',
'originatorType',
'channelType',
'actions'
];
/** Data source for Loan Originators table. */
dataSource: MatTableDataSource<any>;
/** Paginator for Loan Originators table. */
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
/** Sorter for Loan Originators table. */
@ViewChild(MatSort, { static: true }) sort: MatSort;
/* Reference of Loan Originators table */
@ViewChild('tableLoanOriginators') tableLoanOriginators: ElementRef<any>;
/* Template for popover on Loan Originators table */
@ViewChild('templateTableLoanOriginators') templateTableLoanOriginators: TemplateRef<any>;
constructor() {
this.route.data.subscribe((data: { loanOriginatorsData: LoanOriginator[] }) => {
this.loanOriginatorsData = data.loanOriginatorsData;
});
}
/**
* Filters data in Loan Originators table based on passed value.
* @param {string} filterValue Value to filter data.
*/
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
/**
* Sets the Loan Originators table.
*/
ngOnInit() {
this.setLoanOriginators();
}
/**
* Initializes the data source, paginator and sorter for Loan Originators table.
*/
setLoanOriginators() {
this.dataSource = new MatTableDataSource(this.loanOriginatorsData);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
deleteLoanOriginator(loanOriginator: LoanOriginator): void {
const deleteCodeDialogRef = this.dialog.open(DeleteDialogComponent, {
data: {
deleteContext: this.translateService.instant('labels.inputs.Loan Originator') + ' ' + loanOriginator.name
}
});
deleteCodeDialogRef.afterClosed().subscribe((response: any) => {
if (response.delete) {
this.organizationService.deleteLoanOriginator(loanOriginator.id).subscribe(() => {
this.router.navigate(['/organization/manage-loan-originators']);
});
}
});
}
classStatus(currentStatus: string): string {
if (currentStatus === 'ACTIVE') {
return 'status-active';
} else if (currentStatus === 'PENDING') {
return 'status-pending';
} else if (currentStatus === 'INACTIVE') {
return 'status-inactive';
}
return '';
}
}
@@ -0,0 +1,38 @@
/**
* 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/.
*/
/** Angular Imports */
import { Injectable, inject } from '@angular/core';
/** rxjs Imports */
import { Observable } from 'rxjs';
/** Custom Services */
import { OrganizationService } from '../organization.service';
import { ActivatedRouteSnapshot } from '@angular/router';
/**
* Loan Originators data resolver.
*/
@Injectable()
export class LoanOriginatorsResolver {
private organizationService = inject(OrganizationService);
/**
* Returns the Loan Originators data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const originatorId = route.paramMap.get('id');
if (originatorId) {
return this.organizationService.getLoanOriginator(originatorId);
} else {
return this.organizationService.getLoanOriginators();
}
}
}
@@ -0,0 +1,62 @@
<!--
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 m-b-20 layout-row align-end gap-20px">
<button mat-raised-button color="primary" [routerLink]="['edit']" *mifosxHasPermission="'UPDATE_LOAN_ORIGINATOR'">
<fa-icon icon="edit" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Edit' | translate }}
</button>
</div>
<div class="container">
<mat-card>
<mat-card-content>
<div class="layout-row-wrap">
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Name' | translate }}
</div>
<div class="flex-50 wrap-content">
{{ loanOriginatorData.name }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.External Id' | translate }}
</div>
<div class="flex-50 wrap-content">
{{ loanOriginatorData.externalId }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Status' | translate }}
</div>
<div class="flex-50">
{{ loanOriginatorData.status }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Originator Type' | translate }}
</div>
<div class="flex-50">
{{ loanOriginatorData.originatorType.name }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Channel Type' | translate }}
</div>
<div class="flex-50">
{{ loanOriginatorData.channelType.name }}
</div>
</div>
</mat-card-content>
</mat-card>
</div>
@@ -0,0 +1,18 @@
/**
* 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/.
*/
.container {
max-width: 37rem;
.content {
div {
margin: 1rem 0;
word-wrap: break-word;
}
}
}
@@ -0,0 +1,43 @@
/**
* 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/.
*/
/** Angular Imports */
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { LoanOriginator } from 'app/loans/models/loan-account.model';
/**
* View Employee Component.
*/
@Component({
selector: 'mifosx-view-loan-originator',
templateUrl: './view-loan-originator.component.html',
styleUrl: './view-loan-originator.component.scss',
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent
]
})
export class ViewLoanOriginatorComponent {
private route = inject(ActivatedRoute);
/** Employee data. */
loanOriginatorData: LoanOriginator;
/**
* Retrieves the Loan Originator data from `resolve`.
* @param {ActivatedRoute} route Activated Route.
*/
constructor() {
this.route.data.subscribe((data: { loanOriginatorData: LoanOriginator }) => {
this.loanOriginatorData = data.loanOriginatorData;
});
}
}
@@ -111,6 +111,12 @@ import { ViewFundComponent } from './manage-funds/view-fund/view-fund.component'
import { EditFundComponent } from './manage-funds/edit-fund/edit-fund.component';
import { CreateFundComponent } from './manage-funds/create-fund/create-fund.component';
import { InvestorsComponent } from './investors/investors.component';
import { LoanOriginatorsComponent } from './loan-originators/loan-originators.component';
import { LoanOriginatorsResolver } from './loan-originators/loan-originators.resolver';
import { ViewLoanOriginatorComponent } from './loan-originators/view-loan-originator/view-loan-originator.component';
import { EditLoanOriginatorComponent } from './loan-originators/edit-loan-originator/edit-loan-originator.component';
import { CreateLoanOriginatorComponent } from './loan-originators/create-loan-originator/create-loan-originator.component';
import { LoanOriginatorsTemplateResolver } from './loan-originators/loan-originators-template.resolver';
/** Organization Routes */
const routes: Routes = [
@@ -592,6 +598,50 @@ const routes: Routes = [
workingDays: WorkingDaysResolver
}
},
{
path: 'manage-loan-originators',
data: { title: 'Manage Loan Originators', breadcrumb: 'Manage Loan Originators' },
children: [
{
path: '',
component: LoanOriginatorsComponent,
resolve: {
loanOriginatorsData: LoanOriginatorsResolver
}
},
{
path: 'create',
component: CreateLoanOriginatorComponent,
data: { title: 'Create Loan Originator', breadcrumb: 'Create' },
resolve: {
loanOriginatorsTemplateData: LoanOriginatorsTemplateResolver
}
},
{
path: ':id',
data: { routeParamBreadcrumb: 'id', addBreadcrumbLink: false },
children: [
{
path: '',
component: ViewLoanOriginatorComponent,
data: { title: 'View Loan Originator', breadcrumb: 'View', routeParamBreadcrumb: false },
resolve: {
loanOriginatorData: LoanOriginatorsResolver
}
},
{
path: 'edit',
component: EditLoanOriginatorComponent,
data: { title: 'Edit Loan Originator', breadcrumb: 'Edit', routeParamBreadcrumb: false },
resolve: {
loanOriginatorData: LoanOriginatorsResolver,
loanOriginatorsTemplateData: LoanOriginatorsTemplateResolver
}
}
]
}
]
},
{
path: 'manage-funds',
data: { title: 'Manage Funds', breadcrumb: 'Manage Funds' },
@@ -753,7 +803,9 @@ const routes: Routes = [
LoanProvisioningCriteriaTemplateResolver,
LoanProvisioningCriteriaAndTemplateResolver,
StandingInstructionsTemplateResolver,
AdvanceSearchTemplateResolver
AdvanceSearchTemplateResolver,
LoanOriginatorsResolver,
LoanOriginatorsTemplateResolver
]
})
export class OrganizationRoutingModule {}
@@ -323,6 +323,34 @@
</mat-list-item>
</div>
<div #manageLoanOriginators>
<mat-list-item *mifosxHasPermission="'READ_LOAN_ORIGINATOR'">
<div class="menu-list-item-content">
<div class="menu-left-section" [routerLink]="['manage-loan-originators']">
<mat-icon matListIcon>
<fa-icon icon="edit" size="sm"></fa-icon>
</mat-icon>
<div matLine>
{{ 'labels.heading.Manage Loan Originators' | translate }}
@if (arrowBooleans[18]) {
<p matLine [routerLink]="['manage-loan-originators']" class="menu-explanation">
{{ 'labels.text.Loan Originators are associated with loan accounts' | translate }}
</p>
}
</div>
</div>
<div class="menu-right-section">
@if (!arrowBooleans[18]) {
<fa-icon (click)="arrowBooleansToggle(18)" icon="arrow-down" size="md"></fa-icon>
}
@if (arrowBooleans[18]) {
<fa-icon (click)="arrowBooleansToggle(18)" icon="arrow-up" size="md"></fa-icon>
}
</div>
</div>
</mat-list-item>
</div>
<div #bulkloan>
<mat-list-item *mifosxHasPermission="'BULKREASSIGN_LOAN'">
<div class="menu-list-item-content">
@@ -67,7 +67,7 @@ export class OrganizationComponent implements AfterViewInit {
/* Template for popover on manage funds */
@ViewChild('templateManageFunds') templateManageFunds: TemplateRef<any>;
// Initialize an array of 18 boolean values, all set to false
arrowBooleans: boolean[] = new Array(18).fill(false);
arrowBooleans: boolean[] = new Array(19).fill(false);
/**
* Popover function
@@ -831,4 +831,46 @@ export class OrganizationService {
formData.append('dateFormat', this.settingsService.dateFormat);
return this.http.post(`${urlSuffix}/uploadtemplate`, formData, { params: httpParams });
}
/**
* @returns {Observable<any>} Loan Originators data
*/
getLoanOriginators(): Observable<any> {
return this.http.get('/loan-originators');
}
/**
* @returns {Observable<any>} Loan Originators Template data
*/
getLoanOriginatorsTemplate(): Observable<any> {
return this.http.get('/loan-originators/template');
}
/**
* @returns {Observable<any>} Loan Originator data
*/
getLoanOriginator(originatorId: string): Observable<any> {
return this.http.get(`/loan-originators/${originatorId}`);
}
/**
* @returns {Observable<any>}
*/
createLoanOriginator(payload: any): Observable<any> {
return this.http.post(`/loan-originators`, payload);
}
/**
* @returns {Observable<any>}
*/
updateLoanOriginator(originatorId: number, payload: any): Observable<any> {
return this.http.put(`/loan-originators/${originatorId}`, payload);
}
/**
* @returns {Observable<any>}
*/
deleteLoanOriginator(originatorId: number): Observable<any> {
return this.http.delete(`/loan-originators/${originatorId}`);
}
}
+3 -1
View File
@@ -85,6 +85,7 @@ import {
faTrash,
faUndo,
faUniversity,
faUnlink,
faUserCircle,
faUser,
faUsers,
@@ -271,7 +272,8 @@ export class IconsModule {
faUndo,
faReceipt,
faTableCells,
faSave
faSave,
faUnlink
);
}
}