/** * 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 { Component, OnInit, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { DateDdmmyyyyPickerComponent } from '../date-ddmmyyyy-picker/date-ddmmyyyy-picker.component'; import { addPeriod, todayDDMMYYYY } from '../investment-data'; import { InvestmentDiaryDto, InvestmentDiaryRowDto, InvestmentService, apiErrorMessage } from '../investment.service'; type InstrumentFilter = '' | 'SECURITY' | 'FD'; type StatusFilter = '' | 'PENDING' | 'POSTED'; /** * Due Date Diary for All Investment: coupons, 31 Mar / 30 Sep provisions, premium amortisation, maturities and FD * payouts in a period, with what is posted, due or overdue, and a link to the screen that posts each one. */ @Component({ selector: 'mifosx-investment-diary', templateUrl: './investment-diary.component.html', styleUrls: [ '../../cashier/cashier-shared.scss', '../investment-shared.scss', './investment-diary.component.scss' ], standalone: true, imports: [ CommonModule, FormsModule, RouterModule, DateDdmmyyyyPickerComponent ] }) export class InvestmentDiaryComponent implements OnInit { private readonly investmentService = inject(InvestmentService); // Opens on everything still to be done: what is pending from the past year and what falls due in the next 30 days. fromDate = addPeriod(todayDDMMYYYY(), -1, 0, 0); toDate = addPeriod(todayDDMMYYYY(), 0, 0, 30); instrument: InstrumentFilter = ''; status: StatusFilter = 'PENDING'; diary: InvestmentDiaryDto | null = null; loading = false; error = ''; readonly kindLabels: Record = { COUPON: 'Coupon', PROVISION: 'Interest provision', MATURITY: 'Maturity / redemption', AMORTISATION: 'Premium amortisation', FD_PROVISION: 'FD interest provision', FD_PAYOUT: 'FD interest payout', FD_MATURITY: 'FD maturity' }; ngOnInit(): void { this.load(); } /** Quick ranges from today. */ setRange(days: number): void { this.status = ''; this.fromDate = todayDDMMYYYY(); this.toDate = addPeriod(this.fromDate, 0, 0, days); this.load(); } /** What is still pending from the past (up to a year back) and today. */ showOverdue(): void { this.toDate = todayDDMMYYYY(); this.fromDate = addPeriod(this.toDate, -1, 0, 0); this.status = 'PENDING'; this.load(); } load(): void { if (!this.fromDate || !this.toDate) { this.error = 'Enter the From and To dates.'; return; } this.loading = true; this.error = ''; this.investmentService.getDiary(this.fromDate, this.toDate).subscribe({ next: (d) => { this.diary = d; this.loading = false; }, error: (err) => { this.diary = null; this.loading = false; this.error = apiErrorMessage(err); } }); } get rows(): InvestmentDiaryRowDto[] { return (this.diary?.rows ?? []).filter( (r) => (!this.instrument || r.instrument === this.instrument) && (!this.status || (this.status === 'POSTED' ? r.status === 'POSTED' : r.status !== 'POSTED')) ); } /** The screen that posts this entry. */ link(r: InvestmentDiaryRowDto): { path: string; query: Record } { if (r.instrument === 'FD') { return { path: '/investment/fd', query: { tab: 'interest', fd: r.refId } }; } const tab = r.kind === 'MATURITY' ? 'redemption' : r.kind === 'AMORTISATION' ? 'amortize' : 'interest'; return { path: '/investment/bonds', query: { tab } }; } statusLabel(r: InvestmentDiaryRowDto): string { return r.status === 'DUE' ? 'Due today' : r.status.charAt(0) + r.status.slice(1).toLowerCase(); } }