Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/app/core/http/error-handler.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const log = new Logger('ErrorHandlerInterceptor');
export class ErrorHandlerInterceptor implements HttpInterceptor {
private alertService = inject(AlertService);
private translate = inject(TranslateService);
private databaseErrorCodes: string[] = [
'error.msg.data.integrity.issue.entity.duplicated',
'error.msg.data.integrity.issue'
];

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError((error) => this.handleError(error, request)));
Expand Down Expand Up @@ -88,12 +92,18 @@ export class ErrorHandlerInterceptor implements HttpInterceptor {
: nestedMessage
: topLevelMessage;
let parameterName: string | null = null;
if (errorBody?.errors?.[0]) {
if (!nestedMessage) {
errorMessage =
errorBody.errors[0].defaultUserMessage?.replace(/\\./g, ' ') ||
errorBody.errors[0].developerMessage?.replace(/\\./g, ' ') ||
errorMessage;
if (response.error.errors) {
if (response.error.errors[0]) {
if (
response.error.errors[0].userMessageGlobalisationCode &&
this.databaseErrorCodes.indexOf(response.error.errors[0].userMessageGlobalisationCode) > -1
) {
errorMessage = this.translate.instant('errors.error.msg.data.integrity.issue');
} else {
errorMessage =
response.error.errors[0].defaultUserMessage.replace(/\\./g, ' ') ||
response.error.errors[0].developerMessage.replace(/\\./g, ' ');
}
}
if ('parameterName' in errorBody.errors[0]) {
parameterName = errorBody.errors[0].parameterName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class CreateLoansAccountComponent extends LoanProductBaseComponent implem
this.loansAccountProductTemplate = templateData.loanData;
this.loansAccountProductTemplate.options = {
breachOptions: templateData.breachOptions,
nearBreachOptions: templateData.nearBreachOptions,
delinquencyBucketOptions: templateData.delinquencyBucketOptions,
fundOptions: templateData.fundOptions,
periodFrequencyTypeOptions: templateData.periodFrequencyTypeOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class EditLoansAccountComponent extends LoanProductBaseComponent {
this.loansAccountProductTemplate = templateData.loanData;
this.loansAccountProductTemplate.options = {
breachOptions: templateData.breachOptions,
nearBreachOptions: templateData.nearBreachOptions,
delinquencyBucketOptions: templateData.delinquencyBucketOptions,
fundOptions: templateData.fundOptions,
periodFrequencyTypeOptions: templateData.periodFrequencyTypeOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,24 @@ <h3 class="mat-h3 margin-t flex-fill">{{ 'labels.heading.Terms' | translate }}</
</div>
}

@if (loansAccount.breachId) {
<div class="flex-fill">
<span class="flex-30">{{ 'labels.inputs.Breach' | translate }}:</span>
<span class="flex-60"
><mifosx-breach-display [singleRow]="false" [breach]="getBreach(loansAccount.breachId)"
/></span>
</div>
}

@if (loansAccount.nearBreachId) {
<div class="flex-fill">
<span class="flex-30">{{ 'labels.inputs.Near Breach' | translate }}:</span>
<span class="flex-60"
><mifosx-breach-display [singleRow]="false" [nearBreach]="getNearBreach(loansAccount.nearBreachId)"
/></span>
</div>
}

<div class="flex-fill">
<span class="flex-30">{{ 'labels.inputs.Period Payment Rate' | translate }}:</span>
<span class="flex-70"> {{ loansAccount.periodPaymentRate | formatNumber }} % </span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { LoanProductBaseComponent } from 'app/products/loan-products/common/loan-product-base.component';
import { LoanProductBasicDetails } from 'app/loans/models/loan-product.model';
import { LongTextComponent } from 'app/shared/long-text/long-text.component';
import { Breach, NearBreach } from 'app/products/loan-products/models/loan-product.model';
import { BreachDisplayComponent } from 'app/shared/loan/breach-display/breach-display.component';

/**
* Create Loans Account Preview Step
Expand Down Expand Up @@ -65,7 +67,8 @@ import { LongTextComponent } from 'app/shared/long-text/long-text.component';
FormatNumberPipe,
YesnoPipe,
TranslatePipe,
LongTextComponent
LongTextComponent,
BreachDisplayComponent
]
})
export class LoansAccountPreviewStepComponent extends LoanProductBaseComponent implements OnChanges {
Expand Down Expand Up @@ -155,4 +158,20 @@ export class LoansAccountPreviewStepComponent extends LoanProductBaseComponent i
camalize(word: string) {
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}

getBreach(breachId: number | null | undefined): Breach | null {
if (breachId === null || breachId === undefined) {
return null;
}
return this.loansAccountProductTemplate.options.breachOptions?.find((b: Breach) => b.id === breachId) || null;
}

getNearBreach(nearBreachId: number | null | undefined): NearBreach | null {
if (nearBreachId === null || nearBreachId === undefined) {
return null;
}
return (
this.loansAccountProductTemplate.options.nearBreachOptions?.find((b: NearBreach) => b.id === nearBreachId) || null
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,35 @@ <h4 class="mat-h4 flex-98">
</mat-option>
}
</mat-select>
@if (loansAccountTermsForm.controls.breachId) {
<button matSuffix mat-icon-button aria-label="Clear" (click)="clearProperty($event, 'breachId')">
<fa-icon icon="close" size="md"></fa-icon>
</button>
}
</mat-form-field>

@if (loansAccountTermsForm.value.breachId) {
<mat-form-field class="flex-48">
<mat-label>{{ 'labels.inputs.Near Breach' | translate }}</mat-label>
<mat-select formControlName="nearBreachId" class="breach-id-select" panelClass="breach-select-panel">
<mat-select-trigger>
@if (selectedNearBreach) {
<mifosx-breach-display [singleRow]="true" [nearBreach]="selectedNearBreach" />
}
</mat-select-trigger>
@for (nearBreach of nearBreachOptions; track nearBreach) {
<mat-option [value]="nearBreach.id">
<mifosx-breach-display [singleRow]="true" [nearBreach]="nearBreach" />
</mat-option>
}
</mat-select>
@if (loansAccountTermsForm.controls.nearBreachId) {
<button matSuffix mat-icon-button aria-label="Clear" (click)="clearProperty($event, 'nearBreachId')">
<fa-icon icon="close" size="md"></fa-icon>
</button>
}
</mat-form-field>
}
}

@if (loanProductService.isLoanProduct) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { LoansAccountAddCollateralDialogComponent } from 'app/loans/custom-dialog/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component';
import { LoanProducts } from 'app/products/loan-products/loan-products';
import { Breach, LoanProduct } from 'app/products/loan-products/models/loan-product.model';
import { Breach, LoanProduct, NearBreach } from 'app/products/loan-products/models/loan-product.model';
import { SettingsService } from 'app/settings/settings.service';
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
Expand Down Expand Up @@ -178,6 +178,7 @@ export class LoansAccountTermsStepComponent extends LoanProductBaseComponent imp

delinquencyStartTypeOptions: StringEnumOptionData[] = [];
breachOptions: Breach[] = [];
nearBreachOptions: NearBreach[] = [];

constructor() {
super();
Expand Down Expand Up @@ -329,6 +330,7 @@ export class LoansAccountTermsStepComponent extends LoanProductBaseComponent imp
this.termFrequencyTypeData = this.loansAccountTermsData.options?.periodFrequencyTypeOptions;
this.delinquencyStartTypeOptions = this.loansAccountTermsData.options?.delinquencyStartTypeOptions;
this.breachOptions = this.loansAccountTermsData.options?.breachOptions ?? [];
this.nearBreachOptions = this.loansAccountTermsData.options?.nearBreachOptions ?? [];
if (this.loanId != null && 'accountNo' in this.loansAccountTemplate) {
this.loansAccountTermsData = this.loansAccountTemplate;
this.loansAccountTermsForm.patchValue({
Expand All @@ -340,15 +342,17 @@ export class LoansAccountTermsStepComponent extends LoanProductBaseComponent imp
repaymentFrequencyType: this.loansAccountTermsData.repaymentFrequencyType?.id,
delinquencyGraceDays: this.loansAccountTermsData.delinquencyGraceDays,
delinquencyStartType: this.loansAccountTermsData.delinquencyStartType?.code,
breachId: this.loansAccountTermsData.breach?.id
breachId: this.loansAccountTermsData.breach?.id,
nearBreachId: this.loansAccountTermsData.nearBreach?.id
});
} else {
this.loansAccountTermsForm.patchValue({
discount: this.loansAccountTermsData.product.discount || '',
principalAmount: this.loansAccountTermsData.product.principal,
delinquencyGraceDays: this.loansAccountTermsData.product.delinquencyGraceDays || '',
delinquencyStartType: this.loansAccountTermsData.product.delinquencyStartType?.code || '',
breachId: this.loansAccountTermsData.product.breach?.id || ''
breachId: this.loansAccountTermsData.product.breach?.id || '',
nearBreachId: this.loansAccountTermsData.product.nearBreach?.id || ''
});
}
this.allowAttributeOverrides = this.loansAccountProductTemplate.product.allowAttributeOverrides;
Expand All @@ -369,6 +373,7 @@ export class LoansAccountTermsStepComponent extends LoanProductBaseComponent imp
}
if (!this.allowAttributeOverrides.breach || this.allowAttributeOverrides.breach === false) {
this.loansAccountTermsForm.controls.breachId.disable();
this.loansAccountTermsForm.controls.nearBreachId.disable();
}
}
}
Expand Down Expand Up @@ -466,7 +471,8 @@ export class LoansAccountTermsStepComponent extends LoanProductBaseComponent imp
delinquencyStartType:
this.loansAccountTermsData.delinquencyStartType?.id ||
this.loansAccountTermsData.product.delinquencyStartType?.id,
breachId: this.loansAccountTermsData.breach?.id || this.loansAccountTermsData.product.breach?.id
breachId: this.loansAccountTermsData.breach?.id || this.loansAccountTermsData.product.breach?.id,
nearBreachId: this.loansAccountTermsData.nearBreach?.id || this.loansAccountTermsData.product.nearBreach?.id
});
}
}
Expand Down Expand Up @@ -731,7 +737,8 @@ export class LoansAccountTermsStepComponent extends LoanProductBaseComponent imp
]
],
delinquencyStartType: [''],
breachId: ['']
breachId: [''],
nearBreachId: ['']
});
}
}
Expand Down Expand Up @@ -927,4 +934,24 @@ export class LoansAccountTermsStepComponent extends LoanProductBaseComponent imp
const id = this.loansAccountTermsForm.get('breachId')?.value;
return this.breachOptions ? this.breachOptions.find((b) => b.id === id) : undefined;
}

get selectedNearBreach(): NearBreach | undefined {
const id = this.loansAccountTermsForm.get('nearBreachId')?.value;
return this.nearBreachOptions ? this.nearBreachOptions.find((b) => b.id === id) : undefined;
}

clearProperty($event: Event, propertyName: string): void {
if (propertyName === 'breachId') {
this.loansAccountTermsForm.patchValue({
breachId: '',
nearBreachId: ''
});
} else if (propertyName === 'nearBreachId') {
this.loansAccountTermsForm.patchValue({
nearBreachId: ''
});
}
this.loansAccountTermsForm.markAsDirty();
$event.stopPropagation();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ <h3>{{ 'labels.heading.Loan Details' | translate }}</h3>
<span class="flex-50"><mifosx-breach-display [singleRow]="false" [breach]="loanDetails.breach" /></span>
</div>
}

@if (loanDetails.nearBreach) {
<div class="flex-fill layout-row">
<span class="flex-50"> {{ 'labels.inputs.Near Breach' | translate }} </span>
<span class="flex-50"
><mifosx-breach-display [singleRow]="false" [nearBreach]="loanDetails.nearBreach"
/></span>
</div>
}
}

@if (loanProductService.isLoanProduct) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,18 @@ <h3 class="mat-h3 flex-fill">{{ 'labels.heading.Terms' | translate }}</h3>
</div>
<div class="flex-fill layout-row">
<span class="flex-40">{{ 'labels.inputs.Breach' | translate }}:</span>
<span class="flex-60"><mifosx-breach-display [singleRow]="false" [breach]="loanProduct.breach" /></span>
<span class="flex-60"><mifosx-breach-display [singleRow]="false" [breach]="getBreach()" /></span>
</div>
<div class="flex-fill layout-row">
<span class="flex-40">{{ 'labels.inputs.Enable Near Breach' | translate }}:</span>
<span class="flex-60">{{ enableNearBreach() | yesNo }}</span>
</div>
@if (enableNearBreach()) {
<div class="flex-fill layout-row">
<span class="flex-40">{{ 'labels.inputs.Near Breach' | translate }}:</span>
<span class="flex-60"><mifosx-breach-display [singleRow]="false" [nearBreach]="getNearBreach()" /></span>
</div>
}
}
@if (loanProductService.isLoanProduct) {
<div class="flex-fill layout-row">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { Component, Input, OnChanges, OnInit, SimpleChanges, inject } from '@angular/core';
import { DelinquencyBucket, LoanProduct } from '../../models/loan-product.model';
import { Breach, DelinquencyBucket, LoanProduct, NearBreach } from '../../models/loan-product.model';
import {
AccountingMapping,
Charge,
Expand Down Expand Up @@ -538,6 +538,16 @@ export class LoanProductSummaryComponent extends LoanProductBaseComponent implem
};
}
}

if (this.loanProductService.isWorkingCapital) {
/*
let optionValue: OptionData = this.optionDataLookUp(
this.loanProduct.nearBreach.frequencyType,
this.loanProductsTemplate.periodFrequencyTypeOptions
);
this.loanProduct.nearBreachEvalFrequencyType = optionValue;
*/
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -675,11 +685,37 @@ export class LoanProductSummaryComponent extends LoanProductBaseComponent implem
);
}

enableNearBreach(): boolean {
return this.loanProductService.isWorkingCapital && this.getNearBreach() !== null;
}

getAccountingRuleName(value: string): string {
return this.loanProductService.isWorkingCapital ? '' : this.accounting.getAccountRuleName(value.toUpperCase());
}

mapHumanReadableValueStringEnumOptionDataList(incomingParameter: StringEnumOptionData[]): string[] {
return incomingParameter.map((v) => v.value);
}

getBreach(): Breach | null {
if (this.loanProduct.breach) {
return this.loanProduct.breach;
}
if (this.loanProduct.breachId === null || this.loanProduct.breachId === undefined) {
return null;
}
return this.loanProductsTemplate.breachOptions?.find((b: Breach) => b.id === this.loanProduct.breachId) || null;
}

getNearBreach(): NearBreach | null {
if (this.loanProduct.nearBreach) {
return this.loanProduct.nearBreach;
}
if (this.loanProduct.nearBreachId === null || this.loanProduct.nearBreachId === undefined) {
return null;
}
return (
this.loanProductsTemplate.nearBreachOptions?.find((b: Breach) => b.id === this.loanProduct.nearBreachId) || null
);
}
}
Loading
Loading