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
19 changes: 16 additions & 3 deletions src/app/core/http/error-handler.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,18 @@ export class ErrorHandlerInterceptor implements HttpInterceptor {
}

// Combine both messages if both exist
const errorMessage = nestedMessage ? `${topLevelMessage} ${nestedMessage}` : topLevelMessage;

let errorMessage = nestedMessage ? `${topLevelMessage} ${nestedMessage}` : topLevelMessage;
let parameterName: string | null = null;
if (response.error.errors) {
if (response.error.errors[0]) {
errorMessage =
response.error.errors[0].defaultUserMessage.replace(/\\./g, ' ') ||
response.error.errors[0].developerMessage.replace(/\\./g, ' ');
}
Comment on lines +66 to +69
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Regex pattern /\\./g is likely incorrect and property access may throw.

Two issues here:

  1. Regex mismatch: /\\./g matches a literal backslash followed by any character, not a period. If the intent is to replace periods (.) with spaces, use /\./g. If the intent is to replace backslash-dot sequences (\.), use /\\\./g.

  2. Potential TypeError: If defaultUserMessage is undefined, calling .replace() on it will throw before the || fallback is evaluated.

🐛 Proposed fix with optional chaining and corrected regex

Assuming you want to replace periods with spaces:

       errorMessage =
-        response.error.errors[0].defaultUserMessage.replace(/\\./g, ' ') ||
-        response.error.errors[0].developerMessage.replace(/\\./g, ' ');
+        response.error.errors[0].defaultUserMessage?.replace(/\./g, ' ') ||
+        response.error.errors[0].developerMessage?.replace(/\./g, ' ');

Or if you want to replace backslash-dot (\.) sequences with spaces:

       errorMessage =
-        response.error.errors[0].defaultUserMessage.replace(/\\./g, ' ') ||
-        response.error.errors[0].developerMessage.replace(/\\./g, ' ');
+        response.error.errors[0].defaultUserMessage?.replace(/\\\./g, ' ') ||
+        response.error.errors[0].developerMessage?.replace(/\\\./g, ' ');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
errorMessage =
response.error.errors[0].defaultUserMessage.replace(/\\./g, ' ') ||
response.error.errors[0].developerMessage.replace(/\\./g, ' ');
}
errorMessage =
response.error.errors[0].defaultUserMessage?.replace(/\./g, ' ') ||
response.error.errors[0].developerMessage?.replace(/\./g, ' ');
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/core/http/error-handler.interceptor.ts` around lines 52 - 55, The
code is using an incorrect regex and can throw if
defaultUserMessage/developerMessage is undefined; update the replacement to use
the correct regex for periods (/\./g) and guard property access with optional
chaining or a safe fallback before calling replace on errorMessage construction
in the error-handler (refer to errorMessage and
response.error.errors[0].defaultUserMessage / developerMessage), e.g., compute a
source string via optional chaining or nullish coalescing and then call replace
on that string so the fallback is evaluated safely.

if ('parameterName' in response.error.errors[0]) {
parameterName = response.error.errors[0].parameterName;
}
Comment on lines +70 to 72
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bug: parameterName check is outside the null guard and will throw if errors[0] is falsy.

The 'parameterName' in response.error.errors[0] check is placed outside the if (response.error.errors[0]) block. If response.error.errors exists but errors[0] is undefined or null, this will throw a TypeError because the in operator cannot be used on non-objects.

🐛 Proposed fix: move the check inside the existing null guard
     if (response.error.errors) {
       if (response.error.errors[0]) {
         errorMessage =
           response.error.errors[0].defaultUserMessage.replace(/\\./g, ' ') ||
           response.error.errors[0].developerMessage.replace(/\\./g, ' ');
-      }
-      if ('parameterName' in response.error.errors[0]) {
-        parameterName = response.error.errors[0].parameterName;
+        if ('parameterName' in response.error.errors[0]) {
+          parameterName = response.error.errors[0].parameterName;
+        }
       }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ('parameterName' in response.error.errors[0]) {
parameterName = response.error.errors[0].parameterName;
}
if (response.error.errors) {
if (response.error.errors[0]) {
errorMessage =
response.error.errors[0].defaultUserMessage.replace(/\\./g, ' ') ||
response.error.errors[0].developerMessage.replace(/\\./g, ' ');
if ('parameterName' in response.error.errors[0]) {
parameterName = response.error.errors[0].parameterName;
}
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/core/http/error-handler.interceptor.ts` around lines 56 - 58, The
check "'parameterName' in response.error.errors[0]" can throw when errors[0] is
null/undefined; move that membership check inside the existing null guard that
verifies response.error.errors[0] (or replace with a defensive check like
response.error.errors?.[0] && 'parameterName' in response.error.errors[0]) so
you only test for the property after confirming errors[0] exists; update the
logic around the parameterName assignment in the ErrorHandlerInterceptor
(intercept/handleError block where response.error.errors and parameterName are
used) accordingly.

}
const isClientImage404 = status === 404 && request.url.includes('/clients/') && request.url.includes('/images');

