Files
cbsnexora/src/app/account-transfers/make-account-transfers/make-account-transfers.component.ts
T

455 lines
14 KiB
TypeScript

/**
* 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, AfterViewInit, inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import {
AbstractControl,
UntypedFormBuilder,
UntypedFormGroup,
ValidationErrors,
Validators,
FormsModule
} from '@angular/forms';
/** Custom Services */
import { AccountTransfersService } from '../account-transfers.service';
import { SettingsService } from 'app/settings/settings.service';
import { ClientsService } from 'app/clients/clients.service';
import { Dates } from 'app/core/utils/dates';
/** Environment Configuration */
import { environment } from '../../../environments/environment';
import { MatDivider } from '@angular/material/divider';
import { MatFormField, MatLabel, MatHint, MatError } from '@angular/material/form-field';
import { MatAutocompleteTrigger, MatAutocomplete } from '@angular/material/autocomplete';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { CdkTextareaAutosize } from '@angular/cdk/text-field';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
/**
* Create account transfers
*/
@Component({
selector: 'mifosx-make-account-transfers',
templateUrl: './make-account-transfers.component.html',
styleUrls: ['./make-account-transfers.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatDivider,
FormsModule,
MatHint,
MatAutocompleteTrigger,
MatAutocomplete,
FaIconComponent,
CdkTextareaAutosize
]
})
export class MakeAccountTransfersComponent implements OnInit, AfterViewInit {
private formBuilder = inject(UntypedFormBuilder);
private route = inject(ActivatedRoute);
private router = inject(Router);
private accountTransfersService = inject(AccountTransfersService);
private dateUtils = inject(Dates);
private settingsService = inject(SettingsService);
private clientsService = inject(ClientsService);
/** Standing Instructions Data */
accountTransferTemplateData: any;
/** Minimum date allowed. */
minDate = new Date(2000, 0, 1);
/** Maximum date allowed. */
maxDate = new Date(2100, 0, 1);
/** Edit Standing Instructions form. */
makeAccountTransferForm: UntypedFormGroup;
/** To Office Type Data */
toOfficeTypeData: any;
/** To Client Type Data */
toClientTypeData: any;
/** To Account Type Data */
toAccountTypeData: any;
/** To Account Data */
toAccountData: any;
/** Account Type Id */
accountTypeId: any;
/** Account Type */
accountType: any;
/** Savings Id or Loans Id */
id: any;
/** Clients Data */
clientsData: any;
/** interbank transfer */
interbank: boolean = false;
/** Reference of phoneAccount search */
phoneAccount = '';
/** Interbank transfer form flag */
interbankTransferForm: boolean = false;
balance: number = 0;
isLoading: boolean = false;
/**
* Retrieves the standing instructions template from `resolve`.
* @param {ActivatedRoute} route Activated Route.
* @param {FormBuilder} formBuilder Form Builder
* @param {Router} router Router
* @param {AccountTransfersService} accountTransfersService Account Transfers Service
* @param {Dates} dateUtils Date Utils
* @param {SettingsService} settingsService Settings Service
* @param {ClientsService} clientsService Clients Service
*/
constructor() {
this.route.data.subscribe((data: { accountTransferTemplate: any }) => {
this.accountTransferTemplateData = data.accountTransferTemplate;
this.setParams();
this.setOptions();
});
}
/** Sets the value from the URL */
/** Sets the value from the URL */
setParams() {
this.accountType = this.route.snapshot.queryParams['accountType'];
switch (this.accountType) {
case 'fromloans':
this.accountTypeId = '1';
this.id = this.route.snapshot.queryParams['loanId'];
break;
case 'fromsavings':
case 'interbank':
this.accountTypeId = '2';
this.id = this.route.snapshot.queryParams['savingsId'];
this.interbank = this.route.snapshot.queryParams['interbank'] === 'true';
const navigationBalance = this.router.currentNavigation()?.extras?.state?.balance;
const templateBalance =
this.accountTransferTemplateData?.fromAccount?.availableBalance ??
this.accountTransferTemplateData?.fromAccount?.summary?.accountBalance ??
this.accountTransferTemplateData?.fromAccount?.balance ??
0;
this.balance = typeof navigationBalance === 'number' ? navigationBalance : templateBalance;
break;
default:
this.accountTypeId = '0';
}
}
/**
* Creates and sets the create standing instructions form.
*/
ngOnInit() {
this.maxDate = this.settingsService.businessDate;
if (!this.interbank) {
this.createMakeAccountTransferForm();
} else {
this.createEmptyInterbankForm();
}
}
/**
* Crea un formulario vacío para interbank mientras se carga
*/
createEmptyInterbankForm() {
this.makeAccountTransferForm = this.formBuilder.group({
toBank: [
'',
Validators.required
],
toClientId: [
'',
Validators.required
],
toAccountType: [
'',
Validators.required
],
toAccountId: [
'',
Validators.required
],
transferAmount: [
0,
[
Validators.required,
Validators.min(0.01),
this.amountExceedsBalanceValidator.bind(this)
]
],
transferDate: [
this.settingsService.businessDate,
Validators.required
],
transferDescription: [
'',
Validators.required
]
});
}
/**
* Creates the standing instruction form.
*/
createMakeAccountTransferForm() {
this.makeAccountTransferForm = this.formBuilder.group({
toOfficeId: [
'',
Validators.required
],
toClientId: [
'',
Validators.required
],
toAccountType: [
'',
Validators.required
],
toAccountId: [
'',
Validators.required
],
transferAmount: [
this.accountTransferTemplateData.transferAmount,
[
Validators.required,
Validators.min(0.01),
this.amountExceedsBalanceValidator.bind(this)
]
],
transferDate: [
this.settingsService.businessDate,
Validators.required
],
transferDescription: [
'',
Validators.required
]
});
}
createMakeAccountInterbankTransferForm(account: any) {
if (!account) {
console.error('Account data is undefined');
this.isLoading = false;
return;
}
const defaultAmount =
this.accountTransferTemplateData?.transferAmount > 0 ? this.accountTransferTemplateData.transferAmount : 1;
this.makeAccountTransferForm = this.formBuilder.group({
toBank: [
account.destinationFspId || '',
Validators.required
],
toClientId: [
(account.firstName || account.firsName || '') + ' ' + (account.lastName || ''),
Validators.required
],
toAccountType: [
'Saving Account',
Validators.required
],
toAccountId: [
account.partyId || '',
Validators.required
],
transferAmount: [
defaultAmount,
[
Validators.required,
Validators.min(0.01),
this.amountExceedsBalanceValidator.bind(this)
]
],
transferDate: [
this.settingsService.businessDate,
Validators.required
],
transferDescription: [
'Transferencia interbancaria',
Validators.required
]
});
this.isLoading = false;
}
amountExceedsBalanceValidator(control: AbstractControl): ValidationErrors | null {
const amount = control.value;
return amount > this.balance ? { amountExceedsBalance: true } : null;
}
/** Sets options value */
setOptions() {
this.toOfficeTypeData = this.accountTransferTemplateData.toOfficeOptions;
this.toAccountTypeData = this.accountTransferTemplateData.toAccountTypeOptions;
this.toAccountData = this.accountTransferTemplateData.toAccountOptions;
}
/** Executes on change of various select options */
changeEvent() {
const formValue = this.refineObject(this.makeAccountTransferForm.value);
this.accountTransfersService
.newAccountTranferResource(this.id, this.accountTypeId, formValue)
.subscribe((response: any) => {
this.accountTransferTemplateData = response;
this.toClientTypeData = response.toClientOptions;
this.setOptions();
});
}
/** Refine Object
* Removes the object param with null or '' values
*/
refineObject(dataObj: { [x: string]: any; transferAmount: any; transferDate: any; transferDescription: any }) {
delete dataObj.transferAmount;
delete dataObj.transferDate;
delete dataObj.transferDescription;
if (dataObj.toClientId && typeof dataObj.toClientId === 'object') {
dataObj.toClientId = dataObj.toClientId.id;
}
const propNames = Object.getOwnPropertyNames(dataObj);
for (let i = 0; i < propNames.length; i++) {
const propName = propNames[i];
if (dataObj[propName] === null || dataObj[propName] === undefined || dataObj[propName] === '') {
delete dataObj[propName];
}
}
return dataObj;
}
/**
* Subscribes to Clients search filter:
*/
ngAfterViewInit() {
if (!this.interbank && this.makeAccountTransferForm) {
this.makeAccountTransferForm.controls.toClientId.valueChanges.subscribe((value: any) => {
if (typeof value === 'string' && value.length >= 2) {
this.clientsService.getFilteredClients('displayName', 'ASC', true, value).subscribe((data: any) => {
this.clientsData = data.pageItems;
});
this.changeEvent();
}
});
}
}
/**
* Displays Client name in form control input.
* @param {any} client Client data.
* @returns {string} Client name if valid otherwise undefined.
*/
displayClient(client: any): string | undefined {
return client ? client.displayName : undefined;
}
/**
* Submits the standing instructions form
*/
submit() {
this.interbank ? this.makeInterbankTransfer() : this.makeTransfer();
}
makeTransfer() {
this.isLoading = true;
const dateFormat = this.settingsService.dateFormat;
const locale = this.settingsService.language.code;
let toClientIdValue: any;
if (typeof this.makeAccountTransferForm.controls.toClientId.value === 'object') {
toClientIdValue = this.makeAccountTransferForm.controls.toClientId.value.id;
} else {
toClientIdValue = this.makeAccountTransferForm.controls.toClientId.value;
}
const makeAccountTransferData = {
...this.makeAccountTransferForm.value,
transferDate: this.dateUtils.formatDate(this.makeAccountTransferForm.value.transferDate, dateFormat),
dateFormat,
locale,
toClientId: toClientIdValue,
fromAccountId: this.id,
fromAccountType: this.accountTypeId,
fromClientId: this.accountTransferTemplateData.fromClient.id,
fromOfficeId: this.accountTransferTemplateData.fromClient.officeId
};
this.accountTransfersService.createAccountTransfer(makeAccountTransferData).subscribe(() => {
this.isLoading = false;
this.router.navigate(['../../transactions'], { relativeTo: this.route });
});
}
makeInterbankTransfer() {
this.isLoading = true;
if (!this.makeAccountTransferForm.valid) {
console.error('Interbank form is not valid');
this.isLoading = false;
return;
}
const payload = {
homeTransactionId: crypto.randomUUID(),
from: {
fspId: environment.fineractPlatformTenantId,
idType: 'MSISDN',
idValue: this.accountTransferTemplateData.fromAccount.externalId?.trim() || ''
},
to: {
fspId: this.makeAccountTransferForm.controls.toBank.value,
idType: 'MSISDN',
idValue: this.makeAccountTransferForm.controls.toAccountId.value
},
amountType: 'SEND',
amount: {
currencyCode: this.accountTransferTemplateData.currency.code,
amount: this.makeAccountTransferForm.controls.transferAmount.value
},
transactionType: {
scenario: 'TRANSFER',
subScenario: 'DOMESTIC',
initiator: 'PAYER',
initiatorType: 'CUSTOMER'
},
note: this.makeAccountTransferForm.controls.transferDescription.value
};
this.accountTransfersService.sendInterbankTransfer(JSON.stringify(payload)).subscribe(
(trnsfr) => {
if (trnsfr.systemMessage) {
this.isLoading = false;
this.router.navigate(['../../transactions'], { relativeTo: this.route });
}
},
(error) => {
console.error('Interbank transfer error:', error);
this.isLoading = false;
}
);
}
searchAccountByNumber() {
if (!this.phoneAccount || this.phoneAccount.length !== 10) {
return;
}
this.isLoading = true;
this.accountTransfersService
.getAccountByNumber(this.phoneAccount, this.accountTransferTemplateData.currency.code)
.subscribe(
(acc) => {
this.interbankTransferForm = true;
this.createMakeAccountInterbankTransferForm(acc);
},
(error) => {
console.error('searching account error:', error);
this.isLoading = false;
}
);
}
}