Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { DateFormatPipe } from '../../../pipes/date-format.pipe';
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
import { CurrencyPipe } from '@angular/common';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';
import { SettingsService } from 'app/settings/settings.service';
import { Subject } from 'rxjs';
import { takeUntil, catchError } from 'rxjs/operators';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { DateFormatPipe } from '../../../pipes/date-format.pipe';
import { LegalFormId } from 'app/clients/models/legal-form.enum';
import { ClientsService } from 'app/clients/clients.service';
import { MatIcon } from '@angular/material/icon';
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';
import { SettingsService } from 'app/settings/settings.service';
import { AlertService } from 'app/core/alert/alert.service';
import { SystemService } from 'app/system/system.service';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';

/** Custom Services */
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';

/**
* Loans Transaction Reciept resolver.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/fo
import { ActivatedRoute, RouterLink } from '@angular/router';

/** Custom Services */
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';
import { Dates } from 'app/core/utils/dates';
import { SettingsService } from 'app/settings/settings.service';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import { Validators, UntypedFormGroup, UntypedFormControl, ReactiveFormsModule }
import { distinctUntilChanged } from 'rxjs/operators';

/** Custom Services */
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';
import { SettingsService } from 'app/settings/settings.service';

/** Custom Models */
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
import { SelectOption } from 'app/reports/common-models/select-option.model';
import { ReportParameter } from 'app/reporting-plugin/models/report-parameter.model';
import { SelectOption } from 'app/reporting-plugin/models/select-option.model';
import { Dates } from 'app/core/utils/dates';
import { MatDivider } from '@angular/material/divider';
import { NgFor, NgSwitch, NgIf, NgSwitchCase } from '@angular/common';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import {
} from '@angular/forms';

/** Custom Services */
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';

/** Custom Models */
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
import { ReportParameter } from 'app/reporting-plugin/models/report-parameter.model';
import { SettingsService } from 'app/settings/settings.service';
import { MatCheckbox } from '@angular/material/checkbox';
import { EditBusinessRuleParametersComponent } from './edit-business-rule-parameters/edit-business-rule-parameters.component';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import { Component, OnChanges, Input, Output, EventEmitter, OnInit, inject } fro
import { Validators, UntypedFormGroup, UntypedFormControl, ReactiveFormsModule } from '@angular/forms';

/** Custom Services */
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';
import { SettingsService } from 'app/settings/settings.service';

/** Custom Models */
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
import { SelectOption } from 'app/reports/common-models/select-option.model';
import { ReportParameter } from 'app/reporting-plugin/models/report-parameter.model';
import { SelectOption } from 'app/reporting-plugin/models/select-option.model';
import { Dates } from 'app/core/utils/dates';
import { MatDivider } from '@angular/material/divider';
import { NgFor, NgSwitch, NgIf, NgSwitchCase } from '@angular/common';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import {
} from '@angular/forms';

/** Custom Services */
import { ReportsService } from 'app/reports/reports.service';
import { ReportsService } from 'app/reporting-plugin/services/reports.service';

/** Custom Models */
import { ReportParameter } from 'app/reports/common-models/report-parameter.model';
import { ReportParameter } from 'app/reporting-plugin/models/report-parameter.model';

/** Custom Components */
import { BusinessRuleParametersComponent } from './business-rule-parameters/business-rule-parameters.component';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@

@if (taxComponentData.debitAccountType) {
<div class="flex-50">
{{
'labels.inputs.accounting.' + (taxComponentData.debitAccountType.value?.split('.')[1] || '').toUpperCase()
| translate
}}
{{ taxComponentData.debitAccountType.code | translateKey: 'inputs.accounting' }}
</div>
}

Expand All @@ -67,10 +64,7 @@

