feat: add 11 banking compliance & accounting report modules
Adds dialog components and feature modules for RBI/banking compliance, KYC, and accounting reports. Wires them into the shell menu (RBI, General Ledger, CKYC Mgmt, Others, Insurance) and dashboard cards. Dialog components (lazy-loaded MatDialog): - PSL Returns (Form III) — dashboard card + RBI menu - ALM Statement of Structural Liquidity (Form ALM-1) — RBI menu - CIMS / XBRL Returns (OSS-2 / PSA / RAL / RCP-UCB) — RBI menu, shared dialog with form-pill switcher - CIC DQR (CIBIL / Experian / Equifax / CRIF, 72-col TUDF) — Others menu + dashboard card - CKYC Compliance Reports (Re-KYC / Risk Review / CERSAI) — CKYC Mgmt - Accounting Reports (P&L / Balance Sheet / Comparative B/S / GL DR/CR / Misc Expense Ledger) — General Ledger menu Feature modules (Route.withShell): - DEAF (/deaf): Form I, Form II, Annex 4, accounts register - DICGC (/dicgc): Slab Report, SAHAY Depositor List - FIU-IND (/fiu): FINnet 2.0 Statement of Financial Transactions - SFT (/sft): Income Tax Section 285BA SFT - CKYC Reports (/ckyc/ckyc-reports): standalone dashboard UI: - Theme-colored icons on 22 wired menu links across 5 menus — visually distinguishes wired (colored) from placeholder (gray) links. - Backdrop classes per dialog in main.scss. All using dummy data (Bharat Urban Bank Ltd, Karthik Kamath as Principal Officer, dummy customer names) — no real bank or customer references. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
634003a75c
commit
12bfc81219
@@ -0,0 +1,654 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SFT — Statement of Financial Transactions, filed under
|
||||
* Section 285BA of the Income Tax Act 1961 + Rule 114E,
|
||||
* with the Income Tax Reporting Portal (report.insight.gov.in)
|
||||
* by 31 May for the previous FY.
|
||||
*
|
||||
* Schema reproduces the actual SFT CSV templates; bank/customer names are dummy.
|
||||
*
|
||||
* SFT-001 Cash purchase of bank drafts / pay orders / banker's cheques (≥ ₹10 L cash)
|
||||
* SFT-002 Time-deposit (FD/RD) opening (≥ ₹10 L cash)
|
||||
* SFT-003 Cash deposits / withdrawals — current account (≥ ₹50 L per FY)
|
||||
* SFT-004 Cash deposits — savings account (≥ ₹10 L per FY)
|
||||
* SFT-015 Dividend payments
|
||||
* SFT-016 Interest paid on deposits
|
||||
*/
|
||||
|
||||
export type SftCode = 'SFT-001' | 'SFT-002' | 'SFT-003' | 'SFT-004' | 'SFT-015' | 'SFT-016';
|
||||
|
||||
/** Person-wise schema (SFT-001 / 002 / 004) */
|
||||
export interface SftPersonRecord {
|
||||
reportSerialNumber: number;
|
||||
originalReportSerialNumber: number;
|
||||
personName: string;
|
||||
personType: string; // I=Individual, F=Firm, P=Partnership, etc.
|
||||
customerIdentity: string;
|
||||
gender: 'M' | 'F' | 'O' | '';
|
||||
fatherName: string;
|
||||
pan: string;
|
||||
aadhaar: string;
|
||||
form60Ack: string;
|
||||
identificationType: string;
|
||||
identificationNumber: string;
|
||||
dobOrIncorporation: string;
|
||||
nationality: string;
|
||||
addressType: string;
|
||||
fixAddress: string;
|
||||
city: string;
|
||||
postCode: string;
|
||||
state: string;
|
||||
countryCode: string;
|
||||
businessOrOccupation: string;
|
||||
primaryStdCode: string;
|
||||
primaryPhoneNo: string;
|
||||
primaryMobileNo: string;
|
||||
email: string;
|
||||
remarks: string;
|
||||
productType: string; // SFT-001/002/004 — DD / Pay Order / Time Deposit / SB
|
||||
aggGrossReceived: number;
|
||||
aggGrossReceivedCash: number;
|
||||
aggGrossPaid: number;
|
||||
productIdentifier: string;
|
||||
lastDateOfTransaction: string;
|
||||
amountReceivedFromPerson: number;
|
||||
cashAmountReceivedFromPerson: number;
|
||||
amountPaidToPerson: number;
|
||||
relatedAccountNumber: string;
|
||||
relatedInstituteName: string;
|
||||
relatedInstituteRefNumber: string;
|
||||
/** Which SFT code this row belongs to */
|
||||
sftCode: SftCode;
|
||||
}
|
||||
|
||||
/** Account-wise schema with embedded Person Card (SFT-003) */
|
||||
export interface SftAccountRecord {
|
||||
reportSerialNumber: number;
|
||||
originalReportSerialNumber: number;
|
||||
accountType: string;
|
||||
accountNumber: string;
|
||||
accountHolderName: string;
|
||||
accountStatus: 'A' | 'C' | 'D';
|
||||
branchReferenceNumber: string;
|
||||
branchName: string;
|
||||
branchAddress: string;
|
||||
branchCity: string;
|
||||
branchPostalCode: string;
|
||||
branchState: string;
|
||||
branchCountry: string;
|
||||
branchStdCode: string;
|
||||
branchPhone: string;
|
||||
branchMobile: string;
|
||||
branchFaxStd: string;
|
||||
branchFaxPhone: string;
|
||||
branchEmail: string;
|
||||
aggGrossCashCredited: number;
|
||||
aggGrossCashDebited: number;
|
||||
amountCreditedBeforeDemonetization: number;
|
||||
amountCreditedAfterDemonetization: number;
|
||||
// Person Card (PC)
|
||||
pcAccountRelationship: string;
|
||||
pcPersonName: string;
|
||||
pcPersonType: string;
|
||||
pcCustomerIdentity: string;
|
||||
pcGender: 'M' | 'F' | 'O' | '';
|
||||
pcFatherName: string;
|
||||
pcPan: string;
|
||||
pcAadhaar: string;
|
||||
pcForm60Ack: string;
|
||||
pcIdentificationType: string;
|
||||
pcIdentificationNumber: string;
|
||||
pcDob: string;
|
||||
pcNationality: string;
|
||||
pcBusiness: string;
|
||||
pcAddressType: string;
|
||||
pcAddress: string;
|
||||
pcCity: string;
|
||||
pcPin: string;
|
||||
pcState: string;
|
||||
pcCountry: string;
|
||||
pcPrimaryStd: string;
|
||||
pcPrimaryPhone: string;
|
||||
pcPrimaryMobile: string;
|
||||
pcEmail: string;
|
||||
}
|
||||
|
||||
/** Compact pipe-delimited schema (SFT-015 dividend / SFT-016 interest) */
|
||||
export interface SftInterestDividendRecord {
|
||||
rsn: number;
|
||||
fy: string;
|
||||
name: string;
|
||||
pan: string;
|
||||
aadhaar: string;
|
||||
mobile: string;
|
||||
email: string;
|
||||
/** Address only present in SFT-015 */
|
||||
address?: string;
|
||||
pincode?: string;
|
||||
/** Account fields only present in SFT-016 */
|
||||
accountNumber?: string;
|
||||
typeOfAccount?: string;
|
||||
/** SFT-015 = dividend amount; SFT-016 = interest paid */
|
||||
amount: number;
|
||||
sftCode: 'SFT-015' | 'SFT-016';
|
||||
}
|
||||
|
||||
export const SFT_BANK = {
|
||||
name: 'Bharat Urban Bank Ltd',
|
||||
branch: 'Head Office',
|
||||
filerPan: 'AABCB1100M',
|
||||
filerTan: 'MUMR99001E',
|
||||
itdReportingPortalId: 'BHARATURBAN-285BA',
|
||||
fy: '2025-26',
|
||||
filingDueDate: '31-05-2026',
|
||||
principalOfficer: 'Karthik Kamath',
|
||||
poDesignation: 'Chief Compliance Officer',
|
||||
branchRef: 'YESB0BHRT',
|
||||
branchAddress: '12 MG Road, Worli',
|
||||
branchCity: 'MUMBAI',
|
||||
branchState: 'MH',
|
||||
branchPostalCode: '400018',
|
||||
branchCountry: 'IN',
|
||||
branchStdCode: '022',
|
||||
branchPhone: '24938000',
|
||||
branchMobile: '9892000100',
|
||||
branchEmail: 'compliance@bharaturbanbank.example'
|
||||
};
|
||||
|
||||
/** ── SFT-001 / 002 / 004 — Person-wise records ── */
|
||||
export const SFT_PERSON_RECORDS: SftPersonRecord[] = [
|
||||
// SFT-001 — Cash purchase of DD ≥ ₹10 L
|
||||
{
|
||||
reportSerialNumber: 1,
|
||||
originalReportSerialNumber: 0,
|
||||
personName: 'RAVI KUMAR SHARMA',
|
||||
personType: 'I',
|
||||
customerIdentity: '10078',
|
||||
gender: 'M',
|
||||
fatherName: 'RAJESH KUMAR SHARMA',
|
||||
pan: 'AAQPS8560J',
|
||||
aadhaar: '',
|
||||
form60Ack: '',
|
||||
identificationType: 'C',
|
||||
identificationNumber: 'AAQPS8560J',
|
||||
dobOrIncorporation: '10-08-1985',
|
||||
nationality: 'IN',
|
||||
addressType: '2',
|
||||
fixAddress: 'FLAT 402 SUNRISE TOWERS MAROL ANDHERI EAST',
|
||||
city: 'MUMBAI',
|
||||
postCode: '400069',
|
||||
state: 'MH',
|
||||
countryCode: 'IN',
|
||||
businessOrOccupation: 'SERVICE',
|
||||
primaryStdCode: '',
|
||||
primaryPhoneNo: '',
|
||||
primaryMobileNo: '9892011001',
|
||||
email: 'ravi.sharma@example.com',
|
||||
remarks: '',
|
||||
productType: 'BANK_DRAFT',
|
||||
aggGrossReceived: 1500000,
|
||||
aggGrossReceivedCash: 1500000,
|
||||
aggGrossPaid: 0,
|
||||
productIdentifier: 'DD24304',
|
||||
lastDateOfTransaction: '20-01-2026',
|
||||
amountReceivedFromPerson: 1500000,
|
||||
cashAmountReceivedFromPerson: 1500000,
|
||||
amountPaidToPerson: 0,
|
||||
relatedAccountNumber: '110001101000838',
|
||||
relatedInstituteName: 'BHARAT URBAN BANK',
|
||||
relatedInstituteRefNumber: 'YESB0BHRT',
|
||||
sftCode: 'SFT-001'
|
||||
},
|
||||
{
|
||||
reportSerialNumber: 2,
|
||||
originalReportSerialNumber: 0,
|
||||
personName: 'PRIYA PATEL',
|
||||
personType: 'I',
|
||||
customerIdentity: '10079',
|
||||
gender: 'F',
|
||||
fatherName: 'HARISH PATEL',
|
||||
pan: 'AKLPS9933P',
|
||||
aadhaar: '',
|
||||
form60Ack: '',
|
||||
identificationType: 'C',
|
||||
identificationNumber: 'AKLPS9933P',
|
||||
dobOrIncorporation: '22-03-1990',
|
||||
nationality: 'IN',
|
||||
addressType: '2',
|
||||
fixAddress: 'B-12 SHIVAJI NAGAR FC ROAD',
|
||||
city: 'PUNE',
|
||||
postCode: '411005',
|
||||
state: 'MH',
|
||||
countryCode: 'IN',
|
||||
businessOrOccupation: 'BUSINESS',
|
||||
primaryStdCode: '',
|
||||
primaryPhoneNo: '',
|
||||
primaryMobileNo: '9822055002',
|
||||
email: 'priya.patel@example.com',
|
||||
remarks: '',
|
||||
productType: 'BANKERS_CHEQUE',
|
||||
aggGrossReceived: 1100000,
|
||||
aggGrossReceivedCash: 1100000,
|
||||
aggGrossPaid: 0,
|
||||
productIdentifier: 'BC100039',
|
||||
lastDateOfTransaction: '02-01-2026',
|
||||
amountReceivedFromPerson: 1100000,
|
||||
cashAmountReceivedFromPerson: 1100000,
|
||||
amountPaidToPerson: 0,
|
||||
relatedAccountNumber: '110001352000357',
|
||||
relatedInstituteName: 'BHARAT URBAN BANK',
|
||||
relatedInstituteRefNumber: 'YESB0BHRT',
|
||||
sftCode: 'SFT-001'
|
||||
},
|
||||
// SFT-002 — Time deposit ≥ ₹10 L cash
|
||||
{
|
||||
reportSerialNumber: 3,
|
||||
originalReportSerialNumber: 0,
|
||||
personName: 'VIKRAM REDDY',
|
||||
personType: 'I',
|
||||
customerIdentity: '20101',
|
||||
gender: 'M',
|
||||
fatherName: 'SURESH REDDY',
|
||||
pan: 'AAEPR9352L',
|
||||
aadhaar: '',
|
||||
form60Ack: '',
|
||||
identificationType: 'C',
|
||||
identificationNumber: 'AAEPR9352L',
|
||||
dobOrIncorporation: '07-07-1982',
|
||||
nationality: 'IN',
|
||||
addressType: '2',
|
||||
fixAddress: 'NO 23 4TH CROSS JAYANAGAR 5TH BLOCK',
|
||||
city: 'BENGALURU',
|
||||
postCode: '560041',
|
||||
state: 'KA',
|
||||
countryCode: 'IN',
|
||||
businessOrOccupation: 'PROFESSION',
|
||||
primaryStdCode: '',
|
||||
primaryPhoneNo: '',
|
||||
primaryMobileNo: '9845022004',
|
||||
email: '',
|
||||
remarks: '',
|
||||
productType: 'TIME_DEPOSIT',
|
||||
aggGrossReceived: 2500000,
|
||||
aggGrossReceivedCash: 2500000,
|
||||
aggGrossPaid: 0,
|
||||
productIdentifier: 'FDR-2026-001',
|
||||
lastDateOfTransaction: '15-09-2025',
|
||||
amountReceivedFromPerson: 2500000,
|
||||
cashAmountReceivedFromPerson: 2500000,
|
||||
amountPaidToPerson: 0,
|
||||
relatedAccountNumber: '110001352000420',
|
||||
relatedInstituteName: 'BHARAT URBAN BANK',
|
||||
relatedInstituteRefNumber: 'YESB0BHRT',
|
||||
sftCode: 'SFT-002'
|
||||
},
|
||||
{
|
||||
reportSerialNumber: 4,
|
||||
originalReportSerialNumber: 0,
|
||||
personName: 'NIRAV SHAH',
|
||||
personType: 'I',
|
||||
customerIdentity: '40301',
|
||||
gender: 'M',
|
||||
fatherName: 'JAYESH SHAH',
|
||||
pan: 'AHKPS5544Z',
|
||||
aadhaar: '',
|
||||
form60Ack: '',
|
||||
identificationType: 'C',
|
||||
identificationNumber: 'AHKPS5544Z',
|
||||
dobOrIncorporation: '18-05-1992',
|
||||
nationality: 'IN',
|
||||
addressType: '2',
|
||||
fixAddress: 'A-401 ATLANTIS APARTMENTS SATELLITE ROAD',
|
||||
city: 'AHMEDABAD',
|
||||
postCode: '380015',
|
||||
state: 'GJ',
|
||||
countryCode: 'IN',
|
||||
businessOrOccupation: 'SERVICE',
|
||||
primaryStdCode: '',
|
||||
primaryPhoneNo: '',
|
||||
primaryMobileNo: '9824012010',
|
||||
email: 'nirav.shah@example.com',
|
||||
remarks: '',
|
||||
productType: 'TIME_DEPOSIT',
|
||||
aggGrossReceived: 1800000,
|
||||
aggGrossReceivedCash: 1800000,
|
||||
aggGrossPaid: 0,
|
||||
productIdentifier: 'FDR-2026-002',
|
||||
lastDateOfTransaction: '12-12-2025',
|
||||
amountReceivedFromPerson: 1800000,
|
||||
cashAmountReceivedFromPerson: 1800000,
|
||||
amountPaidToPerson: 0,
|
||||
relatedAccountNumber: '110001352000511',
|
||||
relatedInstituteName: 'BHARAT URBAN BANK',
|
||||
relatedInstituteRefNumber: 'YESB0BHRT',
|
||||
sftCode: 'SFT-002'
|
||||
},
|
||||
// SFT-004 — Cash deposits in SB ≥ ₹10 L per FY
|
||||
{
|
||||
reportSerialNumber: 5,
|
||||
originalReportSerialNumber: 0,
|
||||
personName: 'KAVITA VERMA',
|
||||
personType: 'I',
|
||||
customerIdentity: '50402',
|
||||
gender: 'F',
|
||||
fatherName: 'DEEPAK VERMA',
|
||||
pan: 'AKHPV2244R',
|
||||
aadhaar: '',
|
||||
form60Ack: '',
|
||||
identificationType: 'C',
|
||||
identificationNumber: 'AKHPV2244R',
|
||||
dobOrIncorporation: '01-04-1988',
|
||||
nationality: 'IN',
|
||||
addressType: '2',
|
||||
fixAddress: '45 CIVIL LINES MALL ROAD',
|
||||
city: 'KANPUR',
|
||||
postCode: '208001',
|
||||
state: 'UP',
|
||||
countryCode: 'IN',
|
||||
businessOrOccupation: 'SERVICE',
|
||||
primaryStdCode: '',
|
||||
primaryPhoneNo: '',
|
||||
primaryMobileNo: '9415055014',
|
||||
email: '',
|
||||
remarks: '',
|
||||
productType: 'SAVINGS_DEPOSIT',
|
||||
aggGrossReceived: 1250000,
|
||||
aggGrossReceivedCash: 1250000,
|
||||
aggGrossPaid: 0,
|
||||
productIdentifier: 'SB-50402',
|
||||
lastDateOfTransaction: '28-01-2026',
|
||||
amountReceivedFromPerson: 1250000,
|
||||
cashAmountReceivedFromPerson: 1250000,
|
||||
amountPaidToPerson: 0,
|
||||
relatedAccountNumber: '110001101001145',
|
||||
relatedInstituteName: 'BHARAT URBAN BANK',
|
||||
relatedInstituteRefNumber: 'YESB0BHRT',
|
||||
sftCode: 'SFT-004'
|
||||
},
|
||||
{
|
||||
reportSerialNumber: 6,
|
||||
originalReportSerialNumber: 0,
|
||||
personName: 'SAMEER KAPOOR',
|
||||
personType: 'I',
|
||||
customerIdentity: '70601',
|
||||
gender: 'M',
|
||||
fatherName: 'RAJINDER KAPOOR',
|
||||
pan: 'AHKPK7788Q',
|
||||
aadhaar: '',
|
||||
form60Ack: '',
|
||||
identificationType: 'C',
|
||||
identificationNumber: 'AHKPK7788Q',
|
||||
dobOrIncorporation: '03-02-1987',
|
||||
nationality: 'IN',
|
||||
addressType: '2',
|
||||
fixAddress: 'B-148 GREATER KAILASH GK PART 1',
|
||||
city: 'NEW DELHI',
|
||||
postCode: '110048',
|
||||
state: 'DL',
|
||||
countryCode: 'IN',
|
||||
businessOrOccupation: 'SERVICE',
|
||||
primaryStdCode: '',
|
||||
primaryPhoneNo: '',
|
||||
primaryMobileNo: '9811022019',
|
||||
email: 'sameer.kapoor@example.com',
|
||||
remarks: '',
|
||||
productType: 'SAVINGS_DEPOSIT',
|
||||
aggGrossReceived: 2150000,
|
||||
aggGrossReceivedCash: 2150000,
|
||||
aggGrossPaid: 0,
|
||||
productIdentifier: 'SB-70601',
|
||||
lastDateOfTransaction: '15-01-2026',
|
||||
amountReceivedFromPerson: 2150000,
|
||||
cashAmountReceivedFromPerson: 2150000,
|
||||
amountPaidToPerson: 0,
|
||||
relatedAccountNumber: '110001101001458',
|
||||
relatedInstituteName: 'BHARAT URBAN BANK',
|
||||
relatedInstituteRefNumber: 'YESB0BHRT',
|
||||
sftCode: 'SFT-004'
|
||||
}
|
||||
];
|
||||
|
||||
/** ── SFT-003 — Account-wise (current account) cash deposits ≥ ₹50 L per FY ── */
|
||||
export const SFT_ACCOUNT_RECORDS: SftAccountRecord[] = [
|
||||
{
|
||||
reportSerialNumber: 1,
|
||||
originalReportSerialNumber: 0,
|
||||
accountType: 'BC',
|
||||
accountNumber: '110001101000685',
|
||||
accountHolderName: 'NEXTGEN TECH PVT LTD',
|
||||
accountStatus: 'A',
|
||||
branchReferenceNumber: 'YESB0BHRT',
|
||||
branchName: 'HEAD OFFICE',
|
||||
branchAddress: '12 MG ROAD WORLI MUMBAI 400018',
|
||||
branchCity: 'MUMBAI',
|
||||
branchPostalCode: '400018',
|
||||
branchState: 'MH',
|
||||
branchCountry: 'IN',
|
||||
branchStdCode: '022',
|
||||
branchPhone: '24938000',
|
||||
branchMobile: '9892000100',
|
||||
branchFaxStd: '022',
|
||||
branchFaxPhone: '24938011',
|
||||
branchEmail: 'compliance@bharaturbanbank.example',
|
||||
aggGrossCashCredited: 6500000,
|
||||
aggGrossCashDebited: 4200000,
|
||||
amountCreditedBeforeDemonetization: 0,
|
||||
amountCreditedAfterDemonetization: 0,
|
||||
pcAccountRelationship: 'F',
|
||||
pcPersonName: 'NEXTGEN TECH PVT LTD',
|
||||
pcPersonType: 'PF',
|
||||
pcCustomerIdentity: 'E10102',
|
||||
pcGender: '',
|
||||
pcFatherName: '',
|
||||
pcPan: 'AABCN5566K',
|
||||
pcAadhaar: '',
|
||||
pcForm60Ack: '',
|
||||
pcIdentificationType: 'C',
|
||||
pcIdentificationNumber: 'AABCN5566K',
|
||||
pcDob: '18-09-2015',
|
||||
pcNationality: 'IN',
|
||||
pcBusiness: 'IT_SERVICES',
|
||||
pcAddressType: '2',
|
||||
pcAddress: 'TOWER B SUNRISE BUSINESS PARK ANDHERI EAST',
|
||||
pcCity: 'MUMBAI',
|
||||
pcPin: '400093',
|
||||
pcState: 'MH',
|
||||
pcCountry: 'IN',
|
||||
pcPrimaryStd: '',
|
||||
pcPrimaryPhone: '',
|
||||
pcPrimaryMobile: '9824012011',
|
||||
pcEmail: ''
|
||||
},
|
||||
{
|
||||
reportSerialNumber: 2,
|
||||
originalReportSerialNumber: 0,
|
||||
accountType: 'BC',
|
||||
accountNumber: '110001101000922',
|
||||
accountHolderName: 'BHARAT TRADING COMPANY (PROP. RAVI KUMAR SHARMA)',
|
||||
accountStatus: 'A',
|
||||
branchReferenceNumber: 'YESB0BHRT',
|
||||
branchName: 'HEAD OFFICE',
|
||||
branchAddress: '12 MG ROAD WORLI MUMBAI 400018',
|
||||
branchCity: 'MUMBAI',
|
||||
branchPostalCode: '400018',
|
||||
branchState: 'MH',
|
||||
branchCountry: 'IN',
|
||||
branchStdCode: '022',
|
||||
branchPhone: '24938000',
|
||||
branchMobile: '9892000100',
|
||||
branchFaxStd: '022',
|
||||
branchFaxPhone: '24938011',
|
||||
branchEmail: 'compliance@bharaturbanbank.example',
|
||||
aggGrossCashCredited: 5800000,
|
||||
aggGrossCashDebited: 3450000,
|
||||
amountCreditedBeforeDemonetization: 0,
|
||||
amountCreditedAfterDemonetization: 0,
|
||||
pcAccountRelationship: 'F',
|
||||
pcPersonName: 'BHARAT TRADING COMPANY',
|
||||
pcPersonType: 'PF',
|
||||
pcCustomerIdentity: 'E10101',
|
||||
pcGender: '',
|
||||
pcFatherName: '',
|
||||
pcPan: 'ABCFB1234N',
|
||||
pcAadhaar: '',
|
||||
pcForm60Ack: '',
|
||||
pcIdentificationType: 'C',
|
||||
pcIdentificationNumber: 'ABCFB1234N',
|
||||
pcDob: '24-03-2008',
|
||||
pcNationality: 'IN',
|
||||
pcBusiness: 'TRADING',
|
||||
pcAddressType: '2',
|
||||
pcAddress: 'GALA 22 LOWER PAREL',
|
||||
pcCity: 'MUMBAI',
|
||||
pcPin: '400013',
|
||||
pcState: 'MH',
|
||||
pcCountry: 'IN',
|
||||
pcPrimaryStd: '',
|
||||
pcPrimaryPhone: '',
|
||||
pcPrimaryMobile: '9892011010',
|
||||
pcEmail: ''
|
||||
}
|
||||
];
|
||||
|
||||
/** ── SFT-015 / SFT-016 — Pipe-delimited interest & dividend ── */
|
||||
export const SFT_INT_DIV_RECORDS: SftInterestDividendRecord[] = [
|
||||
// SFT-016 — Interest paid to depositors
|
||||
{
|
||||
rsn: 1,
|
||||
fy: '2025-26',
|
||||
name: 'RAVI KUMAR SHARMA',
|
||||
pan: 'AAQPS8560J',
|
||||
aadhaar: 'XXXXXXXX1234',
|
||||
mobile: '9892011001',
|
||||
email: 'ravi.sharma@example.com',
|
||||
accountNumber: '110001302000312',
|
||||
typeOfAccount: 'FD',
|
||||
amount: 56250,
|
||||
sftCode: 'SFT-016'
|
||||
},
|
||||
{
|
||||
rsn: 2,
|
||||
fy: '2025-26',
|
||||
name: 'PRIYA PATEL',
|
||||
pan: 'AKLPS9933P',
|
||||
aadhaar: 'XXXXXXXX2210',
|
||||
mobile: '9822055002',
|
||||
email: 'priya.patel@example.com',
|
||||
accountNumber: '110001302000418',
|
||||
typeOfAccount: 'FD',
|
||||
amount: 41280,
|
||||
sftCode: 'SFT-016'
|
||||
},
|
||||
{
|
||||
rsn: 3,
|
||||
fy: '2025-26',
|
||||
name: 'VIKRAM REDDY',
|
||||
pan: 'AAEPR9352L',
|
||||
aadhaar: 'XXXXXXXX6622',
|
||||
mobile: '9845022004',
|
||||
email: '',
|
||||
accountNumber: '110001302000711',
|
||||
typeOfAccount: 'FD',
|
||||
amount: 73500,
|
||||
sftCode: 'SFT-016'
|
||||
},
|
||||
{
|
||||
rsn: 4,
|
||||
fy: '2025-26',
|
||||
name: 'LAKSHMI IYER',
|
||||
pan: '',
|
||||
aadhaar: 'XXXXXXXX5511',
|
||||
mobile: '9845066005',
|
||||
email: '',
|
||||
accountNumber: '110001301000814',
|
||||
typeOfAccount: 'SB',
|
||||
amount: 18420,
|
||||
sftCode: 'SFT-016'
|
||||
},
|
||||
{
|
||||
rsn: 5,
|
||||
fy: '2025-26',
|
||||
name: 'NIRAV SHAH',
|
||||
pan: 'AHKPS5544Z',
|
||||
aadhaar: 'XXXXXXXX9933',
|
||||
mobile: '9824012010',
|
||||
email: 'nirav.shah@example.com',
|
||||
accountNumber: '110001352000511',
|
||||
typeOfAccount: 'FD',
|
||||
amount: 124200,
|
||||
sftCode: 'SFT-016'
|
||||
},
|
||||
{
|
||||
rsn: 6,
|
||||
fy: '2025-26',
|
||||
name: 'KAVITA VERMA',
|
||||
pan: 'AKHPV2244R',
|
||||
aadhaar: 'XXXXXXXX1144',
|
||||
mobile: '9415055014',
|
||||
email: '',
|
||||
accountNumber: '110001101001145',
|
||||
typeOfAccount: 'SB',
|
||||
amount: 9850,
|
||||
sftCode: 'SFT-016'
|
||||
},
|
||||
{
|
||||
rsn: 7,
|
||||
fy: '2025-26',
|
||||
name: 'SAMEER KAPOOR',
|
||||
pan: 'AHKPK7788Q',
|
||||
aadhaar: 'XXXXXXXX8877',
|
||||
mobile: '9811022019',
|
||||
email: 'sameer.kapoor@example.com',
|
||||
accountNumber: '110001304001458',
|
||||
typeOfAccount: 'TL',
|
||||
amount: 285600,
|
||||
sftCode: 'SFT-016'
|
||||
},
|
||||
// SFT-015 — Dividend payments (bank as paying entity, e.g. on shares held)
|
||||
{
|
||||
rsn: 8,
|
||||
fy: '2025-26',
|
||||
name: 'ABHISHEK BANERJEE',
|
||||
pan: 'AHKPK7788Q',
|
||||
aadhaar: 'XXXXXXXX3344',
|
||||
mobile: '9831022021',
|
||||
email: 'abhishek.b@example.com',
|
||||
address: '14/B SALT LAKE SECTOR V BIDHANNAGAR',
|
||||
pincode: '700091',
|
||||
amount: 24500,
|
||||
sftCode: 'SFT-015'
|
||||
},
|
||||
{
|
||||
rsn: 9,
|
||||
fy: '2025-26',
|
||||
name: 'GAURAV THAKUR',
|
||||
pan: 'AILPT4455G',
|
||||
aadhaar: 'XXXXXXXX5566',
|
||||
mobile: '9418022023',
|
||||
email: '',
|
||||
address: 'HOUSE 7 NEW SHIMLA SECTOR 4',
|
||||
pincode: '171002',
|
||||
amount: 18750,
|
||||
sftCode: 'SFT-015'
|
||||
},
|
||||
{
|
||||
rsn: 10,
|
||||
fy: '2025-26',
|
||||
name: 'MANMOHAN SINGH GILL',
|
||||
pan: 'ANBPG2233J',
|
||||
aadhaar: '',
|
||||
mobile: '9876022027',
|
||||
email: 'manmohan.gill@example.com',
|
||||
address: 'H 32 MODEL TOWN MAJITHA ROAD AMRITSAR',
|
||||
pincode: '143001',
|
||||
amount: 35200,
|
||||
sftCode: 'SFT-015'
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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 { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { Route } from '../core/route/route.service';
|
||||
import { SftComponent } from './sft.component';
|
||||
|
||||
const routes: Routes = [
|
||||
Route.withShell([
|
||||
{
|
||||
path: 'sft',
|
||||
component: SftComponent,
|
||||
data: { title: 'Income Tax — SFT', breadcrumb: 'SFT Reports' }
|
||||
}
|
||||
])
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class SftRoutingModule {}
|
||||
@@ -0,0 +1,397 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<div class="sft-root">
|
||||
<!-- HEADER -->
|
||||
<div class="sft-head">
|
||||
<div class="sft-head-left">
|
||||
<fa-icon icon="file-invoice" class="sft-head-ic"></fa-icon>
|
||||
<div>
|
||||
<h2 class="sft-head-t">Income Tax — Statement of Financial Transactions (SFT)</h2>
|
||||
<div class="sft-head-s">
|
||||
{{ bank.name }} · {{ bank.branch }} · FY <strong>{{ bank.fy }}</strong> · IT Act §285BA /
|
||||
Rule 114E · Due <strong>{{ bank.filingDueDate }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sft-head-meta">
|
||||
<div>
|
||||
<span class="sft-meta-l">Filer PAN</span><span class="sft-meta-v mono">{{ bank.filerPan }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="sft-meta-l">TAN</span><span class="sft-meta-v mono">{{ bank.filerTan }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="sft-meta-l">Principal Officer</span><span class="sft-meta-v">{{ bank.principalOfficer }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TABS -->
|
||||
<div class="sft-tabs">
|
||||
<button class="sft-tab" [class.active]="activeTab === 'dashboard'" (click)="showTab('dashboard')">
|
||||
<fa-icon icon="chart-pie"></fa-icon> Dashboard
|
||||
</button>
|
||||
<button class="sft-tab" [class.active]="activeTab === 'person'" (click)="showTab('person')">
|
||||
<fa-icon icon="users"></fa-icon> Person-wise (001 / 002 / 004)
|
||||
</button>
|
||||
<button class="sft-tab" [class.active]="activeTab === 'account'" (click)="showTab('account')">
|
||||
<fa-icon icon="university"></fa-icon> Account-wise (003)
|
||||
</button>
|
||||
<button class="sft-tab" [class.active]="activeTab === 'intdiv'" (click)="showTab('intdiv')">
|
||||
<fa-icon icon="coins"></fa-icon> Interest & Dividend (015 / 016)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- DASHBOARD -->
|
||||
@if (activeTab === 'dashboard') {
|
||||
<div class="sft-section">
|
||||
<div class="sft-kpis">
|
||||
<div class="sft-kpi sft-kpi--blue">
|
||||
<div class="sft-kpi-ic"><fa-icon icon="file-alt"></fa-icon></div>
|
||||
<div>
|
||||
<div class="sft-kpi-n">{{ totalRecords | number }}</div>
|
||||
<div class="sft-kpi-l">Total Records</div>
|
||||
<div class="sft-kpi-sub">all SFT codes combined</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sft-kpi sft-kpi--teal">
|
||||
<div class="sft-kpi-ic"><fa-icon icon="coins"></fa-icon></div>
|
||||
<div>
|
||||
<div class="sft-kpi-n">₹{{ totalReportableAmount | number: '1.0-0' }}</div>
|
||||
<div class="sft-kpi-l">Reportable Amount</div>
|
||||
<div class="sft-kpi-sub">aggregate per FY {{ bank.fy }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sft-kpi sft-kpi--amber">
|
||||
<div class="sft-kpi-ic"><fa-icon icon="users"></fa-icon></div>
|
||||
<div>
|
||||
<div class="sft-kpi-n">{{ sft001Count + sft002Count + sft004Count | number }}</div>
|
||||
<div class="sft-kpi-l">Person-wise Records</div>
|
||||
<div class="sft-kpi-sub">
|
||||
{{ sft001Count }} ·001 · {{ sft002Count }} ·002 · {{ sft004Count }} ·004
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sft-kpi sft-kpi--purple">
|
||||
<div class="sft-kpi-ic"><fa-icon icon="university"></fa-icon></div>
|
||||
<div>
|
||||
<div class="sft-kpi-n">{{ sft003Count | number }}</div>
|
||||
<div class="sft-kpi-l">SFT-003 Accounts</div>
|
||||
<div class="sft-kpi-sub">cash CA ≥ ₹50 L per FY</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sft-kpi sft-kpi--green">
|
||||
<div class="sft-kpi-ic"><fa-icon icon="coins"></fa-icon></div>
|
||||
<div>
|
||||
<div class="sft-kpi-n">{{ sft015Count + sft016Count | number }}</div>
|
||||
<div class="sft-kpi-l">Int / Dividend</div>
|
||||
<div class="sft-kpi-sub">{{ sft015Count }} ·015 · {{ sft016Count }} ·016</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SFT code summary cards -->
|
||||
<div class="sft-codes">
|
||||
<div class="sft-code-card">
|
||||
<div class="sft-code-h">
|
||||
<span class="sft-code-tag t-001">SFT-001</span><span>Cash Drafts / Pay Orders</span>
|
||||
</div>
|
||||
<p>
|
||||
Cash purchase of bank drafts, pay orders or banker's cheques aggregating ≥ <strong>₹10 L</strong> per
|
||||
person/FY
|
||||
</p>
|
||||
<div class="sft-code-meta">{{ sft001Count }} record{{ sft001Count === 1 ? '' : 's' }}</div>
|
||||
</div>
|
||||
<div class="sft-code-card">
|
||||
<div class="sft-code-h">
|
||||
<span class="sft-code-tag t-002">SFT-002</span><span>Time Deposits (FD/RD)</span>
|
||||
</div>
|
||||
<p>Time-deposit opening with cash ≥ <strong>₹10 L</strong> per person/FY (excluding renewals)</p>
|
||||
<div class="sft-code-meta">{{ sft002Count }} record{{ sft002Count === 1 ? '' : 's' }}</div>
|
||||
</div>
|
||||
<div class="sft-code-card">
|
||||
<div class="sft-code-h"><span class="sft-code-tag t-003">SFT-003</span><span>Current Account Cash</span></div>
|
||||
<p>
|
||||
Cash deposits / withdrawals — <strong>current account</strong> aggregating ≥
|
||||
<strong>₹50 L</strong> per FY
|
||||
</p>
|
||||
<div class="sft-code-meta">{{ sft003Count }} account{{ sft003Count === 1 ? '' : 's' }}</div>
|
||||
</div>
|
||||
<div class="sft-code-card">
|
||||
<div class="sft-code-h"><span class="sft-code-tag t-004">SFT-004</span><span>Savings Bank Cash</span></div>
|
||||
<p>Cash deposits — <strong>savings account</strong> aggregating ≥ <strong>₹10 L</strong> per FY</p>
|
||||
<div class="sft-code-meta">{{ sft004Count }} record{{ sft004Count === 1 ? '' : 's' }}</div>
|
||||
</div>
|
||||
<div class="sft-code-card">
|
||||
<div class="sft-code-h"><span class="sft-code-tag t-015">SFT-015</span><span>Dividend Payments</span></div>
|
||||
<p>Dividend payments to shareholders — any amount, aggregated per PAN/FY</p>
|
||||
<div class="sft-code-meta">{{ sft015Count }} record{{ sft015Count === 1 ? '' : 's' }}</div>
|
||||
</div>
|
||||
<div class="sft-code-card">
|
||||
<div class="sft-code-h">
|
||||
<span class="sft-code-tag t-016">SFT-016</span><span>Interest Paid on Deposits</span>
|
||||
</div>
|
||||
<p>Interest paid on deposits — any amount, aggregated per PAN/FY</p>
|
||||
<div class="sft-code-meta">{{ sft016Count }} record{{ sft016Count === 1 ? '' : 's' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sft-toolbar">
|
||||
<button class="sft-btn sft-btn-xls" (click)="exportAll()">
|
||||
<fa-icon icon="file-csv"></fa-icon> Download All SFT Files
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- PERSON-WISE -->
|
||||
@if (activeTab === 'person') {
|
||||
<div class="sft-section">
|
||||
<div class="sft-form-meta">
|
||||
<div class="sft-form-title">
|
||||
<h3>Person-wise Records — SFT-001 / 002 / 004</h3>
|
||||
<p>One record per person per SFT code per FY (aggregate)</p>
|
||||
</div>
|
||||
<div class="sft-action-group">
|
||||
<select class="sft-inp sft-inp-narrow" [(ngModel)]="personSftFilter">
|
||||
<option value="ALL">All Codes</option>
|
||||
<option value="SFT-001">SFT-001 only</option>
|
||||
<option value="SFT-002">SFT-002 only</option>
|
||||
<option value="SFT-004">SFT-004 only</option>
|
||||
</select>
|
||||
<button class="sft-btn sft-btn-csv" (click)="exportPersonCsv('SFT-001')">
|
||||
<fa-icon icon="file-csv"></fa-icon> SFT_001.csv
|
||||
</button>
|
||||
<button class="sft-btn sft-btn-csv" (click)="exportPersonCsv('SFT-002')">
|
||||
<fa-icon icon="file-csv"></fa-icon> SFT_002.csv
|
||||
</button>
|
||||
<button class="sft-btn sft-btn-csv" (click)="exportPersonCsv('SFT-004')">
|
||||
<fa-icon icon="file-csv"></fa-icon> SFT_004.csv
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sft-tbl-wrap">
|
||||
<table class="sft-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="ta-c">RSN</th>
|
||||
<th>Code</th>
|
||||
<th>Person Name</th>
|
||||
<th class="ta-c">Type</th>
|
||||
<th>PAN</th>
|
||||
<th>DOB</th>
|
||||
<th>Mobile</th>
|
||||
<th>City</th>
|
||||
<th>Product</th>
|
||||
<th class="ta-r">Aggregate</th>
|
||||
<th class="ta-r">Cash</th>
|
||||
<th>Last Txn</th>
|
||||
<th>Account</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (r of filteredPersons; track r.reportSerialNumber + '-' + r.sftCode) {
|
||||
<tr>
|
||||
<td class="ta-c">{{ r.reportSerialNumber }}</td>
|
||||
<td>
|
||||
<span class="sft-code-tag" [class]="'t-' + r.sftCode.replace('SFT-', '')">{{ r.sftCode }}</span>
|
||||
</td>
|
||||
<td>{{ r.personName }}</td>
|
||||
<td class="ta-c">{{ r.personType }}</td>
|
||||
<td class="mono">{{ r.pan }}</td>
|
||||
<td>{{ r.dobOrIncorporation }}</td>
|
||||
<td class="mono">{{ r.primaryMobileNo }}</td>
|
||||
<td>{{ r.city }}, {{ r.state }}</td>
|
||||
<td>
|
||||
<span class="muted">{{ productLabel(r.productType) }}</span>
|
||||
</td>
|
||||
<td class="ta-r mono">₹{{ r.aggGrossReceived | number: '1.0-0' }}</td>
|
||||
<td class="ta-r mono">₹{{ r.aggGrossReceivedCash | number: '1.0-0' }}</td>
|
||||
<td>{{ r.lastDateOfTransaction }}</td>
|
||||
<td class="mono">{{ r.relatedAccountNumber }}</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<tr>
|
||||
<td colspan="13" class="sft-empty">No records for the selected SFT code.</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ACCOUNT-WISE (SFT-003) -->
|
||||
@if (activeTab === 'account') {
|
||||
<div class="sft-section">
|
||||
<div class="sft-form-meta">
|
||||
<div class="sft-form-title">
|
||||
<h3>SFT-003 — Cash Deposits/Withdrawals in Current Accounts</h3>
|
||||
<p>Account-wise schema with embedded Person Card; aggregate ≥ ₹50 L cash per FY</p>
|
||||
</div>
|
||||
<button class="sft-btn sft-btn-csv" (click)="exportAccountCsv()">
|
||||
<fa-icon icon="file-csv"></fa-icon> Download SFT_003.csv
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sft-acc-list">
|
||||
@for (r of accountRecords; track r.reportSerialNumber) {
|
||||
<div class="acc-card">
|
||||
<div class="acc-card-h">
|
||||
<div class="acc-card-id">
|
||||
<span class="acc-rsn">RSN-{{ r.reportSerialNumber }}</span>
|
||||
<span class="acc-type-pill">{{ r.accountType }}</span>
|
||||
<span class="acc-status-pill" [class.act]="r.accountStatus === 'A'">{{
|
||||
r.accountStatus === 'A' ? 'Active' : r.accountStatus === 'C' ? 'Closed' : 'Dormant'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="acc-card-amt">
|
||||
<span class="muted">Cash credited</span>
|
||||
<strong>₹{{ r.aggGrossCashCredited | number: '1.0-0' }}</strong>
|
||||
<span class="muted">/ debited</span>
|
||||
<strong>₹{{ r.aggGrossCashDebited | number: '1.0-0' }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="acc-card-body">
|
||||
<div class="acc-meta">
|
||||
<div>
|
||||
<label>Account No.</label><span class="mono">{{ r.accountNumber }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Holder</label><span>{{ r.accountHolderName }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Branch</label><span>{{ r.branchName }} ({{ r.branchReferenceNumber }})</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Branch Address</label><span>{{ r.branchAddress }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Branch Phone</label><span class="mono">{{ r.branchStdCode }}-{{ r.branchPhone }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Branch Email</label><span>{{ r.branchEmail }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="acc-pc-block">
|
||||
<div class="acc-pc-h"><fa-icon icon="user"></fa-icon> Person Card</div>
|
||||
<div class="acc-meta">
|
||||
<div>
|
||||
<label>Name</label><span>{{ r.pcPersonName }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Type</label><span>{{ r.pcPersonType }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>PAN</label><span class="mono">{{ r.pcPan }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>DOB / Incorp.</label><span>{{ r.pcDob }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Business</label><span>{{ productLabel(r.pcBusiness) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Mobile</label><span class="mono">{{ r.pcPrimaryMobile }}</span>
|
||||
</div>
|
||||
<div class="acc-pc-addr">
|
||||
<label>Address</label><span>{{ r.pcAddress }}, {{ r.pcCity }}, {{ r.pcState }} {{ r.pcPin }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @empty {
|
||||
<div class="sft-empty-card">No SFT-003 records this FY.</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- INTEREST & DIVIDEND -->
|
||||
@if (activeTab === 'intdiv') {
|
||||
<div class="sft-section">
|
||||
<div class="sft-form-meta">
|
||||
<div class="sft-form-title">
|
||||
<h3>SFT-015 (Dividend) & SFT-016 (Interest Paid)</h3>
|
||||
<p>Pipe-delimited compact format submitted to Income Tax Reporting Portal</p>
|
||||
</div>
|
||||
<div class="sft-action-group">
|
||||
<select class="sft-inp sft-inp-narrow" [(ngModel)]="intDivFilter">
|
||||
<option value="ALL">Both</option>
|
||||
<option value="SFT-015">SFT-015 (Dividend)</option>
|
||||
<option value="SFT-016">SFT-016 (Interest)</option>
|
||||
</select>
|
||||
<button class="sft-btn sft-btn-csv" (click)="exportIntDivCsv('SFT-015')">
|
||||
<fa-icon icon="file-csv"></fa-icon> SFT15.csv
|
||||
</button>
|
||||
<button class="sft-btn sft-btn-csv" (click)="exportIntDivCsv('SFT-016')">
|
||||
<fa-icon icon="file-csv"></fa-icon> SFT16.csv
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sft-tbl-wrap">
|
||||
<table class="sft-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="ta-c">RSN</th>
|
||||
<th>Code</th>
|
||||
<th>FY</th>
|
||||
<th>Name</th>
|
||||
<th>PAN</th>
|
||||
<th>Aadhaar (mask)</th>
|
||||
<th>Mobile</th>
|
||||
<th>Account</th>
|
||||
<th class="ta-c">Type</th>
|
||||
<th class="ta-r">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (r of filteredIntDiv; track r.rsn + '-' + r.sftCode) {
|
||||
<tr>
|
||||
<td class="ta-c">{{ r.rsn }}</td>
|
||||
<td>
|
||||
<span class="sft-code-tag" [class]="'t-' + r.sftCode.replace('SFT-', '')">{{ r.sftCode }}</span>
|
||||
</td>
|
||||
<td>{{ r.fy }}</td>
|
||||
<td>{{ r.name }}</td>
|
||||
<td class="mono">{{ r.pan || '—' }}</td>
|
||||
<td class="mono">{{ r.aadhaar || '—' }}</td>
|
||||
<td class="mono">{{ r.mobile }}</td>
|
||||
<td class="mono">{{ r.accountNumber || '—' }}</td>
|
||||
<td class="ta-c">{{ r.typeOfAccount || (r.sftCode === 'SFT-015' ? 'DIV' : '—') }}</td>
|
||||
<td class="ta-r mono">
|
||||
<strong>₹{{ r.amount | number: '1.0-0' }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
} @empty {
|
||||
<tr>
|
||||
<td colspan="10" class="sft-empty">No records for the selected SFT code.</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="sft-note">
|
||||
<fa-icon icon="info-circle"></fa-icon>
|
||||
<span
|
||||
><strong>Note:</strong> SFT-015 (Dividend) and SFT-016 (Interest) are pipe-delimited (`|`) files, one row per
|
||||
PAN per FY with aggregated amounts. Submit via the IT Reporting Portal under {{ bank.principalOfficer }}'s
|
||||
digital signature.</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- TOAST -->
|
||||
<div class="sft-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
|
||||
</div>
|
||||
@@ -0,0 +1,689 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
:host {
|
||||
--s-navy: #0b1e3d;
|
||||
--s-blue: #1d4ed8;
|
||||
--s-blue-l: #eff6ff;
|
||||
--s-teal: #0e7e6b;
|
||||
--s-teal-l: #e0f4f0;
|
||||
--s-green: #15803d;
|
||||
--s-green-l: #dcfce7;
|
||||
--s-amber: #d97706;
|
||||
--s-amber-l: #fef3c7;
|
||||
--s-red: #c0392b;
|
||||
--s-red-l: #fdecea;
|
||||
--s-purple: #6d28d9;
|
||||
--s-purple-l: #ede9fe;
|
||||
--s-pink: #db2777;
|
||||
--s-pink-l: #fce7f3;
|
||||
--s-g50: #f8fafc;
|
||||
--s-g100: #f1f5f9;
|
||||
--s-g200: #e2e8f0;
|
||||
--s-g300: #cbd5e1;
|
||||
--s-g500: #64748b;
|
||||
--s-g700: #334155;
|
||||
--s-g900: #0f172a;
|
||||
|
||||
display: block;
|
||||
font-family:
|
||||
'DM Sans',
|
||||
system-ui,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
background: var(--s-g50);
|
||||
color: var(--s-g900);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.sft-root {
|
||||
padding: 18px 22px;
|
||||
max-width: 1500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.sft-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.sft-head-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.sft-head-ic {
|
||||
font-size: 28px;
|
||||
color: var(--s-blue);
|
||||
}
|
||||
|
||||
.sft-head-t {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--s-g900);
|
||||
}
|
||||
|
||||
.sft-head-s {
|
||||
font-size: 12px;
|
||||
color: var(--s-g500);
|
||||
}
|
||||
|
||||
.sft-head-meta {
|
||||
display: flex;
|
||||
gap: 22px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.sft-meta-l {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--s-g500);
|
||||
}
|
||||
|
||||
.sft-meta-v {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--s-g900);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sft-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--s-g200);
|
||||
border-radius: 10px;
|
||||
padding: 4px;
|
||||
margin-bottom: 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.sft-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 9px 14px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--s-g500);
|
||||
cursor: pointer;
|
||||
border-radius: 7px;
|
||||
white-space: nowrap;
|
||||
transition: all 0.15s;
|
||||
font-family: inherit;
|
||||
|
||||
&:hover {
|
||||
color: var(--s-g900);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--s-blue);
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 6px rgb(29 78 216 / 25%);
|
||||
}
|
||||
}
|
||||
|
||||
.sft-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* KPIs */
|
||||
.sft-kpis {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sft-kpi {
|
||||
background: #fff;
|
||||
border: 1px solid var(--s-g200);
|
||||
border-radius: 12px;
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 5%);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 14px rgb(0 0 0 / 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.sft-kpi-ic {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 11px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sft-kpi--blue .sft-kpi-ic {
|
||||
background: var(--s-blue-l);
|
||||
color: var(--s-blue);
|
||||
}
|
||||
|
||||
.sft-kpi--teal .sft-kpi-ic {
|
||||
background: var(--s-teal-l);
|
||||
color: var(--s-teal);
|
||||
}
|
||||
|
||||
.sft-kpi--amber .sft-kpi-ic {
|
||||
background: var(--s-amber-l);
|
||||
color: var(--s-amber);
|
||||
}
|
||||
|
||||
.sft-kpi--purple .sft-kpi-ic {
|
||||
background: var(--s-purple-l);
|
||||
color: var(--s-purple);
|
||||
}
|
||||
|
||||
.sft-kpi--green .sft-kpi-ic {
|
||||
background: var(--s-green-l);
|
||||
color: var(--s-green);
|
||||
}
|
||||
|
||||
.sft-kpi-n {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--s-g900);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.sft-kpi-l {
|
||||
font-size: 12px;
|
||||
color: var(--s-g700);
|
||||
font-weight: 500;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.sft-kpi-sub {
|
||||
font-size: 11px;
|
||||
color: var(--s-g500);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
/* SFT code summary cards */
|
||||
.sft-codes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sft-code-card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--s-g200);
|
||||
border-radius: 12px;
|
||||
padding: 14px 16px;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
.sft-code-h {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--s-g900);
|
||||
}
|
||||
|
||||
.sft-code-tag {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
font-family: 'DM Mono', monospace;
|
||||
background: var(--s-g100);
|
||||
color: var(--s-g700);
|
||||
|
||||
&.t-001 {
|
||||
background: var(--s-blue-l);
|
||||
color: var(--s-blue);
|
||||
}
|
||||
|
||||
&.t-002 {
|
||||
background: var(--s-teal-l);
|
||||
color: var(--s-teal);
|
||||
}
|
||||
|
||||
&.t-003 {
|
||||
background: var(--s-purple-l);
|
||||
color: var(--s-purple);
|
||||
}
|
||||
|
||||
&.t-004 {
|
||||
background: var(--s-amber-l);
|
||||
color: var(--s-amber);
|
||||
}
|
||||
|
||||
&.t-015 {
|
||||
background: var(--s-pink-l);
|
||||
color: var(--s-pink);
|
||||
}
|
||||
|
||||
&.t-016 {
|
||||
background: var(--s-green-l);
|
||||
color: var(--s-green);
|
||||
}
|
||||
}
|
||||
|
||||
.sft-code-card p {
|
||||
margin: 0 0 10px;
|
||||
font-size: 12px;
|
||||
color: var(--s-g500);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.sft-code-meta {
|
||||
font-size: 12px;
|
||||
color: var(--s-g700);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Toolbar */
|
||||
.sft-toolbar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Form header */
|
||||
.sft-form-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sft-form-title {
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--s-g900);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 2px 0 0;
|
||||
font-size: 12px;
|
||||
color: var(--s-g500);
|
||||
}
|
||||
}
|
||||
|
||||
.sft-action-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.sft-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 14px;
|
||||
border: 1px solid var(--s-g300);
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--s-g700);
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
transition: all 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: var(--s-g50);
|
||||
border-color: var(--s-g500);
|
||||
}
|
||||
}
|
||||
|
||||
.sft-btn-csv {
|
||||
background: #1d4ed8;
|
||||
border-color: #1d4ed8;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background: #1e40af;
|
||||
border-color: #1e40af;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.sft-btn-xls {
|
||||
background: #16a34a;
|
||||
border-color: #16a34a;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background: #15803d;
|
||||
border-color: #15803d;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
.sft-inp {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--s-g200);
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
background: #fff;
|
||||
font-family: inherit;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--s-blue);
|
||||
box-shadow: 0 0 0 3px rgb(29 78 216 / 15%);
|
||||
}
|
||||
}
|
||||
|
||||
.sft-inp-narrow {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
/* Table */
|
||||
.sft-tbl-wrap {
|
||||
background: #fff;
|
||||
border: 1px solid var(--s-g200);
|
||||
border-radius: 12px;
|
||||
overflow: auto;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
.sft-tbl {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 10px 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--s-g500);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
background: var(--s-g50);
|
||||
border-bottom: 1px solid var(--s-g200);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--s-g100);
|
||||
font-size: 12px;
|
||||
color: var(--s-g700);
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background: var(--s-g50);
|
||||
}
|
||||
}
|
||||
|
||||
.ta-c {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ta-r {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: 'DM Mono', monospace;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--s-g500);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.sft-empty {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
color: var(--s-g500);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sft-empty-card {
|
||||
background: #fff;
|
||||
border: 1px dashed var(--s-g300);
|
||||
border-radius: 12px;
|
||||
padding: 50px;
|
||||
text-align: center;
|
||||
color: var(--s-g500);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Account-wise SFT-003 cards */
|
||||
.sft-acc-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.acc-card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--s-g200);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
.acc-card-h {
|
||||
padding: 12px 16px;
|
||||
background: var(--s-g50);
|
||||
border-bottom: 1px solid var(--s-g200);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.acc-card-id {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.acc-rsn {
|
||||
font-family: 'DM Mono', monospace;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--s-g900);
|
||||
}
|
||||
|
||||
.acc-type-pill {
|
||||
display: inline-block;
|
||||
padding: 2px 9px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: var(--s-purple-l);
|
||||
color: var(--s-purple);
|
||||
}
|
||||
|
||||
.acc-status-pill {
|
||||
display: inline-block;
|
||||
padding: 2px 9px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: var(--s-g100);
|
||||
color: var(--s-g500);
|
||||
|
||||
&.act {
|
||||
background: var(--s-green-l);
|
||||
color: var(--s-green);
|
||||
}
|
||||
}
|
||||
|
||||
.acc-card-amt {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
|
||||
strong {
|
||||
font-size: 14px;
|
||||
color: var(--s-g900);
|
||||
font-family: 'DM Mono', monospace;
|
||||
}
|
||||
}
|
||||
|
||||
.acc-card-body {
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.acc-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px 18px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
label {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: var(--s-g500);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
color: var(--s-g900);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.acc-pc-block {
|
||||
background: var(--s-blue-l);
|
||||
border-radius: 8px;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
|
||||
.acc-pc-h {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--s-blue);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.acc-pc-addr {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
/* Note */
|
||||
.sft-note {
|
||||
background: var(--s-amber-l);
|
||||
border: 1px solid #fed7aa;
|
||||
border-radius: 10px;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
font-size: 12px;
|
||||
color: #92400e;
|
||||
line-height: 1.5;
|
||||
|
||||
fa-icon {
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Toast */
|
||||
.sft-toast {
|
||||
position: fixed;
|
||||
bottom: 32px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(20px);
|
||||
background: var(--s-navy);
|
||||
color: #fff;
|
||||
padding: 12px 22px;
|
||||
border-radius: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 6px 20px rgb(0 0 0 / 25%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: all 0.25s;
|
||||
z-index: 1500;
|
||||
|
||||
&.show {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 1100px) {
|
||||
.sft-kpis {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.sft-codes {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.acc-meta {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 720px) {
|
||||
.sft-kpis {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.sft-codes {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.acc-meta {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,466 @@
|
||||
/**
|
||||
* 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 } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import {
|
||||
SFT_ACCOUNT_RECORDS,
|
||||
SFT_BANK,
|
||||
SFT_INT_DIV_RECORDS,
|
||||
SFT_PERSON_RECORDS,
|
||||
SftAccountRecord,
|
||||
SftCode,
|
||||
SftInterestDividendRecord,
|
||||
SftPersonRecord
|
||||
} from './sft-data';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-sft',
|
||||
templateUrl: './sft.component.html',
|
||||
styleUrls: ['./sft.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class SftComponent {
|
||||
/** Active tab */
|
||||
activeTab: 'dashboard' | 'person' | 'account' | 'intdiv' = 'dashboard';
|
||||
|
||||
bank = SFT_BANK;
|
||||
personRecords: SftPersonRecord[] = SFT_PERSON_RECORDS;
|
||||
accountRecords: SftAccountRecord[] = SFT_ACCOUNT_RECORDS;
|
||||
intDivRecords: SftInterestDividendRecord[] = SFT_INT_DIV_RECORDS;
|
||||
|
||||
/** Person tab — switch which SFT code is being shown */
|
||||
personSftFilter: 'ALL' | 'SFT-001' | 'SFT-002' | 'SFT-004' = 'ALL';
|
||||
|
||||
/** Int/Div tab — switch interest vs dividend */
|
||||
intDivFilter: 'ALL' | 'SFT-015' | 'SFT-016' = 'ALL';
|
||||
|
||||
toastMsg = '';
|
||||
toastVisible = false;
|
||||
private toastTimer: any;
|
||||
|
||||
showTab(tab: 'dashboard' | 'person' | 'account' | 'intdiv'): void {
|
||||
this.activeTab = tab;
|
||||
}
|
||||
|
||||
// -------------------- KPIs --------------------
|
||||
get totalRecords(): number {
|
||||
return this.personRecords.length + this.accountRecords.length + this.intDivRecords.length;
|
||||
}
|
||||
get sft001Count(): number {
|
||||
return this.personRecords.filter((r) => r.sftCode === 'SFT-001').length;
|
||||
}
|
||||
get sft002Count(): number {
|
||||
return this.personRecords.filter((r) => r.sftCode === 'SFT-002').length;
|
||||
}
|
||||
get sft003Count(): number {
|
||||
return this.accountRecords.length;
|
||||
}
|
||||
get sft004Count(): number {
|
||||
return this.personRecords.filter((r) => r.sftCode === 'SFT-004').length;
|
||||
}
|
||||
get sft015Count(): number {
|
||||
return this.intDivRecords.filter((r) => r.sftCode === 'SFT-015').length;
|
||||
}
|
||||
get sft016Count(): number {
|
||||
return this.intDivRecords.filter((r) => r.sftCode === 'SFT-016').length;
|
||||
}
|
||||
get totalReportableAmount(): number {
|
||||
return (
|
||||
this.personRecords.reduce((s, r) => s + r.aggGrossReceived, 0) +
|
||||
this.accountRecords.reduce((s, r) => s + r.aggGrossCashCredited, 0) +
|
||||
this.intDivRecords.reduce((s, r) => s + r.amount, 0)
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------- Filters --------------------
|
||||
get filteredPersons(): SftPersonRecord[] {
|
||||
if (this.personSftFilter === 'ALL') return this.personRecords;
|
||||
return this.personRecords.filter((r) => r.sftCode === this.personSftFilter);
|
||||
}
|
||||
|
||||
get filteredIntDiv(): SftInterestDividendRecord[] {
|
||||
if (this.intDivFilter === 'ALL') return this.intDivRecords;
|
||||
return this.intDivRecords.filter((r) => r.sftCode === this.intDivFilter);
|
||||
}
|
||||
|
||||
// -------------------- Helpers --------------------
|
||||
productLabel(t: string): string {
|
||||
return t.replace(/_/g, ' ');
|
||||
}
|
||||
sftLabel(c: SftCode): string {
|
||||
const map: Record<SftCode, string> = {
|
||||
'SFT-001': 'Cash Drafts/PO',
|
||||
'SFT-002': 'Time Deposits',
|
||||
'SFT-003': 'Cash CA',
|
||||
'SFT-004': 'Cash SB',
|
||||
'SFT-015': 'Dividend',
|
||||
'SFT-016': 'Interest Paid'
|
||||
};
|
||||
return map[c];
|
||||
}
|
||||
|
||||
// -------------------- CSV exports --------------------
|
||||
|
||||
/** SFT-001/002/004 — Person-wise CSV (comma-separated, all 43 columns) */
|
||||
exportPersonCsv(filter?: SftCode): void {
|
||||
const list = filter ? this.personRecords.filter((r) => r.sftCode === filter) : this.filteredPersons;
|
||||
const headers = [
|
||||
'Report Serial Number',
|
||||
'Original Report Serial Number',
|
||||
'Person Name',
|
||||
'Person Type',
|
||||
'Customer Identity',
|
||||
'Gender',
|
||||
'Father\u2019s Name',
|
||||
'PAN',
|
||||
'Aadhaar Number',
|
||||
'Form 60 Acknowledgment',
|
||||
'Identification Type',
|
||||
'Identification Number',
|
||||
'Date of birth/Incorporation',
|
||||
'Nationality/Country of Incorporation',
|
||||
'AddressType',
|
||||
'Fix Address',
|
||||
'City/Town',
|
||||
'PostCode',
|
||||
'State',
|
||||
'CountryCode',
|
||||
'Business or occupation',
|
||||
'Primary STD Code',
|
||||
'Primary Phone No',
|
||||
'Primary Mobile No',
|
||||
'Secondary STD Code',
|
||||
'Secondary Phone No',
|
||||
'Secondary Mobile No',
|
||||
'Email',
|
||||
'Remarks',
|
||||
'Product Type',
|
||||
'Aggregate gross amount received from the person',
|
||||
'Aggregate gross amount received from the person in cash',
|
||||
'Aggregate gross amount paid to the person',
|
||||
'TransactionSummry Remarks',
|
||||
'Product Identifier',
|
||||
'Last Date of Transaction',
|
||||
'Amount Received From Person',
|
||||
'Cash Amount Received From Person',
|
||||
'Amount Paid To Person',
|
||||
'Related Account Number',
|
||||
'Related Institute Name',
|
||||
'Related Institute Ref Number',
|
||||
'Financial TransactionDtls Remarks'
|
||||
];
|
||||
const rows: string[][] = [headers];
|
||||
if (list.length === 0) {
|
||||
rows.push(['NIL Report']);
|
||||
} else {
|
||||
for (const r of list) {
|
||||
rows.push([
|
||||
r.reportSerialNumber.toString(),
|
||||
r.originalReportSerialNumber.toString(),
|
||||
r.personName,
|
||||
r.personType,
|
||||
r.customerIdentity,
|
||||
r.gender,
|
||||
r.fatherName,
|
||||
r.pan,
|
||||
r.aadhaar,
|
||||
r.form60Ack,
|
||||
r.identificationType,
|
||||
r.identificationNumber,
|
||||
r.dobOrIncorporation.replace(/-/g, '-'),
|
||||
r.nationality,
|
||||
r.addressType,
|
||||
r.fixAddress,
|
||||
r.city,
|
||||
r.postCode,
|
||||
r.state,
|
||||
r.countryCode,
|
||||
r.businessOrOccupation,
|
||||
r.primaryStdCode,
|
||||
r.primaryPhoneNo,
|
||||
r.primaryMobileNo,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
r.email,
|
||||
r.remarks,
|
||||
r.productType,
|
||||
r.aggGrossReceived.toString(),
|
||||
r.aggGrossReceivedCash.toString(),
|
||||
r.aggGrossPaid.toString(),
|
||||
'',
|
||||
r.productIdentifier,
|
||||
r.lastDateOfTransaction,
|
||||
r.amountReceivedFromPerson.toString(),
|
||||
r.cashAmountReceivedFromPerson.toString(),
|
||||
r.amountPaidToPerson.toString(),
|
||||
r.relatedAccountNumber,
|
||||
r.relatedInstituteName,
|
||||
r.relatedInstituteRefNumber,
|
||||
''
|
||||
]);
|
||||
}
|
||||
}
|
||||
const fileTag = filter || 'ALL';
|
||||
this.saveCsv(rows, `${fileTag.replace('-', '_')}.csv`);
|
||||
this.toast(`${fileTag} exported (${list.length} rows)`);
|
||||
}
|
||||
|
||||
/** SFT-003 — Account-wise CSV with embedded Person Card */
|
||||
exportAccountCsv(): void {
|
||||
const headers = [
|
||||
'Report Serial Number',
|
||||
'Original Report Serial Number',
|
||||
'Account Type',
|
||||
'Account Number',
|
||||
'Account Holder Name',
|
||||
'Account Status',
|
||||
'Branch Reference Number',
|
||||
'Branch Name',
|
||||
'Branch Address',
|
||||
'Branch City/Town',
|
||||
'Branch Postal Code',
|
||||
'Branch State',
|
||||
'Branch Country',
|
||||
'Branch STD Code',
|
||||
'Branch Phone Number',
|
||||
'Branch Mobile Number',
|
||||
'Branch Fax STD Code',
|
||||
'Branch Fax Phone No',
|
||||
'Branch Email',
|
||||
'Branch Remarks',
|
||||
'Aggregate gross amount credited to the account in cash',
|
||||
'Aggregate gross amount debited to the account in cash',
|
||||
'Amount credited to the account before demonetization',
|
||||
'Amount credited to the account after demonetization',
|
||||
'Account Remarks',
|
||||
'Account Relationship (PC)',
|
||||
'Person Name (PC)',
|
||||
'Person Type (PC)',
|
||||
'Customer Identity (PC)',
|
||||
'Gender (PC)',
|
||||
'Father\u2019s Name (PC)',
|
||||
'PAN (PC)',
|
||||
'Aadhaar Number (PC)',
|
||||
'Form 60 Acknowledgement (PC)',
|
||||
'Identification Type (PC)',
|
||||
'Identification Number (PC)',
|
||||
'Date of Birth/ Incorporation (PC)',
|
||||
'Nationality/Country of Incorporation (PC)',
|
||||
'Business or Occupation (PC)',
|
||||
'Address Type (PC-L)',
|
||||
'Address (PC-L)',
|
||||
'City/Town (PC-L)',
|
||||
'Pin Code (PC-L)',
|
||||
'State (PC-L)',
|
||||
'Country (PC-L)',
|
||||
'Primary STD Code (PC)',
|
||||
'Primary Telephone Number (PC)',
|
||||
'Primary Mobile Number (PC)',
|
||||
'Secondary STD Code (PC)',
|
||||
'Secondary Telephone Number (PC)',
|
||||
'Secondary Mobile Number (PC)',
|
||||
'Email (PC)',
|
||||
'Remarks (PC)'
|
||||
];
|
||||
const rows: string[][] = [headers];
|
||||
if (this.accountRecords.length === 0) {
|
||||
rows.push(['NIL Report']);
|
||||
} else {
|
||||
for (const r of this.accountRecords) {
|
||||
rows.push([
|
||||
r.reportSerialNumber.toString(),
|
||||
r.originalReportSerialNumber.toString(),
|
||||
r.accountType,
|
||||
r.accountNumber,
|
||||
r.accountHolderName,
|
||||
r.accountStatus,
|
||||
r.branchReferenceNumber,
|
||||
r.branchName,
|
||||
r.branchAddress,
|
||||
r.branchCity,
|
||||
r.branchPostalCode,
|
||||
r.branchState,
|
||||
r.branchCountry,
|
||||
r.branchStdCode,
|
||||
r.branchPhone,
|
||||
r.branchMobile,
|
||||
r.branchFaxStd,
|
||||
r.branchFaxPhone,
|
||||
r.branchEmail,
|
||||
'',
|
||||
r.aggGrossCashCredited.toString(),
|
||||
r.aggGrossCashDebited.toString(),
|
||||
r.amountCreditedBeforeDemonetization.toString(),
|
||||
r.amountCreditedAfterDemonetization.toString(),
|
||||
'',
|
||||
r.pcAccountRelationship,
|
||||
r.pcPersonName,
|
||||
r.pcPersonType,
|
||||
r.pcCustomerIdentity,
|
||||
r.pcGender,
|
||||
r.pcFatherName,
|
||||
r.pcPan,
|
||||
r.pcAadhaar,
|
||||
r.pcForm60Ack,
|
||||
r.pcIdentificationType,
|
||||
r.pcIdentificationNumber,
|
||||
r.pcDob,
|
||||
r.pcNationality,
|
||||
r.pcBusiness,
|
||||
r.pcAddressType,
|
||||
r.pcAddress,
|
||||
r.pcCity,
|
||||
r.pcPin,
|
||||
r.pcState,
|
||||
r.pcCountry,
|
||||
r.pcPrimaryStd,
|
||||
r.pcPrimaryPhone,
|
||||
r.pcPrimaryMobile,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
r.pcEmail,
|
||||
''
|
||||
]);
|
||||
}
|
||||
}
|
||||
this.saveCsv(rows, 'SFT_003.csv');
|
||||
this.toast(`SFT-003 exported (${this.accountRecords.length} accounts)`);
|
||||
}
|
||||
|
||||
/** SFT-015 / SFT-016 — Pipe-delimited */
|
||||
exportIntDivCsv(filter?: 'SFT-015' | 'SFT-016'): void {
|
||||
const list = filter ? this.intDivRecords.filter((r) => r.sftCode === filter) : this.filteredIntDiv;
|
||||
const fileTag = filter || 'BOTH';
|
||||
const lines: string[] = [];
|
||||
if (filter === 'SFT-015' || (!filter && list.some((r) => r.sftCode === 'SFT-015'))) {
|
||||
// Dividend file header
|
||||
const headers15 = [
|
||||
'RSN',
|
||||
'FY',
|
||||
'Name',
|
||||
'PAN',
|
||||
'Aadhaar',
|
||||
'Address',
|
||||
'Pincode',
|
||||
'Mobile',
|
||||
'Email',
|
||||
'Dividend',
|
||||
''
|
||||
];
|
||||
const div15 = list.filter((r) => r.sftCode === 'SFT-015');
|
||||
if (filter === 'SFT-015' || div15.length > 0) {
|
||||
if (filter !== 'SFT-016') {
|
||||
lines.push(headers15.join('|'));
|
||||
for (const r of div15) {
|
||||
lines.push(
|
||||
[
|
||||
r.rsn.toString(),
|
||||
r.fy,
|
||||
r.name,
|
||||
r.pan,
|
||||
r.aadhaar,
|
||||
r.address || '',
|
||||
r.pincode || '',
|
||||
r.mobile,
|
||||
r.email,
|
||||
r.amount.toString(),
|
||||
''
|
||||
].join('|')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filter === 'SFT-016' || (!filter && list.some((r) => r.sftCode === 'SFT-016'))) {
|
||||
const headers16 = [
|
||||
'RSN',
|
||||
'FY',
|
||||
'Name',
|
||||
'PAN',
|
||||
'Aadhaar',
|
||||
'Mobile',
|
||||
'Email',
|
||||
'Account Number',
|
||||
'Type of Account',
|
||||
'Interest',
|
||||
''
|
||||
];
|
||||
const int16 = list.filter((r) => r.sftCode === 'SFT-016');
|
||||
if (filter !== 'SFT-015' && int16.length > 0) {
|
||||
if (lines.length > 0) lines.push('');
|
||||
lines.push(headers16.join('|'));
|
||||
for (const r of int16) {
|
||||
lines.push(
|
||||
[
|
||||
r.rsn.toString(),
|
||||
r.fy,
|
||||
r.name,
|
||||
r.pan,
|
||||
r.aadhaar,
|
||||
r.mobile,
|
||||
r.email,
|
||||
r.accountNumber || '',
|
||||
r.typeOfAccount || '',
|
||||
r.amount.toString(),
|
||||
''
|
||||
].join('|')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const filename = filter === 'SFT-015' ? 'SFT15.csv' : filter === 'SFT-016' ? 'SFT16.csv' : 'SFT15_SFT16.csv';
|
||||
this.saveText(lines.join('\n') + '\n', filename, 'text/csv');
|
||||
this.toast(`${fileTag} exported (${list.length} rows)`);
|
||||
}
|
||||
|
||||
/** Bundle: all SFT files (one per SFT code) */
|
||||
exportAll(): void {
|
||||
setTimeout(() => this.exportPersonCsv('SFT-001'), 0);
|
||||
setTimeout(() => this.exportPersonCsv('SFT-002'), 200);
|
||||
setTimeout(() => this.exportAccountCsv(), 400);
|
||||
setTimeout(() => this.exportPersonCsv('SFT-004'), 600);
|
||||
setTimeout(() => this.exportIntDivCsv('SFT-015'), 800);
|
||||
setTimeout(() => this.exportIntDivCsv('SFT-016'), 1000);
|
||||
}
|
||||
|
||||
private saveCsv(rows: string[][], filename: string): void {
|
||||
const escape = (v: string): string => {
|
||||
const s = (v || '').replace(/"/g, '""');
|
||||
if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`;
|
||||
return s;
|
||||
};
|
||||
const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n';
|
||||
this.saveText(csv, filename, 'text/csv');
|
||||
}
|
||||
|
||||
private saveText(content: string, filename: string, mime: string): void {
|
||||
const blob = new Blob([content], { type: `${mime};charset=utf-8` });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
toast(msg: string): void {
|
||||
this.toastMsg = msg;
|
||||
this.toastVisible = true;
|
||||
if (this.toastTimer) clearTimeout(this.toastTimer);
|
||||
this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 { NgModule } from '@angular/core';
|
||||
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { SftRoutingModule } from './sft-routing.module';
|
||||
import { SftComponent } from './sft.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
SftRoutingModule,
|
||||
SftComponent
|
||||
]
|
||||
})
|
||||
export class SftModule {}
|
||||
Reference in New Issue
Block a user