Files
cbsnexora/src/app/tds-reports/tds-remittance-report.component.ts
T

205 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import * as ExcelJS from 'exceljs';
import { BranchScopeService } from '../core/branch-scope.service';
import { FY_TAX_CONFIG, QUARTER_LABEL } from '../letter-mgmt/certificates/form-15gh/tds-15gh.compute';
import { TDS_AS_AT, tdsFys } from './tds-reports-data';
import { Tds26qSummary, TdsRemittanceRow, tds26qSummary, tdsRemittanceRows } from './tds-reports.compute';
@Component({
selector: 'mifosx-tds-remittance-report',
templateUrl: './tds-remittance-report.component.html',
styleUrls: ['./tds-reports.shared.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent,
FormsModule
]
})
export class TdsRemittanceReportComponent {
private scope = inject(BranchScopeService);
/** Filled by the route resolver from Organization › TDS Configuration. */
readonly financialYears = tdsFys();
readonly quarterLabel = QUARTER_LABEL;
readonly asAt = TDS_AS_AT;
fy = tdsFys()[0] ?? '';
exporting = false;
/**
* Provision and return references for the selected year. The quarterly return
* became Form 140 under the 2025 Act, so the labels are read from the config
* rather than written into the template.
*/
get statute(): { section: string; statement: string; frequency: string } {
const cfg = FY_TAX_CONFIG[this.fy];
return {
section: cfg?.interestTdsSection ?? '—',
statement: cfg?.tdsStatementForm ?? '—',
frequency: cfg?.declarationStatementFrequency === 'MONTHLY' ? 'monthly' : 'quarterly'
};
}
get scopeLabel(): string {
return this.scope.isHeadOffice ? 'Bank as a whole' : this.scope.officeName;
}
get rows(): TdsRemittanceRow[] {
return tdsRemittanceRows(this.fy, this.asAt);
}
get summary(): Tds26qSummary {
return tds26qSummary(this.fy, this.asAt);
}
/**
* The control that matters. Tax deducted must equal tax deposited plus tax
* still within its due date. Anything else means something has gone missing
* and must be found before the return is filed.
*/
get reconciled(): boolean {
return this.summary.difference === 0;
}
get totalDefaultInterest(): number {
return this.summary.defaultInterest;
}
trackRow(_: number, row: TdsRemittanceRow): string {
return `${row.fy}-${row.quarter}`;
}
async exportWorkbook(): Promise<void> {
this.exporting = true;
try {
const s = this.summary;
const wb = new ExcelJS.Workbook();
const challan = wb.addWorksheet('Challans');
challan.addRow([`TDS remitted to Government — FY ${this.fy}`]);
challan.addRow([`Scope: ${this.scopeLabel} · as at ${this.asAt}`]);
challan.addRow([]);
challan.addRow([
'Quarter',
'Deduction month',
'Amount deducted',
'Due date',
'Deposited on',
'Challan no.',
'BSR code',
'Status',
'Delay (months)',
'Late-deposit interest',
`${this.statute.statement} due`
]);
for (const r of this.rows) {
challan.addRow([
`Q${r.quarter}`,
r.month,
r.amount,
r.dueDate,
r.depositedOn ?? 'PENDING',
r.challanNo ?? '',
r.bsrCode ?? '',
r.overdue ? 'OVERDUE' : r.late ? 'LATE' : r.depositedOn ? 'On time' : 'Within due date',
r.delayMonths || '',
r.defaultInterest || '',
r.returnDueDate
]);
}
challan.getRow(1).font = { bold: true, size: 13 };
challan.getRow(4).font = { bold: true };
challan.columns.forEach((c) => (c.width = 18));
const recon = wb.addWorksheet('Return Summary');
recon.addRow([`${this.statute.statement} year-end position — FY ${this.fy}`]);
recon.addRow([]);
const lines: [string, number | string][] = [
[
'Deductees',
s.deductees
],
[
'Total interest paid on time deposits',
s.totalInterestPaid
],
[
' — with tax deducted',
s.interestWithDeduction
],
[
' — without deduction (declaration on record)',
s.interestWithoutDeduction
],
[
'TDS at standard rate (PAN on record)',
s.tdsAtStandardRate
],
[
'TDS at higher rate (no PAN)',
s.tdsAtHigherRate
],
[
'Total TDS deducted',
s.totalDeducted
],
[
'Total deposited with Government',
s.totalDeposited
],
[
'Still within due date',
s.pendingDeposit
],
[
'DIFFERENCE (must be zero)',
s.difference
],
[
'Interest payable for late deposit',
s.defaultInterest
],
[
'Challans deposited late',
s.lateChallans
],
[
'Challans overdue',
s.overdueChallans
]
];
for (const line of lines) {
recon.addRow(line);
}
recon.getRow(1).font = { bold: true, size: 13 };
recon.columns.forEach((c) => (c.width = 42));
recon.getColumn(2).width = 20;
const buffer = await wb.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `TDS-Remittance-FY${this.fy}.xlsx`;
a.click();
URL.revokeObjectURL(url);
} finally {
this.exporting = false;
}
}
}