@if (taxComponentData.creditAccountType) {
<div class="flex-50">
{{
'labels.inputs.accounting.' +
(taxComponentData.creditAccountType.value?.split('.')[1] || '').toUpperCase() | translate
}}
{{ taxComponentData.creditAccountType.code | translateKey: 'inputs.accounting' }}
</div>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/** Chart Data Response Interface */
export interface ChartDataResponse {
columnHeaders: { columnName: string }[];
data: { row: (string | number)[] }[];
}

/** Chart Data Model */
export class ChartData {
keysLabel: string;
valuesLabel: string;
keys: string[];
values: number[];

constructor(response: any) {
constructor(response: ChartDataResponse) {
this.keysLabel = response.columnHeaders[0].columnName;
this.valuesLabel = response.columnHeaders[1].columnName;
this.keys = response.data.map((object: any) => object.row[0]);
this.values = response.data.map((object: any) => object.row[1]);
this.keys = response.data.map((object: { row: (string | number)[] }) => object.row[0] as string);
this.values = response.data.map((object: { row: (string | number)[] }) => object.row[1] as number);
}
}
40 changes: 40 additions & 0 deletions src/app/reporting-plugin/models/report-parameter.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* 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/.
*/

/** Custom Models */
import { SelectOption } from './select-option.model';

/** Report Parameter Model */
export class ReportParameter {
name: string;
variable: string;
label: string;
displayType: string;
formatType: string;
defaultVal: string | number | boolean | null;
selectOne: string | null;
selectAll: string | null;
parentParameterName: string | null;
inputName: string;
selectOptions: SelectOption[] = [];
childParameters: ReportParameter[] = [];
pentahoName: string | null;

constructor(options: (string | number | boolean | null)[]) {
this.name = options[0] as string;
this.variable = options[1] as string;
this.label = options[2] as string;
this.displayType = options[3] as string;
this.formatType = options[4] as string;
this.defaultVal = options[5];
this.selectOne = options[6];
this.selectAll = options[7];
this.parentParameterName = options[8] as string | null;
this.inputName = `R_${this.variable}`;
}
}
Comment on lines +13 to +40
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n '"strictPropertyInitialization"|"strict": true' -g 'tsconfig*.json'
rg -n 'pentahoName|constructor\(options' src/app/reporting-plugin/models/report-parameter.model.ts

Repository: openMF/web-app

Length of output: 204


🏁 Script executed:

# Find where ReportParameter is instantiated
rg -n 'new ReportParameter' -A 3 src/app/reporting-plugin/services/reports.service.ts

# Check for any usage of pentahoName
rg -n 'pentahoName' src/app/reporting-plugin/

# Check the structure of entry.row to understand the payload shape
rg -n 'entry\.row|\.row\[' src/app/reporting-plugin/services/reports.service.ts -B 2 -A 2

Repository: openMF/web-app

Length of output: 699


🏁 Script executed:

# Check all usages of pentahoName across the entire codebase
rg -n 'pentahoName' --type ts --type html --type tsx

# Get the full ReportParameter model to see the complete context
cat -n src/app/reporting-plugin/models/report-parameter.model.ts

# Check if there are any tests or other files that might show the expected payload shape
rg -n 'entry\.row' -B 3 -A 3 src/app/reporting-plugin/

Repository: openMF/web-app

Length of output: 3000


🏁 Script executed:

# Check the main tsconfig.json for strict mode setting
cat -n tsconfig.json | grep -A 5 -B 5 '"strict"'

# Also verify if there are any compilation errors reported
git status

Repository: openMF/web-app

Length of output: 258


🏁 Script executed:

# Check main tsconfig.json directly
head -50 tsconfig.json

Repository: openMF/web-app

Length of output: 741


Improve type safety of the constructor by using a tuple type instead of a loose array.

The constructor accepts (string | number | boolean | null)[] but always accesses exactly 9 indices matching the API payload from entry.row. This should be a tuple type for better type safety and to allow the compiler to guarantee the correct properties are assigned without casts.

Additionally, pentahoName is declared on line 26 but never assigned in the constructor and has no usages in the codebase—either initialize it or remove it.

🔧 Proposed fix
 export class ReportParameter {
   name: string;
   variable: string;
   label: string;
   displayType: string;
   formatType: string;
   defaultVal: string | number | boolean | null;
   selectOne: string | null;
   selectAll: string | null;
   parentParameterName: string | null;
   inputName: string;
   selectOptions: SelectOption[] = [];
   childParameters: ReportParameter[] = [];
-  pentahoName: string | null;
+  pentahoName: string | null = null;
 
-  constructor(options: (string | number | boolean | null)[]) {
-    this.name = options[0] as string;
-    this.variable = options[1] as string;
-    this.label = options[2] as string;
-    this.displayType = options[3] as string;
-    this.formatType = options[4] as string;
+  constructor(
+    options: [string, string, string, string, string, string | number | boolean | null, string | null, string | null, string | null]
+  ) {
+    this.name = options[0];
+    this.variable = options[1];
+    this.label = options[2];
+    this.displayType = options[3];
+    this.formatType = options[4];
     this.defaultVal = options[5];
     this.selectOne = options[6];
     this.selectAll = options[7];
-    this.parentParameterName = options[8] as string | null;
+    this.parentParameterName = options[8];
     this.inputName = `R_${this.variable}`;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/reporting-plugin/models/report-parameter.model.ts` around lines 13 -
40, Change the ReportParameter constructor to accept a fixed tuple instead of a
loose array (e.g. [string, string, string, string, string,
string|number|boolean|null, string|null, string|null, string|null]) so the
compiler enforces the nine expected indices used to set name, variable, label,
displayType, formatType, defaultVal, selectOne, selectAll, parentParameterName
in the ReportParameter class; update the constructor parameter type and remove
the explicit casts (as string | null) when assigning these fields. Also address
the unused pentahoName property on ReportParameter—either initialize pentahoName
in the constructor from an appropriate tuple slot or remove the pentahoName
declaration if it’s not needed. Ensure inputName is still set as
`R_${this.variable}` after assignment.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class SelectOption {
id: number;
name: string;

constructor(options: any[]) {
constructor(options: [number, string]) {
this.id = options[0];
this.name = options[1];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';

/** Custom Services */
import { ReportsService } from '../reports.service';
import { ReportsService } from '../services/reports.service';

/**
* Reports data resolver.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ActivatedRouteSnapshot } from '@angular/router';
import { Observable } from 'rxjs';

/** Custom Services */
import { ReportsService } from '../reports.service';
import { ReportsService } from '../services/reports.service';

/**
* Reports data resolver.
Expand Down
186 changes: 186 additions & 0 deletions src/app/reporting-plugin/services/reports.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/**
* 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';
import { HttpClient, HttpParams } from '@angular/common/http';

/** rxjs Imports */
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

/** Custom Models */
import { ReportParameter } from '../models/report-parameter.model';
import { SelectOption } from '../models/select-option.model';
import { ChartData } from '../models/chart-data.model';

/**
* Reports service.
*/
@Injectable({
providedIn: 'root'
})
export class ReportsService {
private http = inject(HttpClient);

/**
* @returns {Observable<any>} Reports data
*/
getReports(): Observable<any> {
return this.http.get('/reports');
}

/**
* @param {string} officeId Office ID to filter reports.
* @returns {Observable<any>} Reports data for a specific office.
*/
getReportsByOffice(officeId: string): Observable<any> {
const httpParams = new HttpParams().set('officeId', officeId);
return this.http.get('/reports', { params: httpParams });
}

/**
* @param {string} reportName Report name for which parameters are needed.
* @returns {Observable<ReportParameter[]>}
*/
getReportParams(reportName: string): Observable<ReportParameter[]> {
const httpParams = new HttpParams().set('R_reportListing', `'${reportName}'`).set('parameterType', 'true');
return this.http
.get(`/runreports/FullParameterList`, { params: httpParams })
.pipe(map((response: any) => response.data.map((entry: any) => new ReportParameter(entry.row))));
}

/**
* @param {string} inputString URL substring containing object details.
* @returns {Observable<SelectOption[]>}
*/
getSelectOptions(inputString: string): Observable<SelectOption[]> {
const httpParams = new HttpParams().set('parameterType', 'true');
return this.http
.get(`/runreports/${inputString}`, { params: httpParams })
.pipe(map((response: any) => response.data.map((entry: any) => new SelectOption(entry.row))));
}

/**
* @param {number} reportId Report id for which pentaho parameters are needed.
* @returns {Observable<any>}
*/
getPentahoParams(reportId: number): Observable<any> {
const httpParams = new HttpParams().set('fields', 'reportParameters');
return this.http
.get(`/reports/${reportId}`, { params: httpParams })
.pipe(map((response: any) => response.reportParameters));
}

/**
* Run Report Data for Table and SMS.
* @param {any} reportName report name
* @param {object} formData Form Data.
* @returns {Observable<any>}
*/
getRunReportData(reportName: string, formData: object): Observable<any> {
let httpParams = new HttpParams();
for (const [
key,
value
] of Object.entries(formData)) {
httpParams = httpParams.set(key, value);
}
return this.http.get(`/runreports/${reportName}`, { params: httpParams });
}

/**
* Run Report Data for Charts.
* @param {any} reportName report name
* @param {object} formData Form Data.
* @returns {Observable<ChartData>}
*/
getChartRunReportData(reportName: string, formData: object): Observable<ChartData> {
let httpParams = new HttpParams();
for (const [
key,
value
] of Object.entries(formData)) {
httpParams = httpParams.set(key, value);
}
return this.http
.get(`/runreports/${reportName}`, { params: httpParams })
.pipe(map((response: any) => new ChartData(response)));
}

/**
* Run Report Data for Pentaho.
* @param {any} reportName report name
* @param {object} formData Form Data.
* @returns {Observable<any>}
*/
getPentahoRunReportData(
reportName: string,
formData: object,
tenantIdentifier: string,
locale: string,
dateFormat: string
): Observable<any> {
let httpParams = new HttpParams()
.set('tenantIdentifier', tenantIdentifier)
.set('locale', locale)
.set('dateFormat', dateFormat);
for (const [
key,
value
] of Object.entries(formData)) {
httpParams = httpParams.set(key, value);
}
return this.http.get(`/runreports/${reportName}`, {
responseType: 'arraybuffer',
observe: 'response',
params: httpParams
});
}

/**
* @param {number} reportId
* @returns {Observable<any>}
*/
getBirtParams(reportId: number): Observable<any> {
const httpParams = new HttpParams().set('fields', 'reportParameters');
return this.http
.get(`/reports/${reportId}`, { params: httpParams })
.pipe(map((response: any) => response.reportParameters));
}

/**
* Run Report Data for BIRT.
* @param {any} reportName
* @param {object} formData
* @returns {Observable<any>}
*/
getBirtRunReportData(
reportName: string,
formData: object,
tenantIdentifier: string,
locale: string,
dateFormat: string
): Observable<any> {
let httpParams = new HttpParams()
.set('tenantIdentifier', tenantIdentifier)
.set('locale', locale)
.set('dateFormat', dateFormat);
for (const [
key,
value
] of Object.entries(formData)) {
httpParams = httpParams.set(key, value);
}
return this.http.get(`/runreports/${reportName}`, {
responseType: 'arraybuffer',
observe: 'response',
params: httpParams
});
}
}
Loading