diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1f0e7868ec..f62a207589 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,7 @@ /** Angular Imports */ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; +import { I18nService } from './core/i18n/i18n.service'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpBackend, @@ -129,7 +130,8 @@ export function HttpLoaderFactory(http: HttpClient) { provide: HTTP_INTERCEPTORS, useClass: !environment.OIDC.oidcServerEnabled ? TokenInterceptor : ZitadelTokenInterceptor, multi: true - } + }, + I18nService ] }) export class AppModule {} diff --git a/src/app/loans/glim-account/create-glim-account/create-glim-account.component.ts b/src/app/loans/glim-account/create-glim-account/create-glim-account.component.ts index f4f943e867..044ecef291 100644 --- a/src/app/loans/glim-account/create-glim-account/create-glim-account.component.ts +++ b/src/app/loans/glim-account/create-glim-account/create-glim-account.component.ts @@ -177,58 +177,42 @@ export class CreateGlimAccountComponent { }; } - setData(client: any, totalLoan: number): any { + setData(applicationId: number, client: any, totalLoan: number, isFirst: boolean, isLast: boolean): any { const locale = this.settingsService.language.code; const dateFormat = this.settingsService.dateFormat; // const monthDayFormat = 'dd MMMM'; - const data = { + const data: any = { ...this.loansAccount, - charges: (this.loansAccount.charges ?? []) - .map((charge: any) => { - const chargeId = charge.chargeId ?? charge.id; - if (chargeId == null) { - return null; - } - const mappedCharge: any = { - chargeId, - amount: charge.amount - }; - if (charge.id && charge.id !== chargeId) { - mappedCharge.id = charge.id; - } - if (charge.dueDate) { - mappedCharge.dueDate = this.dateUtils.formatDate(charge.dueDate, dateFormat); - } - if (charge.feeInterval !== undefined) { - mappedCharge.feeInterval = charge.feeInterval; - } - if (charge.feeOnMonthDay !== undefined) { - mappedCharge.feeOnMonthDay = charge.feeOnMonthDay; - } - return mappedCharge; - }) - .filter(Boolean), + charges: this.loansAccount.charges.map((charge: any) => ({ + chargeId: charge.id, + amount: charge.amount, + currency: charge.currency + })), clientId: client.id, totalLoan: totalLoan, loanType: 'glim', + applicationId: applicationId, amortizationType: 1, - isParentAccount: true, principal: client.principal, syncDisbursementWithMeeting: false, expectedDisbursementDate: this.dateUtils.formatDate(this.loansAccount.expectedDisbursementDate, dateFormat), submittedOnDate: this.dateUtils.formatDate(this.loansAccount.submittedOnDate, dateFormat), - dateFormat, - // monthDayFormat, - locale + dateFormat: dateFormat, + locale: locale, + groupId: this.loansAccountTemplate.group.id }; - data.groupId = this.loansAccountTemplate.group.id; - + if (isFirst) { + data.isParentAccount = true; + } + if (isLast) { + data.lastApplication = true; + } delete data.principalAmount; - // TODO: 2025-03-17: Apparently (?!) unsupported for GLIM delete data.allowPartialPeriodInterestCalculation; delete data.multiDisburseLoan; delete data.isFloatingInterestRate; - + delete data.moratoriumPrincipal; + delete data.moratoriumInterest; return JSON.stringify(data); } @@ -237,12 +221,17 @@ export class CreateGlimAccountComponent { const requestData = []; const memberSelected = this.selectedMembers?.selectedMembers ?? []; const totalLoan = this.totalLoanAmount(); + const applicationId = Math.floor(1000000000 + Math.random() * 9000000000); + for (let index = 0; index < memberSelected.length; index++) { + const isFirst = index === 0; + const isLast = index === memberSelected.length - 1; requestData.push({ requestId: index.toString(), + reference: index === 0 ? null : (index - 1).toString(), method: 'POST', relativeUrl: 'loans', - body: this.setData(memberSelected[index], totalLoan) + body: this.setData(applicationId, memberSelected[index], totalLoan, isFirst, isLast) }); } return requestData; @@ -257,57 +246,10 @@ export class CreateGlimAccountComponent { return total; } - /** - * Creates a new GLIM account. - */ - submit() { - this.selectedMembers = this.loansActiveClientMembers?.selectedClientMembers; - const memberSelected = this.loansActiveClientMembers?.selectedClientMembers?.selectedMembers ?? []; - if (!memberSelected.length) return; - const gsimMemberIds = new Set(this.dataSource.map((m: any) => Number(m.id))); - for (const member of memberSelected) { - const memberId = Number(member.id); - // Validate savings account ownership - const ownerId = Number(member.linkAccountOwnerId); - if (member.linkAccountId && member.linkAccountOwnerId && ownerId !== memberId) { - this.i18nService.translate('errors.linkedSavingsAccountOwnership').subscribe((msg: string) => { - this.notify({ defaultUserMessage: msg, errors: [] }, { memberId }); - }); - return; - } - // Validate GSIM membership - if (!gsimMemberIds.has(memberId)) { - this.i18nService.translate('errors.clientNotInGSIM', { id: memberId }).subscribe((msg: string) => { - this.notify({ defaultUserMessage: msg, errors: [] }, { memberId }); - }); - return; - } - } - - // Use date format from settingsService for interestChargedFromDate - const data = this.buildRequestData(); - this.loansService.createGlimAccount(data).subscribe((response: any) => { - const body = JSON.parse(response[0].body); - if (body.glimId) { - this.router.navigate( - [ - '../', - body.glimId - ], - { relativeTo: this.route } - ); - } else { - this.notify(body, { batchSize: data.length }); - } - }); - } - - notify(body: any, context?: { [k: string]: unknown }) { - const parts: string[] = [String(body?.defaultUserMessage ?? '')]; - if (Array.isArray(body?.errors)) { - for (const e of body.errors) parts.push(String(e?.developerMessage ?? '')); - } - if (context) parts.push(`Context: ${JSON.stringify(context)}`); - console.error(parts.join(' ').trim()); + notify(body: any, data: any) { + let message = body.defaultUserMessage + ' '; + body.errors?.forEach((error: any) => (message += error.developerMessage + ' ')); + message += 'Data: ' + JSON.stringify(data); + console.error(message); } } diff --git a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html index eb7e364c8b..e322ffd71b 100644 --- a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html +++ b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html @@ -26,7 +26,7 @@ {{ 'labels.inputs.name' | translate }} - {{ charge.name + ', ' + (charge.currency?.displaySymbol || '') }} + {{ charge.name }}, {{ charge.currency.displaySymbol }} @@ -41,7 +41,13 @@ {{ 'labels.inputs.Amount' | translate }} {{ charge.amount }} - @@ -94,7 +100,7 @@ {{ 'labels.inputs.Actions' | translate }} - @@ -111,7 +117,9 @@

{{ 'labels.heading.Overdue Charges' | translate }} {{ 'labels.inputs.name' | translate }} - {{ charge.name + ', ' + (charge.currency?.displaySymbol || '') }} + + {{ charge.name }}, {{ charge.currency.displaySymbol }} + @@ -121,7 +129,18 @@

{{ 'labels.heading.Overdue Charges' | translate }} {{ 'labels.inputs.Amount' | translate }} - {{ charge.amount | formatNumber }} + + {{ charge.amount | formatNumber }} + + @@ -135,17 +154,23 @@

{{ 'labels.heading.Overdue Charges' | translate }}
- - + <<<<<<< HEAD @if (loanId) { } + ======= + + >>>>>>> 140a28dfb (WEB-95: Apply cherry-picked changes)
diff --git a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts index 321ae8c523..cfa61bc71e 100644 --- a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts +++ b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts @@ -323,6 +323,32 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges { }); } + editOverdueChargeAmount(charge: { amount: number; [key: string]: any }) { + const formfields: FormfieldBase[] = [ + new InputBase({ + controlName: 'amount', + label: 'Amount', + value: charge.amount, + type: 'number', + required: false + }) + ]; + const data = { + title: 'Edit Overdue Charge Amount', + layout: { addButtonText: 'Confirm' }, + formfields: formfields + }; + const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data }); + editNoteDialogRef.afterClosed().subscribe((response?: { data?: { value: { amount: number } } }) => { + if (response?.data) { + const newCharge = { ...charge, amount: response.data.value.amount }; + this.overDueChargesDataSource.splice(this.overDueChargesDataSource.indexOf(charge), 1, newCharge); + this.overDueChargesDataSource = this.overDueChargesDataSource.concat([]); + this.pristine = false; + } + }); + } + get isValid() { return true; // !this.activeClientMembers || diff --git a/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html index c673c84113..c6a53cf757 100644 --- a/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html +++ b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html @@ -332,10 +332,10 @@

{{ 'labels.heading.Charges' | translate }}

} @if ( !( - charge.chargeTimeType.value === 'Monthly Fee' || - charge.chargeTimeType.value === 'Annual Fee' || - charge.chargeTimeType.value === 'Specified due date' || - charge.chargeTimeType.value === 'Weekly Fee' + charge.chargeTimeType?.value === 'Monthly Fee' || + charge.chargeTimeType?.value === 'Annual Fee' || + charge.chargeTimeType?.value === 'Specified due date' || + charge.chargeTimeType?.value === 'Weekly Fee' ) ) { diff --git a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html index d9bab5f2d6..0ded50d87e 100644 --- a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html +++ b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html @@ -65,6 +65,7 @@

{{ 'labels.inputs.Repayments' | translate }}

matInput formControlName="numberOfRepayments" matTooltip="{{ 'tooltips.Enter the total count of repayments' | translate }}" + aria-label="Number of repayments" /> @if (loansAccountTermsForm.controls.numberOfRepayments.hasError('required')) { @@ -90,6 +91,7 @@

{{ 'labels.inputs.Repayments' | translate }}

matTooltip="{{ 'tooltips.May be entered to override' | translate }}" [matDatepicker]="repaymentsPicker" formControlName="repaymentsStartingFromDate" + aria-label="First repayment on" /> @@ -104,6 +106,7 @@

{{ 'labels.inputs.Repayments' | translate }}

[matDatepicker]="interestPicker" matTooltip="{{ 'tooltips.May be entered to override the date' | translate }}" formControlName="interestChargedFromDate" + aria-label="Interest charged from" /> @@ -125,6 +128,7 @@

required formControlName="repaymentEvery" matTooltip="{{ 'tooltips.Fields are input to calculating the repayment schedule' | translate }}" + aria-label="Repaid every" /> @if (loansAccountTermsForm.controls.repaymentEvery.hasError('required')) { @@ -190,7 +194,7 @@

{{ 'labels.inputs.Nominal interest rate' | translate @if (!loansAccountTermsData?.isLoanProductLinkedToFloatingRate) { {{ 'labels.inputs.Nominal interest rate' | translate }} % - + {{ 'labels.inputs.Frequency' | translate }} @@ -367,6 +371,7 @@

{{ 'labels.heading.Interest Calculations' | translate type="number" formControlName="inArrearsTolerance" matTooltip="{{ 'tooltips.With Arrears tolerance' | translate }}" + aria-label="Arrears tolerance" /> @@ -376,6 +381,7 @@

{{ 'labels.heading.Interest Calculations' | translate matInput formControlName="graceOnInterestCharged" matTooltip="{{ 'tooltips.If the Interest Free Period' | translate }}" + aria-label="Interest free period" /> @@ -386,17 +392,17 @@

{{ 'labels.inputs.Grace on principal payment' | translate }} - + {{ 'labels.inputs.Grace on interest payment' | translate }} - + {{ 'labels.inputs.On arrears ageing' | translate }} - + @if (isDelinquencyEnabled()) { @@ -567,6 +573,7 @@

{{ 'labels.heading.Loan Tranche Details' | translate type="button" mat-icon-button color="primary" + aria-label="Add Disbursement Data Entry" required (click)="addDisbursementDataEntry(disbursementData)" [disabled]="isMultiDisbursedCompleted" @@ -602,6 +609,7 @@

type="button" mat-icon-button color="warn" + aria-label="Remove Disbursement Data Entry" (click)="removeDisbursementDataEntry(rowIndex)" matTooltip="{{ 'tooltips.Delete' | translate }}" matTooltipPosition="left" @@ -677,7 +685,7 @@

{{ 'labels.heading.Collaterals Data' | translate }}
-
@@ -707,7 +715,13 @@

{{ 'labels.heading.Collaterals Data' | translate }} {{ 'labels.inputs.Actions' | translate }} - @@ -719,11 +733,11 @@

{{ 'labels.heading.Collaterals Data' | translate }}
- - diff --git a/src/app/login/login-form/login-form.component.ts b/src/app/login/login-form/login-form.component.ts index 633e14fe35..dd2099292d 100644 --- a/src/app/login/login-form/login-form.component.ts +++ b/src/app/login/login-form/login-form.component.ts @@ -10,7 +10,7 @@ import { AuthenticationService } from '../../core/authentication/authentication. import { MatFormField, MatPrefix, MatLabel, MatError, MatSuffix } from '@angular/material/form-field'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { MatIconButton, MatButton } from '@angular/material/button'; -import { MatCheckbox } from '@angular/material/checkbox'; +import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatProgressBar } from '@angular/material/progress-bar'; import { MatProgressSpinner } from '@angular/material/progress-spinner'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @@ -31,6 +31,7 @@ import { environment } from '../../../environments/environment'; MatPrefix, FaIconComponent, MatIconButton, + MatCheckboxModule, MatProgressBar, MatProgressSpinner ]