/** * 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'; /** * Fixed Deposits Service. */ @Injectable({ providedIn: 'root' }) export class FixedDepositsService { private http = inject(HttpClient); /** * @param accountId Account Id. * @returns {Observable} Fixed Deposits data. */ getFixedDepositsAccountData(accountId: string): Observable { const httpParams = new HttpParams().set('associations', 'all'); return this.http.get(`/fixeddepositaccounts/${accountId}`, { params: httpParams }); } /** * @param {string} accountId fixed deposits account Id * @returns {Observable} */ deleteFixedDepositsAccount(accountId: string): Observable { return this.http.delete(`/fixeddepositaccounts/${accountId}`); } /** * @param {string} accountId Account Id * @param {string} command Command * @param {any} data Data * @returns {Observable} */ executeFixedDepositsAccountCommand(accountId: string, command: string, data: any): Observable { const httpParams = new HttpParams().set('command', command); return this.http.post(`/fixeddepositaccounts/${accountId}`, data, { params: httpParams }); } /** * @param {string} accountId Fixed Deposits Account Id * @param {string} transactionId Transaction Id * @returns {Observable} */ getFixedDepositsAccountTransaction(accountId: string, transactionId: string): Observable { return this.http.get(`/fixeddepositaccounts/${accountId}/transactions/${transactionId}`); } /** * @param {string} accountId Fixed Deposits Account Id * @param {string} command Command * @param {any} data Data * @param {string} transactionId Transaction Id * @returns {Observable} */ executeFixedDepositsAccountTransactionsCommand( accountId: string, command: string, data: any, transactionId?: any ): Observable { const httpParams = new HttpParams().set('command', command); return this.http.post(`/fixeddepositaccounts/${accountId}/transactions/${transactionId}`, data, { params: httpParams }); } /** * @param {string} accountId Fixed Deposits Account Id * @returns {Observable} */ getFixedDepositsAccountClosureTemplate(accountId: string): Observable { const httpParams = new HttpParams().set('command', 'close'); return this.http.get(`/fixeddepositaccounts/${accountId}/template`, { params: httpParams }); } /** * Template for the Payment action. Same shape as the closure template, but its `onAccountClosureOptions` also * include Partial Re-Invest, which the closure commands exclude because only Payment implements it. * @param {string} accountId Fixed Deposits Account Id * @returns {Observable} */ getFixedDepositsAccountPaymentTemplate(accountId: string): Observable { const httpParams = new HttpParams().set('command', 'payment'); return this.http.get(`/fixeddepositaccounts/${accountId}/template`, { params: httpParams }); } /** * @param {string} accountId Fixed Deposits Account Id * @returns {Observable} */ getFixedDepositsAccountTransactionTemplate(accountId: string): Observable { return this.http.get(`/fixeddepositaccounts/${accountId}/transactions/template`); } /** * @param clientId Client Id assosciated with fixed deposits account. * @returns {Observable} Fixed Deposits account template. */ getFixedDepositsAccountTemplate(clientId: string, productId?: string): Observable { let httpParams = new HttpParams().set('clientId', clientId); httpParams = productId ? httpParams.set('productId', productId) : httpParams; return this.http.get('/fixeddepositaccounts/template', { params: httpParams }); } /** * @param {string} accountId Fixed Deposits Account Id * @returns {Observable} */ getFixedDepositsAccountAndTemplate(accountId: any) { const httpParams = new HttpParams().set('associations', 'charges,+linkedAccount').set('template', 'true'); return this.http.get(`/fixeddepositaccounts/${accountId}`, { params: httpParams }); } /** * Calculate FD maturity using backend formula. * When submittedOnDate + daysInYear are provided, uses exact calendar-based calculation * matching the actual account creation engine. */ calculateFixedDepositInterest(params: { principalAmount: number; annualInterestRate: number; tenureInMonths?: number; tenureInDays?: number; // alternative to tenureInMonths, for sub-month FD Single tenures interestCompoundingPeriodInMonths: number; interestPostingPeriodInMonths: number; submittedOnDate?: string; // yyyy-MM-dd — enables exact calendar calculation daysInYear?: number; // 360 or 365 }): Observable { let httpParams = new HttpParams(); Object.entries(params).forEach( ([ key, val ]) => { if (val !== undefined && val !== null) { httpParams = httpParams.set(key, String(val)); } } ); return this.http.get(`/fixeddepositaccounts/calculate-fd-interest`, { params: httpParams }); } /** * FD Dashboard's "Renew" action on a matured FD whose product has a renewal grace period * configured: backdated to the original maturity date within the grace window, or from today * with a gap-period interest credit (at the linked Savings account's rate) once it has passed. * @param accountId Fixed Deposit Account Id * @returns {Observable} { renewedAccountId, maturityDate, maturityAmount, backdated, gapInterestAmount? } */ /** `reinvestType` is the staff's choice made at renewal time: 300 = Reinvest Principal + Interest, * 400 = Reinvest Principal Only (rest of the balance goes to the FD's linked Savings account). */ renewFixedDepositAccount(accountId: string | number, reinvestType: 300 | 400): Observable { return this.http.post(`/fixeddepositaccounts/${accountId}/renew`, { reinvestType }); } /** Pre-step the Payment dialog calls, from the FD Dashboard, before submitting the generic `payment` * command -- credits gap-period interest (at the linked account's rate) if the product's renewal * grace window has passed; a no-op (`gapInterestAmount: 0`) if still within it. */ creditGapInterestIfPastGrace(accountId: string | number): Observable { return this.http.post(`/fixeddepositaccounts/${accountId}/credit-gap-interest`, {}); } /** Read-only -- what a Renew is about to do (current balance, linked account's rate, gap days, * gap-interest amount, projected new balance), shown in the Payment dialog before anything is * confirmed. Posts nothing; safe to call as often as the dialog needs. */ getFdRenewalPreview(accountId: string | number): Observable { return this.http.get(`/fixeddepositaccounts/${accountId}/renewal-preview`); } /** * @param fixedDepositAccount Fixed Deposit Account * @returns {Observable} */ createFixedDepositAccount(fixedDepositAccount: any): Observable { return this.http.post(`/fixeddepositaccounts`, fixedDepositAccount); } /** * @param {any} accountId Account Id * @param {any} fixedDepositAccount Fixed Deposit Account * @returns {Observable} */ updateFixedDepositAccount(accountId: any, fixedDepositAccount: any): Observable { return this.http.put(`/fixeddepositaccounts/${accountId}`, fixedDepositAccount); } /** * @param clientId Client Id * @param clientName Client Name * @param fromAccountId Account Id * @param locale Locale * @param dateFormat Date Format * @returns {Observable} Standing Instructions */ getStandingInstructions( clientId: string, clientName: string, fromAccountId: string, locale: string, dateFormat: string ): Observable { const httpParams = new HttpParams() .set('clientId', clientId) .set('clientName', clientName) .set('fromAccountId', fromAccountId) .set('fromAccountType', '2') .set('locale', locale) .set('dateFormat', dateFormat); return this.http.get(`/standinginstructions`, { params: httpParams }); } }