feat: cash-only registers, day book sections and DD amendments
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s
Tests / testRigor (push) Canceled after 0s

This commit is contained in:
2026-09-03 18:26:22 +05:30
parent 6442562df0
commit fbce73fd17
111 changed files with 9076 additions and 3507 deletions
@@ -8,12 +8,12 @@
@if (loading) {
<div class="cert-empty">
<fa-icon icon="spinner" [spin]="true" size="2x" style="color: #7c3aed; margin-bottom: 12px;"></fa-icon>
<fa-icon icon="spinner" [spin]="true" size="2x" style="color: #7c3aed; margin-bottom: 12px"></fa-icon>
<div>Loading live declarations and accounts from Fineract…</div>
</div>
} @else if (loadError) {
<div class="cert-empty" style="color: #ef4444;">
<fa-icon icon="exclamation-circle" size="2x" style="margin-bottom: 12px;"></fa-icon>
<div class="cert-empty" style="color: #ef4444">
<fa-icon icon="exclamation-circle" size="2x" style="margin-bottom: 12px"></fa-icon>
<div>Could not load real data from Fineract. Check connection.</div>
</div>
} @else {
@@ -139,7 +139,9 @@
<tr [class.cert-row--warn]="!row.row.eligibility.eligible">
<td>
<div class="cert-cust-name">{{ row.row.record.customerName }}</div>
<div class="mono small muted">{{ row.row.record.customerId }} · PAN {{ row.row.record.customerPan || '—' }}</div>
<div class="mono small muted">
{{ row.row.record.customerId }} · PAN {{ row.row.record.customerPan || '—' }}
</div>
<div class="small muted">DOB {{ row.row.record.dob | date: 'd MMM y' }}</div>
</td>
<td>
@@ -306,8 +308,8 @@
@if (row.row.schedule.shieldedInterest > 0) {
<div class="small muted">
₹{{ row.row.schedule.shieldedInterest | number: '1.0-0' }} paid without deduction on the strength of
the declaration — reportable in the quarterly {{ statute.statement }} return.
₹{{ row.row.schedule.shieldedInterest | number: '1.0-0' }} paid without deduction on the strength
of the declaration — reportable in the quarterly {{ statute.statement }} return.
</div>
}
</td>
@@ -64,7 +64,11 @@ export interface RealForm15ghRow {
/** Fineract hands dates back as [yyyy, m, d]; normalise to ISO. */
function toIso(value: unknown): string | null {
if (Array.isArray(value) && value.length >= 3) {
const [y, m, d] = value as number[];
const [
y,
m,
d
] = value as number[];
return `${y}-${`${m}`.padStart(2, '0')}-${`${d}`.padStart(2, '0')}`;
}
if (typeof value === 'string' && value.trim()) {
@@ -118,7 +122,10 @@ export class RealForm15ghViewComponent implements OnInit, OnDestroy {
private clientKycCache = new Map<string, Observable<any>>();
private clientAccountsCache = new Map<string, Observable<any>>();
readonly availableFys = ['FY 2026-27', 'FY 2025-26'];
readonly availableFys = [
'FY 2026-27',
'FY 2025-26'
];
ngOnInit(): void {
this.loadRealData();
@@ -311,9 +318,11 @@ export class RealForm15ghViewComponent implements OnInit, OnDestroy {
depositPrincipal,
fy: this.selectedFy,
status,
filedOn: hasDeclaration ? toIso(kyc?.submittedOnDate) ?? `${fy.slice(0, 4)}-04-01` : undefined,
filedOn: hasDeclaration ? (toIso(kyc?.submittedOnDate) ?? `${fy.slice(0, 4)}-04-01`) : undefined,
assessed: true,
interestPostingPeriodType: detailsList.find(d => d.depositType?.code?.includes('fixedDeposit'))?.interestPostingPeriodType?.code || 'savingsPostingInterestPeriodType.quarterly'
interestPostingPeriodType:
detailsList.find((d) => d.depositType?.code?.includes('fixedDeposit'))?.interestPostingPeriodType?.code ||
'savingsPostingInterestPeriodType.quarterly'
};
}
@@ -226,7 +226,11 @@ export function uinTaxYearPart(fy: string): string {
*/
export function buildUin(sequence: number, fy: string, tan: string): string {
const digits = `${Math.max(0, Math.floor(sequence))}`.slice(-9).padStart(9, '0');
const normalisedTan = (tan || '').toUpperCase().replace(/[^A-Z0-9]/g, '').padEnd(10, 'X').slice(0, 10);
const normalisedTan = (tan || '')
.toUpperCase()
.replace(/[^A-Z0-9]/g, '')
.padEnd(10, 'X')
.slice(0, 10);
return `D${digits}${uinTaxYearPart(fy)}${normalisedTan}`;
}
@@ -12,9 +12,7 @@
<fa-icon icon="book" class="cert-head-ic"></fa-icon>
<div>
<h2 class="cert-head-t">Membership Register</h2>
<div class="cert-head-s">
Statutory register of members — one line per member, in order of admission.
</div>
<div class="cert-head-s">Statutory register of members — one line per member, in order of admission.</div>
</div>
</div>
<div class="cert-head-r">
@@ -98,9 +96,7 @@
<input type="date" [(ngModel)]="toDate" />
</div>
<button class="cert-clear" (click)="clearFilters()">
<fa-icon icon="sync"></fa-icon> Clear
</button>
<button class="cert-clear" (click)="clearFilters()"><fa-icon icon="sync"></fa-icon> Clear</button>
</div>
<!-- Register -->
@@ -109,9 +109,7 @@
<input type="date" [(ngModel)]="toDate" />
</div>
<button class="cert-clear" (click)="clearFilters()">
<fa-icon icon="sync"></fa-icon> Clear
</button>
<button class="cert-clear" (click)="clearFilters()"><fa-icon icon="sync"></fa-icon> Clear</button>
</div>
<!-- Report -->
@@ -191,7 +189,9 @@
<span class="cert-pill" [class]="'sar-pill--' + row.status.toLowerCase()">{{ row.statusLabel }}</span>
</td>
<td>
<span class="cert-pill" [class]="'sar-pay--' + row.status.toLowerCase()">{{ row.paymentStatus }}</span>
<span class="cert-pill" [class]="'sar-pay--' + row.status.toLowerCase()">{{
row.paymentStatus
}}</span>
</td>
<td class="cert-row-actions">
<fa-icon icon="chevron-right" class="sar-chevron"></fa-icon>
@@ -105,9 +105,7 @@ export class ShareAllotmentReportComponent implements OnInit {
if (!items.length) {
return of([] as AllotmentRow[]);
}
return forkJoin(items.map((item) => this.buildAllotments(item))).pipe(
map((perAccount) => perAccount.flat())
);
return forkJoin(items.map((item) => this.buildAllotments(item))).pipe(map((perAccount) => perAccount.flat()));
}),
catchError(() => {
this.loadError = true;
@@ -100,9 +100,7 @@
</select>
</div>
<button class="cert-clear" (click)="clearFilters()">
<fa-icon icon="sync"></fa-icon> Clear
</button>
<button class="cert-clear" (click)="clearFilters()"><fa-icon icon="sync"></fa-icon> Clear</button>
</div>
<!-- Register -->