feat(investment): Daily Script Master, market rate on purchase, broken-period interest as expense
- 09 Daily Script Master now saves to and loads from the backend (sample rows removed), with Drop to correct a wrong rate. - Purchase Entry takes the rate (NAV for a mutual fund) from the newest Daily Script Master rate on or before the deal date, shows which rate was used, warns when the rate entered is outside the day's low-high, and clears the rate when the script has none. - Broken-period interest paid to the seller is debited to expense GL 500606 instead of Interest Receivable (RBI UCB Investment Directions, para 152, confirmed by Jitendra Sir); Interest Entry labels it so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -275,6 +275,28 @@
|
|||||||
><span>Other Charges (₹)</span><input type="number" [(ngModel)]="purchaseForm.otherCharges" min="0"
|
><span>Other Charges (₹)</span><input type="number" [(ngModel)]="purchaseForm.otherCharges" min="0"
|
||||||
/></label>
|
/></label>
|
||||||
</div>
|
</div>
|
||||||
|
@if (purchaseMarketRate) {
|
||||||
|
<p class="pg-sub" style="padding: 0 16px 8px">
|
||||||
|
Market rate (Daily Script Master) as on {{ purchaseMarketRate.rateDate }}: Close
|
||||||
|
{{ purchaseMarketRate.closeRate | number: '1.2-4' }}
|
||||||
|
@if (purchaseMarketRate.lowRate && purchaseMarketRate.highRate) {
|
||||||
|
(Low {{ purchaseMarketRate.lowRate | number: '1.2-4' }} – High
|
||||||
|
{{ purchaseMarketRate.highRate | number: '1.2-4' }})
|
||||||
|
}
|
||||||
|
@if (purchaseMarketRate.rateDate !== purchaseForm.dealDate) {
|
||||||
|
— no rate entered for the deal date, the latest earlier rate is used.
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
@if (purchaseRateOutOfRange) {
|
||||||
|
<p class="pg-sub" style="padding: 0 16px 8px; color: #b42318; font-weight: 600">
|
||||||
|
Warning: the rate entered is outside the day's market range.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
} @else if (purchaseMarketRate === null) {
|
||||||
|
<p class="pg-sub" style="padding: 0 16px 8px; color: #b42318">
|
||||||
|
No rate in the Daily Script Master for this script — enter the day's rate there first.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- 2. Calculated — filled by the system, read-only -->
|
<!-- 2. Calculated — filled by the system, read-only -->
|
||||||
<div class="pos-section-label">Calculated by System</div>
|
<div class="pos-section-label">Calculated by System</div>
|
||||||
@@ -732,7 +754,7 @@
|
|||||||
><span>Maturity Date</span><input type="text" [value]="sc.maturityDate" readonly
|
><span>Maturity Date</span><input type="text" [value]="sc.maturityDate" readonly
|
||||||
/></label>
|
/></label>
|
||||||
<label class="inv-form-field"
|
<label class="inv-form-field"
|
||||||
><span>Accrued Interest Paid at Purchase</span
|
><span>Broken Period Interest Paid (expense 500606)</span
|
||||||
><input type="text" [value]="'₹' + (sc.purchasedAccruedInterest | number: '1.2-2')" readonly
|
><input type="text" [value]="'₹' + (sc.purchasedAccruedInterest | number: '1.2-2')" readonly
|
||||||
/></label>
|
/></label>
|
||||||
<label class="inv-form-field"
|
<label class="inv-form-field"
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
INVESTMENT_GL_BY_TYPE,
|
INVESTMENT_GL_BY_TYPE,
|
||||||
INVESTMENT_GL_LOOKUP,
|
INVESTMENT_GL_LOOKUP,
|
||||||
INTEREST_RECEIVABLE_GL,
|
INTEREST_RECEIVABLE_GL,
|
||||||
|
BROKEN_PERIOD_INTEREST_GL,
|
||||||
STAMP_DUTY_GL,
|
STAMP_DUTY_GL,
|
||||||
BROKERAGE_GL,
|
BROKERAGE_GL,
|
||||||
OTHER_CHARGES_GL,
|
OTHER_CHARGES_GL,
|
||||||
@@ -54,6 +55,7 @@ import {
|
|||||||
} from '../investment-data';
|
} from '../investment-data';
|
||||||
import { exportWorkbook } from '../investment-export';
|
import { exportWorkbook } from '../investment-export';
|
||||||
import {
|
import {
|
||||||
|
InvestmentDailyRateDto,
|
||||||
InvestmentInterestScheduleDto,
|
InvestmentInterestScheduleDto,
|
||||||
InvestmentPurchaseDto,
|
InvestmentPurchaseDto,
|
||||||
InvestmentService,
|
InvestmentService,
|
||||||
@@ -391,6 +393,7 @@ export class BondInvestmentComponent implements OnInit {
|
|||||||
const type = this.bondType ?? 'GOVT_SECURITY';
|
const type = this.bondType ?? 'GOVT_SECURITY';
|
||||||
this.showMoreDetails = false;
|
this.showMoreDetails = false;
|
||||||
this.retrieveCertNo = '';
|
this.retrieveCertNo = '';
|
||||||
|
this.purchaseMarketRate = undefined;
|
||||||
return {
|
return {
|
||||||
bondType: type,
|
bondType: type,
|
||||||
dealNo: this.nextDealNo(),
|
dealNo: this.nextDealNo(),
|
||||||
@@ -598,6 +601,7 @@ export class BondInvestmentComponent implements OnInit {
|
|||||||
/** Re-suggests the last coupon date when the deal date changes. */
|
/** Re-suggests the last coupon date when the deal date changes. */
|
||||||
onDealDateChange(): void {
|
onDealDateChange(): void {
|
||||||
this.suggestLastIntDate();
|
this.suggestLastIntDate();
|
||||||
|
this.loadMarketRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private suggestLastIntDate(): void {
|
private suggestLastIntDate(): void {
|
||||||
@@ -672,6 +676,46 @@ export class BondInvestmentComponent implements OnInit {
|
|||||||
f.productName = s.scriptName;
|
f.productName = s.scriptName;
|
||||||
}
|
}
|
||||||
this.suggestLastIntDate();
|
this.suggestLastIntDate();
|
||||||
|
this.loadMarketRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Market rate of the script for the deal date from the Daily Script Master (newest rate on or before it). It fills
|
||||||
|
* the purchase rate (NAV for a mutual fund); the dealer may still change it. undefined = not looked up yet,
|
||||||
|
* null = no rate entered for the script.
|
||||||
|
*/
|
||||||
|
purchaseMarketRate: InvestmentDailyRateDto | null | undefined;
|
||||||
|
|
||||||
|
private loadMarketRate(): void {
|
||||||
|
const s = this.purchaseScript;
|
||||||
|
const f = this.purchaseForm;
|
||||||
|
if (!s?.id || !f.dealDate) {
|
||||||
|
this.purchaseMarketRate = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.investmentService.getLatestDailyRate(s.id, f.dealDate).subscribe({
|
||||||
|
next: (rate) => {
|
||||||
|
this.purchaseMarketRate = rate;
|
||||||
|
// No market rate: clear the field rather than leave another script's rate in it.
|
||||||
|
if (this.isMutualFund) {
|
||||||
|
f.nav = rate ? rate.closeRate : null;
|
||||||
|
} else {
|
||||||
|
f.purchaseRate = rate ? rate.closeRate : null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (err) => this.toast(`Could not read the market rate: ${apiErrorMessage(err)}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True when the rate entered is outside the day's low–high in the Daily Script Master. */
|
||||||
|
get purchaseRateOutOfRange(): boolean {
|
||||||
|
const m = this.purchaseMarketRate;
|
||||||
|
const f = this.purchaseForm;
|
||||||
|
const rate = this.isMutualFund ? f.nav : f.purchaseRate;
|
||||||
|
if (!m || rate == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (m.lowRate != null && rate < m.lowRate) || (m.highRate != null && rate > m.highRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Settlement Date = Deal Date + T+n days (greyed / auto in the existing CBS). */
|
/** Settlement Date = Deal Date + T+n days (greyed / auto in the existing CBS). */
|
||||||
@@ -709,11 +753,12 @@ export class BondInvestmentComponent implements OnInit {
|
|||||||
const invGl = this.purchaseInvestmentGlCode;
|
const invGl = this.purchaseInvestmentGlCode;
|
||||||
const lines: AccountingPreviewLine[] = [
|
const lines: AccountingPreviewLine[] = [
|
||||||
{ side: 'Dr', glCode: invGl, glName: this.glNameOf(invGl), amount: book }];
|
{ side: 'Dr', glCode: invGl, glName: this.glNameOf(invGl), amount: book }];
|
||||||
|
// Broken-period interest paid to the seller is an expense (RBI para 152, confirmed by Jitendra Sir).
|
||||||
if (accrued > 0) {
|
if (accrued > 0) {
|
||||||
lines.push({
|
lines.push({
|
||||||
side: 'Dr',
|
side: 'Dr',
|
||||||
glCode: INTEREST_RECEIVABLE_GL.glCode,
|
glCode: BROKEN_PERIOD_INTEREST_GL.glCode,
|
||||||
glName: INTEREST_RECEIVABLE_GL.glName,
|
glName: BROKEN_PERIOD_INTEREST_GL.glName,
|
||||||
amount: accrued
|
amount: accrued
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1509,6 +1509,13 @@ export const INVESTMENT_GL_BY_TYPE: Record<InstrumentType, string> = {
|
|||||||
SHARE: '100706'
|
SHARE: '100706'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Broken-period interest paid to the seller at purchase — an expense, not part of cost (RBI UCB Investment
|
||||||
|
* Directions para 152). */
|
||||||
|
export const BROKEN_PERIOD_INTEREST_GL: InvestmentGlOption = {
|
||||||
|
glCode: '500606',
|
||||||
|
glName: 'BROKEN PERIOD INTEREST PAID ON INVESTMENTS'
|
||||||
|
};
|
||||||
|
|
||||||
/** Charges on an investment deal — expenses on their own GLs, not part of the book value (Jitendra Sir). */
|
/** Charges on an investment deal — expenses on their own GLs, not part of the book value (Jitendra Sir). */
|
||||||
export const STAMP_DUTY_GL: InvestmentGlOption = { glCode: '500601', glName: 'STAMP DUTY ON INVESTMENTS' };
|
export const STAMP_DUTY_GL: InvestmentGlOption = { glCode: '500601', glName: 'STAMP DUTY ON INVESTMENTS' };
|
||||||
export const BROKERAGE_GL: InvestmentGlOption = { glCode: '500602', glName: 'BROKERAGE ON INVESTMENTS' };
|
export const BROKERAGE_GL: InvestmentGlOption = { glCode: '500602', glName: 'BROKERAGE ON INVESTMENTS' };
|
||||||
@@ -2161,13 +2168,15 @@ export const LIEN_REASON_MASTER: LienReason[] = [
|
|||||||
/** Daily Script Master (legacy menu "09") — the day's quoted rate per script, entered/imported
|
/** Daily Script Master (legacy menu "09") — the day's quoted rate per script, entered/imported
|
||||||
* every working day. Distinct from Script Market Detail ("13") which also carries YTM/valuation. */
|
* every working day. Distinct from Script Market Detail ("13") which also carries YTM/valuation. */
|
||||||
export interface DailyScriptRate {
|
export interface DailyScriptRate {
|
||||||
|
/** Backend id once saved. */
|
||||||
|
id?: number;
|
||||||
rateDate: string;
|
rateDate: string;
|
||||||
scriptCode: string;
|
scriptCode: string;
|
||||||
scriptName: string;
|
scriptName: string;
|
||||||
openRate: number;
|
openRate: number | null;
|
||||||
closeRate: number;
|
closeRate: number;
|
||||||
highRate: number;
|
highRate: number | null;
|
||||||
lowRate: number;
|
lowRate: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DAILY_SCRIPT_RATES: DailyScriptRate[] = [
|
export const DAILY_SCRIPT_RATES: DailyScriptRate[] = [
|
||||||
|
|||||||
@@ -225,7 +225,7 @@
|
|||||||
<td>{{ p.city || '—' }}</td>
|
<td>{{ p.city || '—' }}</td>
|
||||||
<td>{{ p.bankType || '—' }}</td>
|
<td>{{ p.bankType || '—' }}</td>
|
||||||
<td>{{ p.glCode ? p.glCode + ' — ' + glName(p.glCode) : '—' }}</td>
|
<td>{{ p.glCode ? p.glCode + ' — ' + glName(p.glCode) : '—' }}</td>
|
||||||
<td class="num">{{ p.limit != null ? '₹' + (p.limit | number: '1.0-0') : '—' }}</td>
|
<td class="num">{{ (p.limit ?? null) !== null ? '₹' + (p.limit | number: '1.0-0') : '—' }}</td>
|
||||||
<td>{{ p.cpBankName ? p.cpBankName + (p.cpIfsc ? ' / ' + p.cpIfsc : '') : '—' }}</td>
|
<td>{{ p.cpBankName ? p.cpBankName + (p.cpIfsc ? ' / ' + p.cpIfsc : '') : '—' }}</td>
|
||||||
<td>{{ p.status ?? 'ACTIVE' }}</td>
|
<td>{{ p.status ?? 'ACTIVE' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -545,10 +545,11 @@
|
|||||||
<th class="num">High</th>
|
<th class="num">High</th>
|
||||||
<th class="num">Low</th>
|
<th class="num">Low</th>
|
||||||
<th class="num">Close</th>
|
<th class="num">Close</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@for (r of dailyScriptRates; track r.rateDate + r.scriptCode) {
|
@for (r of dailyScriptRates; track r.id ?? r.rateDate + r.scriptCode) {
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ r.rateDate }}</td>
|
<td>{{ r.rateDate }}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -559,6 +560,13 @@
|
|||||||
<td class="num">{{ r.highRate | number: '1.2-2' }}</td>
|
<td class="num">{{ r.highRate | number: '1.2-2' }}</td>
|
||||||
<td class="num">{{ r.lowRate | number: '1.2-2' }}</td>
|
<td class="num">{{ r.lowRate | number: '1.2-2' }}</td>
|
||||||
<td class="num">{{ r.closeRate | number: '1.2-2' }}</td>
|
<td class="num">{{ r.closeRate | number: '1.2-2' }}</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="pg-btn pg-btn--ghost" (click)="dropDailyRate(r)">Drop</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
} @empty {
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="pg-empty">No rates entered yet.</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
MUTUAL_FUND_PRODUCTS,
|
MUTUAL_FUND_PRODUCTS,
|
||||||
SGL_BANK_MASTER,
|
SGL_BANK_MASTER,
|
||||||
SCRIPT_MARKET_DETAIL,
|
SCRIPT_MARKET_DETAIL,
|
||||||
DAILY_SCRIPT_RATES,
|
|
||||||
LIEN_REASON_MASTER,
|
LIEN_REASON_MASTER,
|
||||||
ScriptMaster,
|
ScriptMaster,
|
||||||
BrokerMaster,
|
BrokerMaster,
|
||||||
@@ -36,7 +35,7 @@ import {
|
|||||||
periodBetween
|
periodBetween
|
||||||
} from '../investment-data';
|
} from '../investment-data';
|
||||||
import { exportWorkbook, ExportSheetSpec } from '../investment-export';
|
import { exportWorkbook, ExportSheetSpec } from '../investment-export';
|
||||||
import { InvestmentGlAccount, InvestmentService, apiErrorMessage } from '../investment.service';
|
import { InvestmentGlAccount, InvestmentService, apiErrorMessage, toDailyScriptRate } from '../investment.service';
|
||||||
|
|
||||||
type MasterTab =
|
type MasterTab =
|
||||||
| 'party'
|
| 'party'
|
||||||
@@ -79,7 +78,8 @@ export class InvestmentMastersComponent implements OnInit {
|
|||||||
mfProducts: MutualFundProductMaster[] = [...MUTUAL_FUND_PRODUCTS];
|
mfProducts: MutualFundProductMaster[] = [...MUTUAL_FUND_PRODUCTS];
|
||||||
sglBanks: SglBankMaster[] = [...SGL_BANK_MASTER];
|
sglBanks: SglBankMaster[] = [...SGL_BANK_MASTER];
|
||||||
marketDetails: ScriptMarketDetail[] = [...SCRIPT_MARKET_DETAIL];
|
marketDetails: ScriptMarketDetail[] = [...SCRIPT_MARKET_DETAIL];
|
||||||
dailyScriptRates: DailyScriptRate[] = [...DAILY_SCRIPT_RATES];
|
/** Loaded from the backend (Daily Script Master) in ngOnInit. */
|
||||||
|
dailyScriptRates: DailyScriptRate[] = [];
|
||||||
lienReasons: LienReason[] = [...LIEN_REASON_MASTER];
|
lienReasons: LienReason[] = [...LIEN_REASON_MASTER];
|
||||||
|
|
||||||
readonly instrumentTypes: InstrumentType[] = [
|
readonly instrumentTypes: InstrumentType[] = [
|
||||||
@@ -131,6 +131,7 @@ export class InvestmentMastersComponent implements OnInit {
|
|||||||
newDailyRate: Partial<DailyScriptRate> = {};
|
newDailyRate: Partial<DailyScriptRate> = {};
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.loadDailyRates();
|
||||||
this.investmentService.getAssetGlAccounts().subscribe({
|
this.investmentService.getAssetGlAccounts().subscribe({
|
||||||
next: (gls) => (this.investmentGlOptions = gls),
|
next: (gls) => (this.investmentGlOptions = gls),
|
||||||
error: (err) => this.toast(`Could not load GL accounts: ${apiErrorMessage(err)}`)
|
error: (err) => this.toast(`Could not load GL accounts: ${apiErrorMessage(err)}`)
|
||||||
@@ -435,24 +436,56 @@ export class InvestmentMastersComponent implements OnInit {
|
|||||||
this.toast('SGL Bank master added.');
|
this.toast('SGL Bank master added.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadDailyRates(): void {
|
||||||
|
this.investmentService.getDailyRates().subscribe({
|
||||||
|
next: (rows) => (this.dailyScriptRates = rows.map(toDailyScriptRate)),
|
||||||
|
error: (err) => this.toast(`Could not load daily script rates: ${apiErrorMessage(err)}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addDailyRate(): void {
|
addDailyRate(): void {
|
||||||
if (!this.newDailyRate.scriptCode || !this.newDailyRate.rateDate || this.newDailyRate.closeRate == null) {
|
const r = this.newDailyRate;
|
||||||
|
if (!r.scriptCode || !r.rateDate || r.closeRate == null) {
|
||||||
this.toast('Enter Rate Date, Script Code and Close Rate.');
|
this.toast('Enter Rate Date, Script Code and Close Rate.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const script = this.scripts.find((s) => s.scriptCode === this.newDailyRate.scriptCode);
|
const script = this.scripts.find((s) => s.scriptCode === r.scriptCode);
|
||||||
this.dailyScriptRates = [
|
if (!script?.id) {
|
||||||
{
|
this.toast(`Script ${r.scriptCode} is not saved in Script Master.`);
|
||||||
scriptName: script?.scriptName || '',
|
return;
|
||||||
openRate: this.newDailyRate.closeRate,
|
}
|
||||||
highRate: this.newDailyRate.closeRate,
|
const body = {
|
||||||
lowRate: this.newDailyRate.closeRate,
|
scriptId: script.id,
|
||||||
...this.newDailyRate
|
rateDate: r.rateDate,
|
||||||
} as DailyScriptRate,
|
closeRate: r.closeRate,
|
||||||
...this.dailyScriptRates
|
...(r.openRate != null ? { openRate: r.openRate } : {}),
|
||||||
];
|
...(r.highRate != null ? { highRate: r.highRate } : {}),
|
||||||
this.newDailyRate = {};
|
...(r.lowRate != null ? { lowRate: r.lowRate } : {}),
|
||||||
this.toast('Daily script rate added.');
|
dateFormat: 'dd-MM-yyyy',
|
||||||
|
locale: 'en'
|
||||||
|
};
|
||||||
|
this.investmentService.createDailyRate(body).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toast(`Rate for ${r.scriptCode} on ${r.rateDate} saved.`);
|
||||||
|
this.newDailyRate = {};
|
||||||
|
this.loadDailyRates();
|
||||||
|
},
|
||||||
|
error: (err) => this.toast(apiErrorMessage(err))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A wrong rate is corrected by dropping it and entering it again. */
|
||||||
|
dropDailyRate(r: DailyScriptRate): void {
|
||||||
|
if (!r.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.investmentService.deleteDailyRate(r.id).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toast(`Rate for ${r.scriptCode} on ${r.rateDate} dropped.`);
|
||||||
|
this.loadDailyRates();
|
||||||
|
},
|
||||||
|
error: (err) => this.toast(apiErrorMessage(err))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------- Export to Excel --------------------
|
// -------------------- Export to Excel --------------------
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|||||||
import { Observable, forkJoin } from 'rxjs';
|
import { Observable, forkJoin } from 'rxjs';
|
||||||
import { map, tap } from 'rxjs/operators';
|
import { map, tap } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
|
DailyScriptRate,
|
||||||
InstrumentType,
|
InstrumentType,
|
||||||
InvestmentHolding,
|
InvestmentHolding,
|
||||||
PARTY_MASTER,
|
PARTY_MASTER,
|
||||||
@@ -178,6 +179,27 @@ export class InvestmentService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------- Daily Script Master --------------------
|
||||||
|
getDailyRates(scriptId?: number): Observable<InvestmentDailyRateDto[]> {
|
||||||
|
const params = scriptId ? `?scriptId=${scriptId}` : '';
|
||||||
|
return this.http.get<InvestmentDailyRateDto[]>(`/investment/daily-rates${params}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Newest Daily Script Master rate of a script on or before a date; null when none has been entered. */
|
||||||
|
getLatestDailyRate(scriptId: number, onDate: string): Observable<InvestmentDailyRateDto | null> {
|
||||||
|
return this.http.get<InvestmentDailyRateDto | null>(
|
||||||
|
`/investment/daily-rates/latest?scriptId=${scriptId}&onDate=${encodeURIComponent(onDate)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createDailyRate(body: object): Observable<{ resourceId: number }> {
|
||||||
|
return this.http.post<{ resourceId: number }>('/investment/daily-rates', body);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteDailyRate(rateId: number): Observable<unknown> {
|
||||||
|
return this.http.delete(`/investment/daily-rates/${rateId}`);
|
||||||
|
}
|
||||||
|
|
||||||
createPurchase(body: object): Observable<{ resourceId: number; resourceIdentifier: string }> {
|
createPurchase(body: object): Observable<{ resourceId: number; resourceIdentifier: string }> {
|
||||||
return this.http.post<{ resourceId: number; resourceIdentifier: string }>('/investment/purchases', body);
|
return this.http.post<{ resourceId: number; resourceIdentifier: string }>('/investment/purchases', body);
|
||||||
}
|
}
|
||||||
@@ -337,6 +359,33 @@ export interface InvestmentInterestScheduleDto {
|
|||||||
rows: InvestmentInterestRowDto[];
|
rows: InvestmentInterestRowDto[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A Daily Script Master row (see InvestmentDailyRateData in the backend). */
|
||||||
|
export interface InvestmentDailyRateDto {
|
||||||
|
id: number;
|
||||||
|
scriptId: number;
|
||||||
|
scriptCode: string;
|
||||||
|
scriptDesc: string;
|
||||||
|
bondType: string;
|
||||||
|
rateDate: string;
|
||||||
|
openRate: number | null;
|
||||||
|
highRate: number | null;
|
||||||
|
lowRate: number | null;
|
||||||
|
closeRate: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toDailyScriptRate(d: InvestmentDailyRateDto): DailyScriptRate {
|
||||||
|
return {
|
||||||
|
id: d.id,
|
||||||
|
rateDate: d.rateDate,
|
||||||
|
scriptCode: d.scriptCode,
|
||||||
|
scriptName: d.scriptDesc,
|
||||||
|
openRate: d.openRate,
|
||||||
|
closeRate: d.closeRate,
|
||||||
|
highRate: d.highRate,
|
||||||
|
lowRate: d.lowRate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** The first validation / domain-rule message from a Fineract error response, for a toast. */
|
/** The first validation / domain-rule message from a Fineract error response, for a toast. */
|
||||||
export function apiErrorMessage(err: unknown): string {
|
export function apiErrorMessage(err: unknown): string {
|
||||||
const e = err as HttpErrorResponse;
|
const e = err as HttpErrorResponse;
|
||||||
|
|||||||
Reference in New Issue
Block a user