99 lines
3.7 KiB
TypeScript
99 lines
3.7 KiB
TypeScript
/**
|
|
* 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 { Injectable, inject } from '@angular/core';
|
|
import { AuthenticationService } from './authentication/authentication.service';
|
|
import { GL_BRANCHES, GlBranch } from 'app/gl-reports/gl-reports-data';
|
|
|
|
/**
|
|
* Resolves what branch data the logged-in user is allowed to see.
|
|
*
|
|
* Head Office sees every branch and can drill into any one of them; a branch login is
|
|
* locked to its own book. Previously this logic was copy-pasted into the Cash/Clearing
|
|
* Summary and the NPA report with slightly different branch code lists; this service is
|
|
* the single place it lives so new reports cannot drift.
|
|
*
|
|
* The logged-in office is matched to a branch by name, since session credentials carry
|
|
* only officeId and officeName. Office names are seeded to match the GL branch names
|
|
* exactly (see Liquibase 0442).
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class BranchScopeService {
|
|
private auth = inject(AuthenticationService);
|
|
|
|
/** Sentinel used when a branch login cannot be matched to a known branch. */
|
|
static readonly UNRESOLVED = '__unresolved__';
|
|
|
|
get isHeadOffice(): boolean {
|
|
return this.auth.isHeadOfficeUser();
|
|
}
|
|
|
|
/** The office name on the session, for labelling the scope in report headers. */
|
|
get officeName(): string {
|
|
return this.auth.getCredentials()?.officeName || 'Branch Office';
|
|
}
|
|
|
|
/**
|
|
* Branch code the user is confined to, or null for Head Office.
|
|
*
|
|
* Deliberately returns the UNRESOLVED sentinel rather than falling back to the first
|
|
* branch when the office cannot be matched: showing an unrecognised branch someone
|
|
* else's figures would be worse than showing them nothing.
|
|
*/
|
|
get ownBranchCode(): string | null {
|
|
if (this.isHeadOffice) {
|
|
return null;
|
|
}
|
|
const name = this.officeName.toLowerCase().trim();
|
|
const match = GL_BRANCHES.find((b) => b.code !== 'HO' && name === b.name.toLowerCase());
|
|
// Fall back to a substring match for offices named e.g. "Andheri (E) Branch".
|
|
const loose = match ?? GL_BRANCHES.find((b) => b.code !== 'HO' && name.includes(b.name.toLowerCase()));
|
|
return loose?.code ?? BranchScopeService.UNRESOLVED;
|
|
}
|
|
|
|
/** True when the user may choose which branch to view. */
|
|
get canSelectBranch(): boolean {
|
|
return this.isHeadOffice;
|
|
}
|
|
|
|
/** Branches offered in a selector — everything for Head Office, nothing for a branch login. */
|
|
get selectableBranches(): GlBranch[] {
|
|
return this.isHeadOffice ? GL_BRANCHES : [];
|
|
}
|
|
|
|
/** Branch code a report should start on. '' means all branches consolidated. */
|
|
get defaultBranchCode(): string {
|
|
return this.isHeadOffice ? '' : (this.ownBranchCode ?? '');
|
|
}
|
|
|
|
/**
|
|
* Forces a requested branch code into what the user is actually allowed to see.
|
|
* Call this wherever a branch code reaches a data query, so a stale or hand-edited
|
|
* selection cannot widen the user's scope.
|
|
*/
|
|
enforce(requested: string): string {
|
|
return this.isHeadOffice ? requested : (this.ownBranchCode ?? BranchScopeService.UNRESOLVED);
|
|
}
|
|
|
|
/** True when a branch login's office matched no known branch, so it sees no data. */
|
|
get isUnresolved(): boolean {
|
|
return this.ownBranchCode === BranchScopeService.UNRESOLVED;
|
|
}
|
|
|
|
/** Human label for the current scope, for report headers. */
|
|
scopeLabel(selectedCode: string): string {
|
|
if (!this.isHeadOffice) {
|
|
return this.officeName;
|
|
}
|
|
if (!selectedCode) {
|
|
return 'All Branches (Consolidated)';
|
|
}
|
|
return GL_BRANCHES.find((b) => b.code === selectedCode)?.name ?? selectedCode;
|
|
}
|
|
}
|