diff --git a/src/app/core/shell/shell.component.html b/src/app/core/shell/shell.component.html index ba5ba74f5..7b92aa74b 100644 --- a/src/app/core/shell/shell.component.html +++ b/src/app/core/shell/shell.component.html @@ -1400,27 +1400,27 @@
Core & Books
- - - - - - @@ -1429,19 +1429,19 @@
Bank & Clearing
- - - - @@ -1450,15 +1450,15 @@
Digital & Payments
- - - @@ -1467,11 +1467,11 @@
Instruments & Members
- - @@ -1480,11 +1480,11 @@
Compliance & Financials
- - diff --git a/src/app/fiu/fiu-data.ts b/src/app/fiu/fiu-data.ts index 2973f5baf..dbfb3c457 100644 --- a/src/app/fiu/fiu-data.ts +++ b/src/app/fiu/fiu-data.ts @@ -6,6 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { BRANCHES, BranchMaster } from '../npa/npa-data'; + +export { BRANCHES, BranchMaster }; + /** * Sample FIU-IND data — schema mirrors the FINnet 2.0 CSV submission templates * (TC1, TC2, KC1, KC2, Account_Detail, Account_Person_Relation). @@ -155,6 +159,25 @@ export const FIU_BANK = { cbwtrThreshold: 500000 // CBWTR threshold ₹5 L }; +/** + * CTR/STR records below use the bank's own internal numeric branch codes (as FINnet's TC2 + * schema expects), not the app-wide BranchMaster codes (BR01, BR02, ...) every other module + * scopes Head-Office-vs-branch access by. This maps one to the other so a branch-level login's + * officeName can be matched consistently, the same way NPA/Reconciliation/Loan Origination do. + */ +export const FIU_BRANCH_CODE_TO_CANONICAL: Record = { + '1': 'BR01', + '2': 'BR02', + '3': 'BR03', + '4': 'BR04', + '5': 'BR05', + '6': 'BR06', + '8': 'BR07', + '9': 'BR08', + '10': 'BR02', + '12': 'BR04' +}; + /** * ── CTR transactions (FINnet TC2-style) ── * Only CASH transactions that breach the RBI/PMLA reporting limit of ₹10,00,000 diff --git a/src/app/fiu/fiu.component.html b/src/app/fiu/fiu.component.html index 34f2d3e5c..21769e2ff 100644 --- a/src/app/fiu/fiu.component.html +++ b/src/app/fiu/fiu.component.html @@ -14,12 +14,25 @@

Financial Intelligence Unit – India (FIU-IND)

- {{ bank.name }} · {{ bank.branch }} · Reporting Month {{ bank.reportingMonth }} + {{ bank.name }} · {{ scopeName }} · Reporting Month {{ bank.reportingMonth }} · PMLA 2002 / FINnet 2.0
+
+ Branch + @if (isHeadOffice) { + + } @else { + {{ scopeName }} + } +
Principal Officer{{ bank.principalOfficer }}
@@ -339,7 +352,9 @@ {{ t.instrumentType }} {{ t.instrumentId }} - {{ t.branchCode }} + + {{ t.branchCode }} {{ branchNameFor(t.branchCode) }} + {{ t.relationshipFlag @@ -779,6 +794,9 @@
{{ s.instrumentType }} {{ s.instrumentId }}
+
+ {{ s.branchCode }} {{ branchNameFor(s.branchCode) }} +
diff --git a/src/app/fiu/fiu.component.ts b/src/app/fiu/fiu.component.ts index 651dcd1c1..1bc47c074 100644 --- a/src/app/fiu/fiu.component.ts +++ b/src/app/fiu/fiu.component.ts @@ -13,9 +13,12 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { AmlService } from 'app/aml/aml.service'; import { AuthenticationService } from 'app/core/authentication/authentication.service'; import { + BRANCHES, + BranchMaster, CtrTransaction, CTR_TXNS, FIU_BANK, + FIU_BRANCH_CODE_TO_CANONICAL, KC1_INDIVIDUALS, KC2_ENTITIES, KycEntity, @@ -51,6 +54,19 @@ export class FiuComponent implements OnInit { kc1: KycIndividual[] = KC1_INDIVIDUALS; kc2: KycEntity[] = KC2_ENTITIES; + // -------------------- Head Office vs Branch scope -------------------- + // CTR/STR filing itself is a bank-wide Principal-Officer function (one filing for the whole + // bank, not per branch), but the underlying cash transactions a branch's own staff work + // (evidence capture, referral) are still branch-attributable — scoped the same way every other + // report in the app splits Head Office (sees everything, can drill into one branch) vs a + // branch-level login (locked to its own branch). + readonly branches = BRANCHES; + isHeadOffice = false; + currentOfficeName = 'Branch Office'; + private ownBranchCode: string | null = null; + /** '' = All Branches (Head Office consolidated view); otherwise a BranchMaster.code. */ + branchFilter = ''; + /** CTR filters */ ctrFilterMode: '' | 'Deposit' | 'Withdrawal' = ''; ctrFilterAccount = ''; @@ -223,6 +239,18 @@ export class FiuComponent implements OnInit { } ngOnInit(): void { + const creds = this.authenticationService.getCredentials(); + this.isHeadOffice = this.authenticationService.isHeadOfficeUser(); + this.currentOfficeName = creds?.officeName || 'Branch Office'; + if (this.isHeadOffice) { + this.branchFilter = ''; + } else { + this.ownBranchCode = + this.branches.find((b: BranchMaster) => this.currentOfficeName.toLowerCase().includes(b.name.toLowerCase())) + ?.code || null; + this.branchFilter = this.ownBranchCode || this.branches[0].code; + } + this.loadAmlAlerts(); this.loadAmlCtrReports(); this.loadAmlStrCases(); @@ -614,21 +642,21 @@ export class FiuComponent implements OnInit { // -------------------- KPIs -------------------- get totalReports(): number { - return new Set(this.ctrTxns.map((t) => t.reportRefNo)).size; + return new Set(this.scopedCtrTxns.map((t) => t.reportRefNo)).size; } get totalCtrTxns(): number { - return this.ctrTxns.length; + return this.scopedCtrTxns.length; } get totalCtrAmount(): number { - return this.ctrTxns.reduce((s, t) => s + t.txnAmount, 0); + return this.scopedCtrTxns.reduce((s, t) => s + t.txnAmount, 0); } get pendingStr(): number { - return this.strTxns.filter( + return this.scopedStrTxns.filter( (s) => s.status === 'draft' || s.status === 'po-review' || s.status === 'awaiting-filing' ).length; } get filedStr(): number { - return this.strTxns.filter((s) => s.status === 'filed').length; + return this.scopedStrTxns.filter((s) => s.status === 'filed').length; } // ── Live STR case counts (backend `amlStrCases`) — these are the cases the @@ -671,10 +699,48 @@ export class FiuComponent implements OnInit { return this.amlAlerts.filter((a) => !ctrRuleCodes.includes(a.ruleCode)); } + // -------------------- Head Office vs Branch scope -------------------- + /** Maps a FINnet-style internal branch code to the app-wide canonical branch code. */ + private canonicalBranchOf(branchCode: string): string | undefined { + return FIU_BRANCH_CODE_TO_CANONICAL[branchCode]; + } + + /** Branch name for a row's raw FINnet branch code, for display alongside the code itself. */ + branchNameFor(branchCode: string): string { + const canonical = this.canonicalBranchOf(branchCode); + return this.branches.find((b) => b.code === canonical)?.name || ''; + } + + setBranch(code: string): void { + this.branchFilter = code; + } + + /** Name of the branch/scope currently being viewed, for display in the header. */ + get scopeName(): string { + if (!this.isHeadOffice) { + return this.currentOfficeName; + } + return this.branchFilter + ? this.branches.find((b) => b.code === this.branchFilter)?.name || '' + : 'All Branches (Consolidated)'; + } + + get scopedCtrTxns(): CtrTransaction[] { + return this.branchFilter + ? this.ctrTxns.filter((t) => this.canonicalBranchOf(t.branchCode) === this.branchFilter) + : this.ctrTxns; + } + + get scopedStrTxns(): StrTransaction[] { + return this.branchFilter + ? this.strTxns.filter((s) => this.canonicalBranchOf(s.branchCode) === this.branchFilter) + : this.strTxns; + } + // -------------------- Filtered lists -------------------- get filteredCtr(): CtrTransaction[] { const acc = this.ctrFilterAccount.trim(); - return this.ctrTxns.filter((t) => { + return this.scopedCtrTxns.filter((t) => { if (this.ctrFilterMode && t.depositOrWithdrawal !== this.ctrFilterMode) return false; if (acc && !t.accountNumber.includes(acc)) return false; if (this.ctrFilterMinAmount > 0 && t.txnAmount < this.ctrFilterMinAmount) return false; @@ -683,7 +749,7 @@ export class FiuComponent implements OnInit { } get filteredStr(): StrTransaction[] { - return this.strTxns.filter((s) => { + return this.scopedStrTxns.filter((s) => { if (this.strFilterStatus && s.status !== this.strFilterStatus) return false; if (this.strFilterRisk && s.riskCategory !== this.strFilterRisk) return false; return true;