if (!environment.production && !isClientImage404) {
Expand All @@ -81,9 +91,12 @@ export class ErrorHandlerInterceptor implements HttpInterceptor {
message: this.translate.instant('errors.error.token.invalid.message')
});
} else if (status === 400) {
const message = parameterName
? `[${parameterName}] ${errorMessage || 'Invalid parameters were passed in the request!'}`
: `${errorMessage || 'Invalid parameters were passed in the request!'}`;
this.alertService.alert({
type: this.translate.instant('errors.error.bad.request.type'),
message: errorMessage || this.translate.instant('errors.error.bad.request.message')
message: message || this.translate.instant('errors.error.bad.request.message')
});
} else if (status === 403) {
this.alertService.alert({
Expand Down
7 changes: 5 additions & 2 deletions src/app/directives/directives.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CommonModule } from '@angular/common';
import { HasPermissionDirective } from './has-permission/has-permission.directive';
import { FormatAmountDirective } from './format-amount.directive';
import { ValidateOnFocusDirective } from './validate-on-focus.directive';
import { PositiveNumberDirective } from './positive-number.directive';

/**
* Directives Module
Expand All @@ -25,12 +26,14 @@ import { ValidateOnFocusDirective } from './validate-on-focus.directive';
CommonModule,
HasPermissionDirective,
FormatAmountDirective,
ValidateOnFocusDirective
ValidateOnFocusDirective,
PositiveNumberDirective
],
exports: [
HasPermissionDirective,
FormatAmountDirective,
ValidateOnFocusDirective
ValidateOnFocusDirective,
PositiveNumberDirective
]
})
export class DirectivesModule {}
29 changes: 29 additions & 0 deletions src/app/directives/positive-number.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* 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 { Directive, ElementRef, HostListener, inject } from '@angular/core';

@Directive({ selector: '[mifosxPositiveNumber]' })
export class PositiveNumberDirective {
Comment on lines +11 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if other directives in the same module are standalone
fd -e ts -p 'directive' src/app/directives --exec grep -l "standalone.*true" {} \;

# Check the exact pattern used by existing directives
rg -A2 "@Directive" src/app/directives/

Repository: openMF/web-app

Length of output: 1313


🏁 Script executed:

cat -n src/app/directives/directives.module.ts

Repository: openMF/web-app

Length of output: 1445


Missing standalone: true will cause module import to fail.

The DirectivesModule imports this directive directly into its imports array, but the directive is not marked as standalone. This will cause a compilation error since Angular 14+ requires all classes in an imports array to be standalone.

🐛 Proposed fix
-@Directive({ selector: '[mifosxPositiveNumber]' })
+@Directive({ selector: '[mifosxPositiveNumber]', standalone: true })
 export class PositiveNumberDirective {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/directives/positive-number.directive.ts` around lines 11 - 12, The
PositiveNumberDirective is missing the standalone: true flag in its `@Directive`
metadata causing the DirectivesModule import to fail; update the `@Directive`
decorator for PositiveNumberDirective to include standalone: true (i.e., add
standalone: true inside the decorator object) so the directive can be imported
directly in module imports.

private el = inject(ElementRef);

@HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent): void {
const inputElement = this.el.nativeElement;

// Check if the key is ArrowUp or ArrowDown
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
const currentValue = parseFloat(inputElement.value) || 0;

if (event.key === 'ArrowDown' && currentValue <= 0) {
// Prevent default if trying to decrement below or at 0
event.preventDefault();
}
}
}
}
48 changes: 48 additions & 0 deletions src/app/loans/common-resolvers/loan-base.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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 } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { LOAN_PRODUCT_TYPE, LoanProductType } from 'app/products/loan-products/models/loan-product.model';
import { ActivatedRouteSnapshot } from '@angular/router';

@Injectable({
providedIn: 'root'
})
export class LoanBaseResolver {
productType = new BehaviorSubject<LoanProductType>(LOAN_PRODUCT_TYPE.LOAN);

constructor() {}

protected initialize(route: ActivatedRouteSnapshot): void {
const productType = route.queryParams['productType'];
if (productType !== null) {
if (productType === 'loan') {
this.productType.next(LOAN_PRODUCT_TYPE.LOAN);
} else if (productType === 'working-capital') {
this.productType.next(LOAN_PRODUCT_TYPE.WORKING_CAPITAL);
}
}
}

get isWorkingCapital(): boolean {
return LOAN_PRODUCT_TYPE.WORKING_CAPITAL === this.productType.value;
}

get isLoanProduct(): boolean {
return LOAN_PRODUCT_TYPE.LOAN === this.productType.value;
}

get loanProductPath(): string {
return this.isLoanProduct ? 'loanproducts' : 'working-capital-loan-products';
}

get loanAccountPath(): string {
return this.isLoanProduct ? 'loans' : 'working-capital-loans';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@ import { Injectable, inject } from '@angular/core';
import { ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { LoansService } from '../loans.service';
import { LoanBaseResolver } from './loan-base.resolver';

@Injectable({
providedIn: 'root'
})
export class LoanDelinquencyActionsResolver {
export class LoanDelinquencyActionsResolver extends LoanBaseResolver {
private loansService = inject(LoansService);

constructor() {
super();
}

/**
* Returns the Loans with Association data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
this.initialize(route);
const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
return this.loansService.getDelinquencyActions(loanId);
return this.loansService.getDelinquencyActions(this.loanAccountPath, loanId);
}
Comment on lines 29 to 33
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider adding loanId validation for consistency.

Unlike LoanDelinquencyTagsResolver (which checks !isNaN(+loanId)), this resolver unconditionally calls the service. If loanId is null or non-numeric, the request will be made to an invalid endpoint (e.g., /{productType}/null/delinquency-actions).

💡 Suggested fix for consistency
   resolve(route: ActivatedRouteSnapshot): Observable<any> {
     this.initialize(route);
     const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
+    if (!loanId || isNaN(+loanId)) {
+      return of([]);
+    }
     return this.loansService.getDelinquencyActions(this.loanAccountPath, loanId);
   }

Add import { of } from 'rxjs'; at the top.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/loans/common-resolvers/loan-delinquency-actions.resolver.ts` around
lines 29 - 33, The resolver's resolve method should validate loanId before
calling the service: mirror LoanDelinquencyTagsResolver's check (e.g., ensure
loanId is present and numeric with !isNaN(+loanId)); if invalid, return an
observable using of(null) (or the same fallback used by the tags resolver)
instead of calling loansService.getDelinquencyActions; add import { of } from
'rxjs' at the top and update the resolve function in
LoanDelinquencyActionsResolver to perform this validation before invoking
getDelinquencyActions.

}
13 changes: 11 additions & 2 deletions src/app/loans/common-resolvers/loan-delinquency-data.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,29 @@ import { Observable } from 'rxjs';

/** Custom Services */
import { LoansService } from '../loans.service';
import { LoanBaseResolver } from './loan-base.resolver';

/**
* Loan Delinquency data resolver.
*/
@Injectable()
export class LoanDelinquencyDataResolver {
export class LoanDelinquencyDataResolver extends LoanBaseResolver {
private loansService = inject(LoansService);

constructor() {
super();
}
/**
* Returns the Loans with Association data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
this.initialize(route);
const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
return this.loansService.getDelinquencyData(loanId);
if (!isNaN(+loanId)) {
if (this.isLoanProduct) {
return this.loansService.getDelinquencyData(loanId);
}
}
}
Comment on lines 34 to 42
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return a safe fallback to prevent downstream NPE.

When isLoanProduct is false (working-capital) or loanId is non-numeric, resolve() returns undefined. The consumer component accesses data.loanDelinquencyData.delinquent and data.loanDelinquencyData.currency without null-checking, causing a runtime error.

🛠️ Suggested fix
   resolve(route: ActivatedRouteSnapshot): Observable<any> {
     this.initialize(route);
     const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
     if (!isNaN(+loanId)) {
       if (this.isLoanProduct) {
         return this.loansService.getDelinquencyData(loanId);
       }
     }
+    // Return empty object to prevent NPE in consumers
+    return of({});
   }

Add import { of } from 'rxjs'; at the top.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/loans/common-resolvers/loan-delinquency-data.resolver.ts` around
lines 34 - 42, The resolve method can return undefined when loanId is
missing/non-numeric or when isLoanProduct is false; change
resolve(ActivatedRouteSnapshot) so it always returns an Observable (use of(...)
from 'rxjs') as a safe fallback instead of falling through to undefined, e.g.,
return of({ loanDelinquencyData: { delinquent: false, currency: '' } }) when
!isLoanProduct or loanId is invalid; ensure you import { of } from 'rxjs' and
keep the existing branch that returns
this.loansService.getDelinquencyData(loanId) when isLoanProduct and loanId is
valid.

}
14 changes: 11 additions & 3 deletions src/app/loans/common-resolvers/loan-delinquency-tags.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,28 @@ import { Observable } from 'rxjs';

/** Custom Services */
import { LoansService } from '../loans.service';
import { LoanBaseResolver } from './loan-base.resolver';

/**
* Clients data resolver.
* Loan Delinquency Tags data resolver.
*/
@Injectable()
export class LoanDelinquencyTagsResolver {
export class LoanDelinquencyTagsResolver extends LoanBaseResolver {
private loansService = inject(LoansService);

constructor() {
super();
}

/**
* Returns the Loans with Association data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
this.initialize(route);
const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
return this.loansService.getDelinquencyTags(loanId);
if (!isNaN(+loanId)) {
return this.loansService.getDelinquencyTags(this.loanAccountPath, loanId);
}
}
Comment on lines 35 to 41
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing return value may cause downstream NPE.

When loanId is non-numeric, resolve() returns undefined. The consumer component (loan-delinquency-tags-tab.component.ts lines 121-122) accesses data.loanDelinquencyData.delinquent and data.loanDelinquencyData.currency without null-checking, which will throw a runtime error.

Consider returning an empty observable or a safe fallback object:

🛠️ Suggested fix
   resolve(route: ActivatedRouteSnapshot): Observable<any> {
     this.initialize(route);
     const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
     if (!isNaN(+loanId)) {
       return this.loansService.getDelinquencyTags(this.loanAccountPath, loanId);
     }
+    return of([]);
   }

Add import { of } from 'rxjs'; at the top.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/loans/common-resolvers/loan-delinquency-tags.resolver.ts` around
lines 35 - 41, The resolver resolve(route: ActivatedRouteSnapshot) can return
undefined when loanId is non-numeric; update it to always return an Observable
by importing of from 'rxjs' and returning a safe fallback Observable when loanId
is invalid (e.g., of(null) or of({ loanDelinquencyData: { delinquent: false,
currency: null } })), keeping the successful path that calls
this.loansService.getDelinquencyTags(this.loanAccountPath, loanId) unchanged;
reference resolve, loanId, loanAccountPath and loansService.getDelinquencyTags
when making the change.

}
2 changes: 1 addition & 1 deletion src/app/loans/common-resolvers/loan-details.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class LoanDetailsResolver {
if (resolvedProductType === LOAN_PRODUCT_TYPE.LOAN) {
return this.loansService.getLoanAccountAssociationDetails(loanId);
} else {
return this.loansService.getWorkingCapitalLoannDetails(loanId);
return this.loansService.getWorkingCapitalLoanDetails(loanId);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@ import { Observable } from 'rxjs';

/** Custom Services */
import { LoansService } from '../loans.service';
import { LoanProductService } from 'app/products/loan-products/services/loan-product.service';

/**
* Loan accounts template data resolver.
*/
@Injectable()
export class LoansAccountAndTemplateResolver {
private loansService = inject(LoansService);
private loanProductService = inject(LoanProductService);

/**
* Returns the loan account template data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
return this.loansService.getLoansAccountAndTemplateResource(loanId);
return this.loanProductService.isLoanProduct
? this.loansService.getLoansAccountAndTemplateResource(loanId)
: this.loansService.getWorkingCapitalLoanDetails(loanId);
}
Comment on lines 32 to 37
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the route configuration to verify resolver ordering
rg -n "LoansAccountAndTemplateResolver" --type ts -A 5 -B 5

Repository: openMF/web-app

Length of output: 5266


🏁 Script executed:

#!/bin/bash
# Check the full route configuration for edit-loans-account and surrounding routes
rg -n "edit-loans-account|LoanDetailsResolver" src/app/loans/loans-routing.module.ts -B 3 -A 8

Repository: openMF/web-app

Length of output: 5534


🏁 Script executed:

#!/bin/bash
# Check the LoanProductService initialization and isLoanProduct property
rg -n "isLoanProduct|initialize" src/app/loans -B 2 -A 3 --type ts | head -100

Repository: openMF/web-app

Length of output: 9749


🏁 Script executed:

#!/bin/bash
# Check if productType is used in edit-loans-account navigation or routes
rg -n "productType|edit-loans-account" src/app/loans --type ts | grep -A 3 -B 3 "edit-loans-account"

Repository: openMF/web-app

Length of output: 4331


🏁 Script executed:

#!/bin/bash
# Find LoanProductService class and its properties
fd -e service.ts | xargs grep -l "LoanProductService" | head -5

Repository: openMF/web-app

Length of output: 121


🏁 Script executed:

#!/bin/bash
# Search for resolveProductTypeDefault method implementation
rg -n "resolveProductTypeDefault" src/app --type ts -B 2 -A 10

Repository: openMF/web-app

Length of output: 6349


🏁 Script executed:

#!/bin/bash
# Check LoanProductService for isLoanProduct property and initialization
rg -n "class LoanProductService|isLoanProduct|initialize\(" src/app/loans -B 2 -A 5 --type ts | grep -A 20 "class LoanProductService"

Repository: openMF/web-app

Length of output: 40


🏁 Script executed:

#!/bin/bash
# Find and read the LoanProductService file
cat -n src/app/products/loan-products/services/loan-product.service.ts | head -150

Repository: openMF/web-app

Length of output: 2326


🏁 Script executed:

#!/bin/bash
# Check for isLoanProduct and initialize method in the service
rg -n "isLoanProduct|initialize" src/app/products/loan-products/services/loan-product.service.ts -B 3 -A 5

Repository: openMF/web-app

Length of output: 1265


The resolver must initialize the product type from route parameters instead of relying on pre-existing service state.

This resolver depends on loanProductService.isLoanProduct being correctly set, but Angular executes resolvers before the component's constructor. The component initializes the service from query parameters in its constructor (line 78), which runs after the resolver.

If the resolver executes without the productType query parameter being present or the service not yet initialized from a previous page, isLoanProduct will incorrectly default to true, causing the resolver to call getLoansAccountAndTemplateResource() instead of getWorkingCapitalLoanDetails() for working-capital loans.

The resolver should read productType directly from the route's query parameters (as resolveProductTypeDefault does) and call initialize() itself:

const productType = LoanProductBaseComponent.resolveProductTypeDefault(route, 'loan');
this.loanProductService.initialize(productType);
return this.loanProductService.isLoanProduct
  ? this.loansService.getLoansAccountAndTemplateResource(loanId)
  : this.loansService.getWorkingCapitalLoanDetails(loanId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/loans/common-resolvers/loans-account-and-template.resolver.ts` around
lines 32 - 37, The resolver's resolve method must derive productType from the
route instead of relying on existing loanProductService state; call
LoanProductBaseComponent.resolveProductTypeDefault(route, 'loan') to get the
productType, invoke this.loanProductService.initialize(productType), then use
this.loanProductService.isLoanProduct to choose between
this.loansService.getLoansAccountAndTemplateResource(loanId) and
this.loansService.getWorkingCapitalLoanDetails(loanId); update the
resolve(route: ActivatedRouteSnapshot) implementation accordingly.

}
Loading
Loading