Files
cbsnexora/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.ts
T
nitesh 1317a93838 feat(cashier): full double-entry journal view + Excel export for Cash/Clearing/Transfer Summary
Transaction Details now renders each voucher as a paired Dr/Cr journal entry with a
derived contra account and a Balanced status chip, so both sides of a transaction are
visible at a glance for reconciliation. Adds a shared cashier-export.ts (mirrors the
Investment module's exporter) and an Export to Excel button producing a Summary +
Transaction Details workbook, plus a Branch Breakup sheet for Head Office consolidated view.
2026-08-01 20:32:31 +05:30

1803 lines
55 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 { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AuthenticationService } from '../../core/authentication/authentication.service';
import { ExportSheetSpec, exportWorkbook } from '../cashier-export';
interface SummaryRow {
code: string;
name: string;
/** Balance carried forward as of the start of the day — the only figure NOT derived from txns below. */
opening: number;
}
interface TxnRow {
/** Links this transaction to the SummaryRow.code it posts against — the single source of truth for Credit/Debit totals. */
glCode: string;
voucherNo: string;
accountNo: string;
accountName: string;
particulars: string;
debit: number;
credit: number;
}
interface Branch {
code: string;
name: string;
/** Relative size vs the base dataset below (which represents the Head Office main branch, scale 1). */
scale: number;
/** Numeric offset used to derive this branch's own account numbers / IFSC serials — keeps the
* standard 12-digit / IFSC format intact instead of just tagging a prefix onto Branch 1's values. */
acctShift: number;
}
type TabKey = 'cash' | 'transfer' | 'clearing' | 'digital';
/**
* Dummy static Cash / Clearing / Transfer summary + transaction-detail report.
*
* IMPORTANT: Credit/Debit (and therefore Closing) are always DERIVED from the txn
* lists below (rowCredit/rowDebit), never hand-entered separately — this guarantees
* the Summary view and the Transaction Details view can never go out of sync,
* for any branch or for the Head Office's "All Branches" consolidated view.
*/
@Component({
selector: 'mifosx-cash-clearing-transfer-summary',
templateUrl: './cash-clearing-transfer-summary.component.html',
styleUrls: [
'../cashier-shared.scss',
'./cash-clearing-transfer-summary.component.scss'
],
standalone: true,
imports: [
CommonModule,
FormsModule
]
})
export class CashClearingTransferSummaryComponent {
private auth = inject(AuthenticationService);
asOnDate = '';
activeTab: TabKey = 'cash';
showDetails = false;
/** GL/channel filter for the Transaction Details view — null means "All". */
glFilter: string | null = null;
/** True while an Export to Excel workbook is being built — disables the button to prevent double-clicks. */
generating = false;
private readonly tabLabels: Record<TabKey, string> = {
cash: 'Cash',
transfer: 'Transfer',
clearing: 'Clearing',
digital: 'Digital'
};
/** True when the logged-in user belongs to the Head Office (sees both branches + consolidated view). */
isHeadOffice = false;
/** The logged-in user's own branch/office name, from real session credentials. */
currentOfficeName = 'Branch Office';
/** Set when a branch-level login's officeName matches one of the two dummy branches below. */
private ownBranch: Branch | null = null;
/**
* The two branches this demo covers. A branch-level user is matched to one of these by
* their real officeName (case-insensitive substring match) so "Branch 1" logging in sees
* Branch 1's figures and "Branch 2" sees Branch 2's — rename these to your real office
* names if they differ. Both are proportionally scaled from the same base dataset so
* every branch's own numbers stay internally consistent (Summary always equals the sum
* of its own Transaction Details).
*/
branches: Branch[] = [
{ code: 'BR1', name: 'Branch 1', scale: 1, acctShift: 0 },
{ code: 'BR2', name: 'Branch 2', scale: 0.7, acctShift: 400 }
];
/** 'ALL' = consolidated across both branches; otherwise a Branch.code. Only meaningful for Head Office users. */
selectedBranchCode = 'ALL';
cashRows: SummaryRow[] = [{ code: '100001', name: 'Cash at Vault', opening: 3572000 }];
transferRows: SummaryRow[] = [
{ code: '250101', name: 'Suspense Account - Transfer', opening: 85000 },
{ code: '250102', name: 'Sundry Deposit Account', opening: 132500 },
{ code: '250103', name: 'Inter Branch Account (Head Office)', opening: 610000 },
{ code: '250104', name: 'Inward Remittance Suspense', opening: 42000 },
{ code: '250105', name: 'Outward Remittance Suspense', opening: 38500 },
{ code: '251001', name: 'Bank GL - SBI Current Account', opening: 1850000 },
{ code: '251002', name: 'Bank GL - HDFC Bank Current Account', opening: 960000 },
{ code: '251003', name: 'Bank GL - ICICI Bank Current Account', opening: 745000 },
{ code: '251004', name: 'Bank GL - Axis Bank Current Account', opening: 512000 },
{ code: '252001', name: 'DD Payable Account', opening: 96000 },
{ code: '252002', name: 'Pay Order Payable Account', opening: 64000 },
{ code: '253001', name: 'Sundry Creditors', opening: 158000 },
{ code: '253002', name: 'Sundry Debtors', opening: 121000 },
{ code: '254001', name: 'TDS Payable Account', opening: 28500 },
{ code: '254002', name: 'GST Payable Account', opening: 19500 }
];
clearingRows: SummaryRow[] = [
{ code: '300201', name: 'Outward Clearing (Cheques Sent)', opening: 265000 },
{ code: '300202', name: 'Inward Clearing (Cheques Received)', opening: 198000 },
{ code: '300203', name: 'Clearing Suspense Account', opening: 15500 },
{ code: '300204', name: 'CTS Clearing Difference Account', opening: 4200 },
{ code: '300205', name: 'Local Clearing House Account', opening: 88000 }
];
digitalRows: SummaryRow[] = [
{ code: 'RTGS', name: 'RTGS - Overall', opening: 720000 },
{ code: 'NEFT', name: 'NEFT - Overall', opening: 512000 },
{ code: 'UPI', name: 'UPI - Overall', opening: 96000 },
{ code: 'ATM', name: 'ATM - Overall', opening: 340000 },
{ code: 'NACH', name: 'NACH - Overall', opening: 158000 }
];
cashTxns: TxnRow[] = [
{
glCode: '100001',
voucherNo: 'CSH/2607/0141',
accountNo: 'TIL-0001',
accountName: 'Teller Counter 1 Cash Deposits (Aggregated)',
particulars: 'Cash Deposit - Savings/Current Accounts',
debit: 0,
credit: 950000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0142',
accountNo: 'TIL-0002',
accountName: 'Teller Counter 2 Cash Deposits (Aggregated)',
particulars: 'Cash Deposit - Savings/Current Accounts',
debit: 0,
credit: 700000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0143',
accountNo: '015020198765',
accountName: 'Anita Traders',
particulars: 'Cash Deposit - Current Account',
debit: 0,
credit: 185000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0144',
accountNo: '015010112345',
accountName: 'Ramesh Kumar Sahu',
particulars: 'Cash Deposit - Savings Account',
debit: 0,
credit: 25000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0145',
accountNo: '015020209988',
accountName: 'Kavita Enterprises',
particulars: 'Cash Deposit - Current Account',
debit: 0,
credit: 110000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0146',
accountNo: 'ATM-0001',
accountName: 'ATM Excess Cash Retraction',
particulars: 'Excess Cash Surrendered by ATM to Vault',
debit: 0,
credit: 400000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0147',
accountNo: '015010223344',
accountName: 'Suresh Yadav',
particulars: 'Cash Deposit - Savings Account',
debit: 0,
credit: 15000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0148',
accountNo: '015030112233',
accountName: 'Kiran Bhosale',
particulars: 'Cash Deposit - RD Installment',
debit: 0,
credit: 130000
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0149',
accountNo: 'ATM-0001',
accountName: 'ATM 1 Cash Loading',
particulars: 'Vault to ATM Cash Transfer',
debit: 700000,
credit: 0
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0150',
accountNo: 'ATM-0002',
accountName: 'ATM 2 Cash Loading',
particulars: 'Vault to ATM Cash Transfer',
debit: 500000,
credit: 0
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0151',
accountNo: '015010154321',
accountName: 'Sunita Devi',
particulars: 'Cash Withdrawal - Savings Account',
debit: 12000,
credit: 0
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0152',
accountNo: '015010167890',
accountName: 'Mohan Lal Verma',
particulars: 'Cash Withdrawal - Term Deposit Closure',
debit: 75000,
credit: 0
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0153',
accountNo: 'CCH-0001',
accountName: 'Currency Chest Remittance',
particulars: 'Surplus Cash Remitted to Currency Chest',
debit: 400000,
credit: 0
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0154',
accountNo: '015020278899',
accountName: 'Prakash Jadhav',
particulars: 'Cash Withdrawal - Current Account',
debit: 210000,
credit: 0
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0155',
accountNo: '015010289900',
accountName: 'Snehal Kulkarni',
particulars: 'Cash Withdrawal - Savings Account',
debit: 18000,
credit: 0
},
{
glCode: '100001',
voucherNo: 'CSH/2607/0156',
accountNo: 'VLT-0001',
accountName: 'Vault Denomination Exchange Adjustment',
particulars: 'Shortage Adjustment - Denomination Exchange',
debit: 205000,
credit: 0
}
];
transferTxns: TxnRow[] = [
// 250101 - Suspense Account - Transfer (credit 420000 / debit 398000)
{
glCode: '250101',
voucherNo: 'TRF/2607/0101',
accountNo: '015020145678',
accountName: 'Vikram Traders',
particulars: 'Suspense Recovered - Vikram Traders',
debit: 0,
credit: 250000
},
{
glCode: '250101',
voucherNo: 'TRF/2607/0102',
accountNo: '015020145991',
accountName: 'Rohit Sharma',
particulars: 'Suspense Recovered - Rohit Sharma',
debit: 0,
credit: 170000
},
{
glCode: '250101',
voucherNo: 'TRF/2607/0103',
accountNo: '015020156432',
accountName: 'Meena Enterprises',
particulars: 'Suspense Adjustment - Meena Enterprises',
debit: 230000,
credit: 0
},
{
glCode: '250101',
voucherNo: 'TRF/2607/0104',
accountNo: '015020156877',
accountName: 'Kavita Singh',
particulars: 'Suspense Adjustment - Kavita Singh',
debit: 168000,
credit: 0
},
// 250102 - Sundry Deposit Account (credit 96000 / debit 110000)
{
glCode: '250102',
voucherNo: 'TRF/2607/0105',
accountNo: '015020167531',
accountName: 'Ashok Furnitures',
particulars: 'Sundry Deposit Received - Ashok Furnitures',
debit: 0,
credit: 60000
},
{
glCode: '250102',
voucherNo: 'TRF/2607/0106',
accountNo: '015020167998',
accountName: 'Ganesh Traders',
particulars: 'Sundry Deposit Received - Ganesh Traders',
debit: 0,
credit: 36000
},
{
glCode: '250102',
voucherNo: 'TRF/2607/0107',
accountNo: '015020178654',
accountName: 'Geeta Boutique',
particulars: 'Sundry Deposit Refunded - Geeta Boutique',
debit: 70000,
credit: 0
},
{
glCode: '250102',
voucherNo: 'TRF/2607/0108',
accountNo: '015020178901',
accountName: 'Manisha Enterprises',
particulars: 'Sundry Deposit Refunded - Manisha Enterprises',
debit: 40000,
credit: 0
},
// 250103 - Inter Branch Account, Head Office (credit 1250000 / debit 1180000)
{
glCode: '250103',
voucherNo: 'TRF/2607/0109',
accountNo: 'BR-0007',
accountName: 'Andheri Branch (Branch Code 0007)',
particulars: 'Funds Received from Andheri Branch',
debit: 0,
credit: 750000
},
{
glCode: '250103',
voucherNo: 'TRF/2607/0110',
accountNo: 'BR-0015',
accountName: 'Dadar Branch (Branch Code 0015)',
particulars: 'Funds Received from Dadar Branch',
debit: 0,
credit: 500000
},
{
glCode: '250103',
voucherNo: 'TRF/2607/0111',
accountNo: 'BR-0012',
accountName: 'Powai Branch (Branch Code 0012)',
particulars: 'Fund Transfer to Powai Branch',
debit: 680000,
credit: 0
},
{
glCode: '250103',
voucherNo: 'TRF/2607/0112',
accountNo: 'BR-0019',
accountName: 'Thane Branch (Branch Code 0019)',
particulars: 'Fund Transfer to Thane Branch',
debit: 500000,
credit: 0
},
// 250104 - Inward Remittance Suspense (credit 315000 / debit 300000)
{
glCode: '250104',
voucherNo: 'TRF/2607/0113',
accountNo: '015020189712',
accountName: 'Suresh Patil',
particulars: 'Inward Remittance Received - Suresh Patil',
debit: 0,
credit: 190000
},
{
glCode: '250104',
voucherNo: 'TRF/2607/0114',
accountNo: '015020189834',
accountName: 'Rahul Deshmukh',
particulars: 'Inward Remittance Received - Rahul Deshmukh',
debit: 0,
credit: 125000
},
{
glCode: '250104',
voucherNo: 'TRF/2607/0115',
accountNo: '015020190845',
accountName: 'Lata Joshi',
particulars: 'Inward Remittance Credited - Lata Joshi',
debit: 175000,
credit: 0
},
{
glCode: '250104',
voucherNo: 'TRF/2607/0116',
accountNo: '015020190956',
accountName: 'Farhan Sheikh',
particulars: 'Inward Remittance Credited - Farhan Sheikh',
debit: 125000,
credit: 0
},
// 250105 - Outward Remittance Suspense (credit 275000 / debit 260000)
{
glCode: '250105',
voucherNo: 'TRF/2607/0117',
accountNo: '015020201456',
accountName: 'Anil Kapoor Traders',
particulars: 'Outward Remittance Booked - Anil Kapoor Traders',
debit: 0,
credit: 160000
},
{
glCode: '250105',
voucherNo: 'TRF/2607/0118',
accountNo: '015020201567',
accountName: 'Sameer Qureshi',
particulars: 'Outward Remittance Booked - Sameer Qureshi',
debit: 0,
credit: 115000
},
{
glCode: '250105',
voucherNo: 'TRF/2607/0119',
accountNo: '015020212789',
accountName: 'Deepa Enterprises',
particulars: 'Outward Remittance Settled - Deepa Enterprises',
debit: 150000,
credit: 0
},
{
glCode: '250105',
voucherNo: 'TRF/2607/0120',
accountNo: '015020212890',
accountName: 'Yogesh Pawar',
particulars: 'Outward Remittance Settled - Yogesh Pawar',
debit: 110000,
credit: 0
},
// 251001 - Bank GL - SBI Current Account (credit 3250000 / debit 2980000)
{
glCode: '251001',
voucherNo: 'TRF/2607/0121',
accountNo: 'SBIN0001234',
accountName: 'SBI Mumbai Main Branch',
particulars: 'Inward Settlement via SBI Current A/c',
debit: 0,
credit: 1500000
},
{
glCode: '251001',
voucherNo: 'TRF/2607/0122',
accountNo: 'SBIN0002211',
accountName: 'SBI Andheri Branch',
particulars: 'Inward Settlement via SBI Current A/c',
debit: 0,
credit: 1000000
},
{
glCode: '251001',
voucherNo: 'TRF/2607/0123',
accountNo: 'SBIN0003322',
accountName: 'SBI Thane Branch',
particulars: 'Inward Settlement via SBI Current A/c',
debit: 0,
credit: 750000
},
{
glCode: '251001',
voucherNo: 'TRF/2607/0124',
accountNo: 'SBIN0005678',
accountName: 'SBI Pune Branch',
particulars: 'Outward Settlement via SBI Current A/c',
debit: 1400000,
credit: 0
},
{
glCode: '251001',
voucherNo: 'TRF/2607/0125',
accountNo: 'SBIN0006689',
accountName: 'SBI Nashik Branch',
particulars: 'Outward Settlement via SBI Current A/c',
debit: 900000,
credit: 0
},
{
glCode: '251001',
voucherNo: 'TRF/2607/0126',
accountNo: 'SBIN0007790',
accountName: 'SBI Nagpur Branch',
particulars: 'Outward Settlement via SBI Current A/c',
debit: 680000,
credit: 0
},
// 251002 - Bank GL - HDFC Bank Current Account (credit 1840000 / debit 1725000)
{
glCode: '251002',
voucherNo: 'TRF/2607/0127',
accountNo: 'HDFC0002345',
accountName: 'HDFC Bank Andheri Branch',
particulars: 'Inward Settlement via HDFC Current A/c',
debit: 0,
credit: 900000
},
{
glCode: '251002',
voucherNo: 'TRF/2607/0128',
accountNo: 'HDFC0003456',
accountName: 'HDFC Bank Bandra Branch',
particulars: 'Inward Settlement via HDFC Current A/c',
debit: 0,
credit: 600000
},
{
glCode: '251002',
voucherNo: 'TRF/2607/0129',
accountNo: 'HDFC0004567',
accountName: 'HDFC Bank Malad Branch',
particulars: 'Inward Settlement via HDFC Current A/c',
debit: 0,
credit: 340000
},
{
glCode: '251002',
voucherNo: 'TRF/2607/0130',
accountNo: 'HDFC0006789',
accountName: 'HDFC Bank Borivali Branch',
particulars: 'Outward Settlement via HDFC Current A/c',
debit: 800000,
credit: 0
},
{
glCode: '251002',
voucherNo: 'TRF/2607/0131',
accountNo: 'HDFC0007890',
accountName: 'HDFC Bank Goregaon Branch',
particulars: 'Outward Settlement via HDFC Current A/c',
debit: 600000,
credit: 0
},
{
glCode: '251002',
voucherNo: 'TRF/2607/0132',
accountNo: 'HDFC0008901',
accountName: 'HDFC Bank Kandivali Branch',
particulars: 'Outward Settlement via HDFC Current A/c',
debit: 325000,
credit: 0
},
// 251003 - Bank GL - ICICI Bank Current Account (credit 1420000 / debit 1360000)
{
glCode: '251003',
voucherNo: 'TRF/2607/0133',
accountNo: 'ICIC0003456',
accountName: 'ICICI Bank Thane Branch',
particulars: 'Inward Settlement via ICICI Current A/c',
debit: 0,
credit: 820000
},
{
glCode: '251003',
voucherNo: 'TRF/2607/0134',
accountNo: 'ICIC0004567',
accountName: 'ICICI Bank Ghatkopar Branch',
particulars: 'Inward Settlement via ICICI Current A/c',
debit: 0,
credit: 600000
},
{
glCode: '251003',
voucherNo: 'TRF/2607/0135',
accountNo: 'ICIC0007890',
accountName: 'ICICI Bank Vashi Branch',
particulars: 'Outward Settlement via ICICI Current A/c',
debit: 760000,
credit: 0
},
{
glCode: '251003',
voucherNo: 'TRF/2607/0136',
accountNo: 'ICIC0008901',
accountName: 'ICICI Bank Chembur Branch',
particulars: 'Outward Settlement via ICICI Current A/c',
debit: 600000,
credit: 0
},
// 251004 - Bank GL - Axis Bank Current Account (credit 980000 / debit 905000)
{
glCode: '251004',
voucherNo: 'TRF/2607/0137',
accountNo: 'UTIB0004567',
accountName: 'Axis Bank Dadar Branch',
particulars: 'Inward Settlement via Axis Current A/c',
debit: 0,
credit: 560000
},
{
glCode: '251004',
voucherNo: 'TRF/2607/0138',
accountNo: 'UTIB0005678',
accountName: 'Axis Bank Sion Branch',
particulars: 'Inward Settlement via Axis Current A/c',
debit: 0,
credit: 420000
},
{
glCode: '251004',
voucherNo: 'TRF/2607/0139',
accountNo: 'UTIB0008901',
accountName: 'Axis Bank Kurla Branch',
particulars: 'Outward Settlement via Axis Current A/c',
debit: 505000,
credit: 0
},
{
glCode: '251004',
voucherNo: 'TRF/2607/0140',
accountNo: 'UTIB0009012',
accountName: 'Axis Bank Ghansoli Branch',
particulars: 'Outward Settlement via Axis Current A/c',
debit: 400000,
credit: 0
},
// 252001 - DD Payable Account (credit 240000 / debit 218000)
{
glCode: '252001',
voucherNo: 'TRF/2607/0141',
accountNo: 'DD/2607/5541',
accountName: 'Sanjay Mehta',
particulars: 'Demand Draft Issued - Sanjay Mehta',
debit: 0,
credit: 140000
},
{
glCode: '252001',
voucherNo: 'TRF/2607/0142',
accountNo: 'DD/2607/5556',
accountName: 'Farida Sheikh',
particulars: 'Demand Draft Issued - Farida Sheikh',
debit: 0,
credit: 100000
},
{
glCode: '252001',
voucherNo: 'TRF/2607/0143',
accountNo: 'DD/2607/5498',
accountName: 'Rekha Nair',
particulars: 'Demand Draft Paid - Rekha Nair',
debit: 128000,
credit: 0
},
{
glCode: '252001',
voucherNo: 'TRF/2607/0144',
accountNo: 'DD/2607/5487',
accountName: 'Girish Rao',
particulars: 'Demand Draft Paid - Girish Rao',
debit: 90000,
credit: 0
},
// 252002 - Pay Order Payable Account (credit 175000 / debit 160000)
{
glCode: '252002',
voucherNo: 'TRF/2607/0145',
accountNo: 'PO/2607/1187',
accountName: 'Arvind Traders',
particulars: 'Pay Order Issued - Arvind Traders',
debit: 0,
credit: 100000
},
{
glCode: '252002',
voucherNo: 'TRF/2607/0146',
accountNo: 'PO/2607/1192',
accountName: 'Neelam Gupta',
particulars: 'Pay Order Issued - Neelam Gupta',
debit: 0,
credit: 75000
},
{
glCode: '252002',
voucherNo: 'TRF/2607/0147',
accountNo: 'PO/2607/1145',
accountName: 'Vandana Enterprises',
particulars: 'Pay Order Paid - Vandana Enterprises',
debit: 95000,
credit: 0
},
{
glCode: '252002',
voucherNo: 'TRF/2607/0148',
accountNo: 'PO/2607/1138',
accountName: 'Tushar Bhatt',
particulars: 'Pay Order Paid - Tushar Bhatt',
debit: 65000,
credit: 0
},
// 253001 - Sundry Creditors (credit 92000 / debit 85000)
{
glCode: '253001',
voucherNo: 'TRF/2607/0149',
accountNo: '015020223412',
accountName: 'Om Stationery Suppliers',
particulars: 'Vendor Bill Provided For - Om Stationery Suppliers',
debit: 0,
credit: 52000
},
{
glCode: '253001',
voucherNo: 'TRF/2607/0150',
accountNo: '015020223567',
accountName: 'Shubham Printers',
particulars: 'Vendor Bill Provided For - Shubham Printers',
debit: 0,
credit: 40000
},
{
glCode: '253001',
voucherNo: 'TRF/2607/0151',
accountNo: '015020234598',
accountName: 'Shree Balaji AMC Services',
particulars: 'Vendor Payment - AMC Charges',
debit: 50000,
credit: 0
},
{
glCode: '253001',
voucherNo: 'TRF/2607/0152',
accountNo: '015020234678',
accountName: 'Metro Security Services',
particulars: 'Vendor Payment - Security Services',
debit: 35000,
credit: 0
},
// 253002 - Sundry Debtors (credit 68000 / debit 74000)
{
glCode: '253002',
voucherNo: 'TRF/2607/0153',
accountNo: '015020245671',
accountName: 'Kiran Hardware',
particulars: 'Recovery from Sundry Debtor - Kiran Hardware',
debit: 0,
credit: 40000
},
{
glCode: '253002',
voucherNo: 'TRF/2607/0154',
accountNo: '015020245789',
accountName: 'Vishal Electronics',
particulars: 'Recovery from Sundry Debtor - Vishal Electronics',
debit: 0,
credit: 28000
},
{
glCode: '253002',
voucherNo: 'TRF/2607/0155',
accountNo: '015020256784',
accountName: 'Nilesh Electricals',
particulars: 'Advance Given to Sundry Debtor - Nilesh Electricals',
debit: 44000,
credit: 0
},
{
glCode: '253002',
voucherNo: 'TRF/2607/0156',
accountNo: '015020256890',
accountName: 'Ramdas Plumbing Works',
particulars: 'Advance Given to Sundry Debtor - Ramdas Plumbing Works',
debit: 30000,
credit: 0
},
// 254001 - TDS Payable Account (credit 46000 / debit 40000)
{
glCode: '254001',
voucherNo: 'TRF/2607/0157',
accountNo: 'TDS/2607/9911',
accountName: 'TDS on FD Interest Payouts',
particulars: 'TDS Deducted on FD Interest Payouts',
debit: 0,
credit: 28000
},
{
glCode: '254001',
voucherNo: 'TRF/2607/0158',
accountNo: 'TDS/2607/9917',
accountName: 'TDS on RD Interest Payouts',
particulars: 'TDS Deducted on RD Interest Payouts',
debit: 0,
credit: 18000
},
{
glCode: '254001',
voucherNo: 'TRF/2607/0159',
accountNo: 'TDS/2607/9902',
accountName: 'Income Tax Department',
particulars: 'TDS Remitted to Government - Quarterly Challan',
debit: 24000,
credit: 0
},
{
glCode: '254001',
voucherNo: 'TRF/2607/0160',
accountNo: 'TDS/2607/9903',
accountName: 'Income Tax Department',
particulars: 'TDS Remitted to Government - Adjustment Challan',
debit: 16000,
credit: 0
},
// 254002 - GST Payable Account (credit 38000 / debit 32000)
{
glCode: '254002',
voucherNo: 'TRF/2607/0161',
accountNo: 'GST/2607/7731',
accountName: 'GST on Service Charges',
particulars: 'GST Collected on Service Charges',
debit: 0,
credit: 22000
},
{
glCode: '254002',
voucherNo: 'TRF/2607/0162',
accountNo: 'GST/2607/7736',
accountName: 'GST on Locker Rent',
particulars: 'GST Collected on Locker Rent',
debit: 0,
credit: 16000
},
{
glCode: '254002',
voucherNo: 'TRF/2607/0163',
accountNo: 'GST/2607/7702',
accountName: 'GST Department',
particulars: 'GST Remitted to Government - Monthly Return',
debit: 19000,
credit: 0
},
{
glCode: '254002',
voucherNo: 'TRF/2607/0164',
accountNo: 'GST/2607/7703',
accountName: 'GST Department',
particulars: 'GST Remitted to Government - Late Fee Adjustment',
debit: 13000,
credit: 0
}
];
clearingTxns: TxnRow[] = [
// 300201 - Outward Clearing, Cheques Sent (credit 980000 / debit 910000)
{
glCode: '300201',
voucherNo: 'CLR/2607/0031',
accountNo: 'CHQ-000123',
accountName: 'Rajesh Kumar Singh',
particulars: 'Cheque No 000123 Sent - Rajesh Kumar Singh',
debit: 0,
credit: 550000
},
{
glCode: '300201',
voucherNo: 'CLR/2607/0032',
accountNo: 'CHQ-000129',
accountName: 'Manoj Tiwari',
particulars: 'Cheque No 000129 Sent - Manoj Tiwari',
debit: 0,
credit: 430000
},
{
glCode: '300201',
voucherNo: 'CLR/2607/0033',
accountNo: 'CHQ-000145',
accountName: 'Nisha Agarwal',
particulars: 'Outward Clearing Realised - Nisha Agarwal',
debit: 520000,
credit: 0
},
{
glCode: '300201',
voucherNo: 'CLR/2607/0034',
accountNo: 'CHQ-000151',
accountName: 'Devendra Kale',
particulars: 'Outward Clearing Realised - Devendra Kale',
debit: 390000,
credit: 0
},
// 300202 - Inward Clearing, Cheques Received (credit 875000 / debit 820000)
{
glCode: '300202',
voucherNo: 'CLR/2607/0035',
accountNo: 'CHQ-000456',
accountName: 'Sameer Joshi',
particulars: 'Cheque No 000456 Received - Sameer Joshi',
debit: 0,
credit: 500000
},
{
glCode: '300202',
voucherNo: 'CLR/2607/0036',
accountNo: 'CHQ-000461',
accountName: 'Ritu Bansal',
particulars: 'Cheque No 000461 Received - Ritu Bansal',
debit: 0,
credit: 375000
},
{
glCode: '300202',
voucherNo: 'CLR/2607/0037',
accountNo: 'CHQ-000478',
accountName: 'Pooja Malhotra',
particulars: 'Inward Clearing Passed for Payment - Pooja Malhotra',
debit: 470000,
credit: 0
},
{
glCode: '300202',
voucherNo: 'CLR/2607/0038',
accountNo: 'CHQ-000483',
accountName: 'Ajit Naik',
particulars: 'Inward Clearing Passed for Payment - Ajit Naik',
debit: 350000,
credit: 0
},
// 300203 - Clearing Suspense Account (credit 60000 / debit 58000)
{
glCode: '300203',
voucherNo: 'CLR/2607/0039',
accountNo: 'CHQ-000512',
accountName: 'ABC Enterprises',
particulars: 'Clearing Difference Booked - ABC Enterprises',
debit: 0,
credit: 35000
},
{
glCode: '300203',
voucherNo: 'CLR/2607/0040',
accountNo: 'CHQ-000519',
accountName: 'Global Traders',
particulars: 'Clearing Difference Booked - Global Traders',
debit: 0,
credit: 25000
},
{
glCode: '300203',
voucherNo: 'CLR/2607/0041',
accountNo: 'CHQ-000534',
accountName: 'XYZ Traders',
particulars: 'Cheque Return - Insufficient Funds - XYZ Traders',
debit: 33000,
credit: 0
},
{
glCode: '300203',
voucherNo: 'CLR/2607/0042',
accountNo: 'CHQ-000541',
accountName: 'Om Textiles',
particulars: 'Cheque Return - Insufficient Funds - Om Textiles',
debit: 25000,
credit: 0
},
// 300204 - CTS Clearing Difference Account (credit 12500 / debit 11000)
{
glCode: '300204',
voucherNo: 'CLR/2607/0043',
accountNo: 'CTS-GRID-01A',
accountName: 'CTS Grid Rounding Adjustment - Batch 1 (Cr)',
particulars: 'CTS Grid Rounding Adjustment - Batch 1',
debit: 0,
credit: 7500
},
{
glCode: '300204',
voucherNo: 'CLR/2607/0044',
accountNo: 'CTS-GRID-01B',
accountName: 'CTS Grid Rounding Adjustment - Batch 2 (Cr)',
particulars: 'CTS Grid Rounding Adjustment - Batch 2',
debit: 0,
credit: 5000
},
{
glCode: '300204',
voucherNo: 'CLR/2607/0045',
accountNo: 'CTS-GRID-02A',
accountName: 'CTS Grid Rounding Adjustment - Batch 1 (Dr)',
particulars: 'CTS Grid Rounding Adjustment - Batch 1',
debit: 6500,
credit: 0
},
{
glCode: '300204',
voucherNo: 'CLR/2607/0046',
accountNo: 'CTS-GRID-02B',
accountName: 'CTS Grid Rounding Adjustment - Batch 2 (Dr)',
particulars: 'CTS Grid Rounding Adjustment - Batch 2',
debit: 4500,
credit: 0
},
// 300205 - Local Clearing House Account (credit 310000 / debit 292000)
{
glCode: '300205',
voucherNo: 'CLR/2607/0047',
accountNo: 'MUM-CH-S1',
accountName: 'Mumbai Clearing House - Session I',
particulars: 'CTS Grid Settlement - Session I (Cr)',
debit: 0,
credit: 180000
},
{
glCode: '300205',
voucherNo: 'CLR/2607/0048',
accountNo: 'MUM-CH-S2',
accountName: 'Mumbai Clearing House - Session II',
particulars: 'CTS Grid Settlement - Session II (Cr)',
debit: 0,
credit: 130000
},
{
glCode: '300205',
voucherNo: 'CLR/2607/0049',
accountNo: 'MUM-CH-S1D',
accountName: 'Mumbai Clearing House - Session I',
particulars: 'CTS Grid Settlement - Session I (Dr)',
debit: 165000,
credit: 0
},
{
glCode: '300205',
voucherNo: 'CLR/2607/0050',
accountNo: 'MUM-CH-S2D',
accountName: 'Mumbai Clearing House - Session II',
particulars: 'CTS Grid Settlement - Session II (Dr)',
debit: 127000,
credit: 0
}
];
digitalTxns: TxnRow[] = [
// RTGS - Overall (credit 4850000 / debit 4620000)
{
glCode: 'RTGS',
voucherNo: 'RTGS/2607/8891',
accountNo: 'UTR-RTGS26073100234',
accountName: 'Deepak Enterprises',
particulars: 'RTGS Inward - Remitting Bank HDFC',
debit: 0,
credit: 2200000
},
{
glCode: 'RTGS',
voucherNo: 'RTGS/2607/8892',
accountNo: 'UTR-RTGS26073100256',
accountName: 'Reliance Distributors Pvt Ltd',
particulars: 'RTGS Inward - Remitting Bank ICICI',
debit: 0,
credit: 1650000
},
{
glCode: 'RTGS',
voucherNo: 'RTGS/2607/8893',
accountNo: 'UTR-RTGS26073100278',
accountName: 'Wadia Constructions',
particulars: 'RTGS Inward - Remitting Bank Axis',
debit: 0,
credit: 1000000
},
{
glCode: 'RTGS',
voucherNo: 'RTGS/2607/8894',
accountNo: 'UTR-RTGS26073100891',
accountName: 'Kunal Manufacturing Co.',
particulars: 'RTGS Outward - Beneficiary Bank HDFC',
debit: 2000000,
credit: 0
},
{
glCode: 'RTGS',
voucherNo: 'RTGS/2607/8895',
accountNo: 'UTR-RTGS26073100904',
accountName: 'Bansal Steel Traders',
particulars: 'RTGS Outward - Beneficiary Bank SBI',
debit: 1600000,
credit: 0
},
{
glCode: 'RTGS',
voucherNo: 'RTGS/2607/8896',
accountNo: 'UTR-RTGS26073100918',
accountName: 'Mehta Exports',
particulars: 'RTGS Outward - Beneficiary Bank ICICI',
debit: 1020000,
credit: 0
},
// NEFT - Overall (credit 3260000 / debit 3105000)
{
glCode: 'NEFT',
voucherNo: 'NEFT/2607/6712',
accountNo: 'UTR-NEFT26073100589',
accountName: 'Priya Sharma',
particulars: 'NEFT Inward - Remitting Bank SBI',
debit: 0,
credit: 1500000
},
{
glCode: 'NEFT',
voucherNo: 'NEFT/2607/6713',
accountNo: 'UTR-NEFT26073100601',
accountName: 'Kunal Oberoi',
particulars: 'NEFT Inward - Remitting Bank HDFC',
debit: 0,
credit: 1100000
},
{
glCode: 'NEFT',
voucherNo: 'NEFT/2607/6714',
accountNo: 'UTR-NEFT26073100617',
accountName: 'Sanya Kapoor',
particulars: 'NEFT Inward - Remitting Bank ICICI',
debit: 0,
credit: 660000
},
{
glCode: 'NEFT',
voucherNo: 'NEFT/2607/6715',
accountNo: 'UTR-NEFT26073100742',
accountName: 'Sanjay Wholesale Traders',
particulars: 'NEFT Outward - Beneficiary Bank SBI',
debit: 1400000,
credit: 0
},
{
glCode: 'NEFT',
voucherNo: 'NEFT/2607/6716',
accountNo: 'UTR-NEFT26073100758',
accountName: 'Rakesh Chandok',
particulars: 'NEFT Outward - Beneficiary Bank Axis',
debit: 1000000,
credit: 0
},
{
glCode: 'NEFT',
voucherNo: 'NEFT/2607/6717',
accountNo: 'UTR-NEFT26073100763',
accountName: 'Meera Iyer',
particulars: 'NEFT Outward - Beneficiary Bank HDFC',
debit: 705000,
credit: 0
},
// UPI - Overall (credit 5480000 / debit 5390000) - NPCI settlement batched by time slot
{
glCode: 'UPI',
voucherNo: 'UPI/2607/44521',
accountNo: 'RRN-UPI26073100145',
accountName: 'UPI Inward Settlement - Morning Batch (00:00-12:00)',
particulars: 'UPI Collect - Merchant/P2P Inward (Aggregated)',
debit: 0,
credit: 1980000
},
{
glCode: 'UPI',
voucherNo: 'UPI/2607/44522',
accountNo: 'RRN-UPI26073100289',
accountName: 'UPI Inward Settlement - Afternoon Batch (12:00-18:00)',
particulars: 'UPI Collect - Merchant/P2P Inward (Aggregated)',
debit: 0,
credit: 2100000
},
{
glCode: 'UPI',
voucherNo: 'UPI/2607/44523',
accountNo: 'RRN-UPI26073100356',
accountName: 'UPI Inward Settlement - Evening Batch (18:00-24:00)',
particulars: 'UPI Collect - Merchant/P2P Inward (Aggregated)',
debit: 0,
credit: 1400000
},
{
glCode: 'UPI',
voucherNo: 'UPI/2607/44524',
accountNo: 'RRN-UPI26073100478',
accountName: 'UPI Outward Settlement - Morning Batch (00:00-12:00)',
particulars: 'UPI Pay - Merchant/P2P Outward (Aggregated)',
debit: 1850000,
credit: 0
},
{
glCode: 'UPI',
voucherNo: 'UPI/2607/44525',
accountNo: 'RRN-UPI26073100512',
accountName: 'UPI Outward Settlement - Afternoon Batch (12:00-18:00)',
particulars: 'UPI Pay - Merchant/P2P Outward (Aggregated)',
debit: 2050000,
credit: 0
},
{
glCode: 'UPI',
voucherNo: 'UPI/2607/44526',
accountNo: 'RRN-UPI26073100634',
accountName: 'UPI Outward Settlement - Evening Batch (18:00-24:00)',
particulars: 'UPI Pay - Merchant/P2P Outward (Aggregated)',
debit: 1490000,
credit: 0
},
// ATM - Overall (credit 210000 / debit 685000)
{
glCode: 'ATM',
voucherNo: 'ATM/2607/9081',
accountNo: 'RRN-ATM26073100118',
accountName: 'ATM Cash Deposits - Own Branch ATM (Aggregated)',
particulars: 'ATM Cash Deposit - Own Branch ATM',
debit: 0,
credit: 150000
},
{
glCode: 'ATM',
voucherNo: 'ATM/2607/9082',
accountNo: 'RRN-ATM26073100224',
accountName: 'ATM Cash Deposits - Extension Counter ATM (Aggregated)',
particulars: 'ATM Cash Deposit - Extension Counter ATM',
debit: 0,
credit: 60000
},
{
glCode: 'ATM',
voucherNo: 'ATM/2607/9083',
accountNo: 'RRN-ATM26073100337',
accountName: 'ATM Cash Withdrawals - Own Branch ATM (Aggregated)',
particulars: 'ATM Cash Withdrawal - Own Branch ATM',
debit: 420000,
credit: 0
},
{
glCode: 'ATM',
voucherNo: 'ATM/2607/9084',
accountNo: 'RRN-ATM26073100449',
accountName: 'ATM Cash Withdrawals - Other Bank ATM, NFS Switch (Aggregated)',
particulars: 'ATM Cash Withdrawal - Other Bank ATM (NFS)',
debit: 265000,
credit: 0
},
// NACH - Overall (credit 940000 / debit 865000)
{
glCode: 'NACH',
voucherNo: 'NACH/2607/3304',
accountNo: 'UMRN-NACH26073100112',
accountName: 'NACH Credits - Corporate Payroll Disbursement (Aggregated)',
particulars: 'NACH Credit - Salary Disbursement Batch',
debit: 0,
credit: 650000
},
{
glCode: 'NACH',
voucherNo: 'NACH/2607/3305',
accountNo: 'UMRN-NACH26073100156',
accountName: 'NACH Credits - Government Subsidy (DBT) Disbursement (Aggregated)',
particulars: 'NACH Credit - Subsidy Disbursement Batch',
debit: 0,
credit: 290000
},
{
glCode: 'NACH',
voucherNo: 'NACH/2607/3306',
accountNo: 'UMRN-NACH26073100198',
accountName: 'NACH Debits - Retail Loan EMI Collection (Aggregated)',
particulars: 'NACH Debit - Loan EMI Collection Batch',
debit: 560000,
credit: 0
},
{
glCode: 'NACH',
voucherNo: 'NACH/2607/3307',
accountNo: 'UMRN-NACH26073100233',
accountName: 'NACH Debits - Gold Loan EMI Collection (Aggregated)',
particulars: 'NACH Debit - Gold Loan EMI Collection Batch',
debit: 305000,
credit: 0
}
];
/** YYMM tag baked into every dummy voucher/reference number (e.g. "2607" for Jul 2026) — kept
* in sync with the real current date so it never goes stale in a future month. */
private dateTag = '2607';
constructor() {
const d = new Date();
this.asOnDate = `${String(d.getDate()).padStart(2, '0')}-${String(d.getMonth() + 1).padStart(2, '0')}-${d.getFullYear()}`;
this.dateTag = `${String(d.getFullYear()).slice(-2)}${String(d.getMonth() + 1).padStart(2, '0')}`;
const creds = this.auth.getCredentials();
this.isHeadOffice = this.auth.isHeadOfficeUser();
this.currentOfficeName = creds?.officeName || 'Branch Office';
if (this.isHeadOffice) {
// Head Office starts on the consolidated view and can drill into either branch.
this.selectedBranchCode = 'ALL';
} else {
// Match the logged-in office's real name to one of the two dummy branches, so each
// branch's login shows its own figures. Falls back to the unscaled base dataset
// (still labelled with the user's real office name) if no branch name matches.
this.ownBranch =
this.branches.find((b) => this.currentOfficeName.toLowerCase().includes(b.name.toLowerCase())) || null;
this.selectedBranchCode = this.ownBranch?.code || 'OWN';
}
}
/** Renders a voucher/reference number with the baked-in "2607" swapped for today's real YYMM tag. */
display(value: string): string {
return (value || '').replace(/2607/g, this.dateTag);
}
private baseRowsFor(tab: TabKey): SummaryRow[] {
switch (tab) {
case 'cash':
return this.cashRows;
case 'transfer':
return this.transferRows;
case 'clearing':
return this.clearingRows;
case 'digital':
return this.digitalRows;
}
}
private baseTxnsFor(tab: TabKey): TxnRow[] {
switch (tab) {
case 'cash':
return this.cashTxns;
case 'transfer':
return this.transferTxns;
case 'clearing':
return this.clearingTxns;
case 'digital':
return this.digitalTxns;
}
}
private roundAmt(v: number): number {
return Math.round(v / 100) * 100;
}
private scaleRows(rows: SummaryRow[], factor: number): SummaryRow[] {
if (factor === 1) {
return rows;
}
return rows.map((r) => ({ ...r, opening: this.roundAmt(r.opening * factor) }));
}
/**
* Re-derives a customer account number or bank IFSC code for a different branch by shifting
* its numeric segment — NOT by tagging a prefix onto Branch 1's value, since real account
* numbers and IFSC codes have a fixed, standard shape and a "BR2-" prefix would look fake.
* Everything else (UTR/RRN/UMRN/DD/PO/TDS/GST/cheque/branch/settlement references) keeps the
* simpler branch-tag prefix, since those are already free-form internal reference formats.
*/
private scaleAccountNo(accountNo: string, branch: Branch): string {
if (/^\d{12}$/.test(accountNo)) {
const branchSeg = (parseInt(accountNo.slice(0, 3), 10) + branch.acctShift) % 1000;
return String(branchSeg).padStart(3, '0') + accountNo.slice(3);
}
const ifsc = accountNo.match(/^([A-Z]{4}0)(\d{6})$/);
if (ifsc) {
const serial = (parseInt(ifsc[2], 10) + branch.acctShift) % 1000000;
return ifsc[1] + String(serial).padStart(6, '0');
}
return `${branch.code}-${accountNo}`;
}
private scaleTxns(txns: TxnRow[], branch: Branch | null): TxnRow[] {
if (!branch || branch.scale === 1) {
return txns;
}
return txns.map((t) => ({
...t,
voucherNo: `${branch.code}-${t.voucherNo}`,
accountNo: this.scaleAccountNo(t.accountNo, branch),
debit: this.roundAmt(t.debit * branch.scale),
credit: this.roundAmt(t.credit * branch.scale)
}));
}
/** Rows for a given tab, scoped to the currently selected branch (or consolidated across both, for Head Office). */
private effectiveRows(tab: TabKey): SummaryRow[] {
const base = this.baseRowsFor(tab);
if (!this.isHeadOffice) {
return this.ownBranch ? this.scaleRows(base, this.ownBranch.scale) : base;
}
if (this.selectedBranchCode === 'ALL') {
return base.map((r) => ({
...r,
opening: this.branches.reduce((sum, b) => sum + this.roundAmt(r.opening * b.scale), 0)
}));
}
const branch = this.branches.find((b) => b.code === this.selectedBranchCode);
return this.scaleRows(base, branch ? branch.scale : 1);
}
/** Transactions for a given tab, scoped to the currently selected branch (or merged across both, for Head Office). */
private effectiveTxns(tab: TabKey): TxnRow[] {
const base = this.baseTxnsFor(tab);
if (!this.isHeadOffice) {
return this.scaleTxns(base, this.ownBranch);
}
if (this.selectedBranchCode === 'ALL') {
return this.branches.flatMap((b) => this.scaleTxns(base, b));
}
const branch = this.branches.find((b) => b.code === this.selectedBranchCode);
return this.scaleTxns(base, branch ?? null);
}
get rowsForActiveTab(): SummaryRow[] {
return this.effectiveRows(this.activeTab);
}
get txnsForActiveTab(): TxnRow[] {
return this.effectiveTxns(this.activeTab);
}
/** Name of the branch/scope currently being viewed, for display in the header. */
get scopeName(): string {
if (!this.isHeadOffice) {
return this.currentOfficeName;
}
if (this.selectedBranchCode === 'ALL') {
return 'All Branches (Consolidated)';
}
return this.branches.find((b) => b.code === this.selectedBranchCode)?.name || '';
}
setBranch(code: string): void {
this.selectedBranchCode = code;
this.glFilter = null;
}
setTab(tab: TabKey): void {
this.activeTab = tab;
this.glFilter = null;
}
toggleDetails(): void {
this.showDetails = !this.showDetails;
this.glFilter = null;
}
setGlFilter(code: string | null): void {
this.glFilter = code;
}
/** Transactions for the Transaction Details view, narrowed to the selected GL/channel filter (or all, when null). */
get filteredTxns(): TxnRow[] {
if (!this.glFilter) {
return this.txnsForActiveTab;
}
return this.txnsForActiveTab.filter((t) => t.glCode === this.glFilter);
}
/** Short label for a GL filter chip — the channel code for Digital (RTGS/NEFT/...), the GL name otherwise. */
filterLabel(row: SummaryRow): string {
return this.activeTab === 'digital' ? row.code : row.name;
}
/** GL name for the code tracked on this txn row, so the Transaction Details journal can show
* "which GL" alongside the account, not just a bare code. */
glName(code: string): string {
return this.rowsForActiveTab.find((r) => r.code === code)?.name || code;
}
/**
* Every transaction here is only ever recorded as ONE leg (whichever GL this Cash/Clearing/
* Transfer/Digital screen tracks) — the offsetting leg (the customer's own account, a
* settlement/clearing-house account, etc.) was never modelled as a separate GL row. Rather
* than leaving that side blank, this derives a sensible label for it so the Transaction
* Details view can show the FULL double entry (Dr leg + Cr leg, always equal amounts) —
* exactly what a real CBS "Day Book"/Journal Register shows, and what makes it possible to
* see at a glance that a transaction is complete and ready for reconciliation.
*
* For internal cash-movement postings (ATM cash-in-transit, currency chest, denomination
* shortages, CTS grid rounding) the account name already describes the SAME leg rather than a
* distinct counter-party, so those get a purpose-built contra label. Everything else already
* has a descriptive counter-party name (customer, vendor, bank branch, government department)
* that IS the natural contra account, so it's used as-is — WITHOUT repeating the account/
* reference number, since that number is already shown against the tracked GL leg on the
* other line; showing it a second time here would make both lines look like the same account,
* defeating the point of a "Balanced" two-sided entry.
*/
contraLabel(t: TxnRow): string {
const p = t.particulars.toLowerCase();
if (p.includes('atm') && (p.includes('vault') || p.includes('loading') || p.includes('surrendered'))) {
return 'ATM Cash-in-Transit Account';
}
if (p.includes('currency chest')) {
return 'Currency Chest Account (Sponsor Bank)';
}
if (p.includes('denomination') || p.includes('shortage adjustment')) {
return 'Cash Difference / Shortage Suspense Account';
}
if (p.includes('cts grid rounding')) {
return 'CTS Grid Rounding Suspense Account';
}
return t.accountName;
}
/** The single non-zero amount on this leg — always what the derived contra leg mirrors. */
legAmount(t: TxnRow): number {
return t.debit > 0 ? t.debit : t.credit;
}
/** Credit total for a GL row — always summed from its own txn lines, never a separate hand-entered number. */
rowCredit(row: SummaryRow, txns: TxnRow[]): number {
return txns.filter((t) => t.glCode === row.code).reduce((sum, t) => sum + t.credit, 0);
}
/** Debit total for a GL row — always summed from its own txn lines, never a separate hand-entered number. */
rowDebit(row: SummaryRow, txns: TxnRow[]): number {
return txns.filter((t) => t.glCode === row.code).reduce((sum, t) => sum + t.debit, 0);
}
closing(row: SummaryRow, txns: TxnRow[]): number {
return row.opening + this.rowCredit(row, txns) - this.rowDebit(row, txns);
}
total(rows: SummaryRow[], txns: TxnRow[], key: 'opening' | 'credit' | 'debit'): number {
if (key === 'opening') {
return rows.reduce((sum, r) => sum + r.opening, 0);
}
if (key === 'credit') {
return rows.reduce((sum, r) => sum + this.rowCredit(r, txns), 0);
}
return rows.reduce((sum, r) => sum + this.rowDebit(r, txns), 0);
}
totalClosing(rows: SummaryRow[], txns: TxnRow[]): number {
return this.total(rows, txns, 'opening') + this.total(rows, txns, 'credit') - this.total(rows, txns, 'debit');
}
txnTotal(rows: TxnRow[], key: 'debit' | 'credit'): number {
return rows.reduce((sum, r) => sum + r[key], 0);
}
private readonly allTabs: TabKey[] = [
'cash',
'transfer',
'clearing',
'digital'
];
/** Cash/Transfer/Clearing/Digital category pairs, scoped to the currently selected branch. */
private get categories(): Array<{ rows: SummaryRow[]; txns: TxnRow[] }> {
return this.allTabs.map((tab) => ({ rows: this.effectiveRows(tab), txns: this.effectiveTxns(tab) }));
}
get grandOpening(): number {
return this.categories.reduce((sum, c) => sum + this.total(c.rows, c.txns, 'opening'), 0);
}
get grandCredit(): number {
return this.categories.reduce((sum, c) => sum + this.total(c.rows, c.txns, 'credit'), 0);
}
get grandDebit(): number {
return this.categories.reduce((sum, c) => sum + this.total(c.rows, c.txns, 'debit'), 0);
}
get grandClosing(): number {
return this.grandOpening + this.grandCredit - this.grandDebit;
}
/**
* Branch-wise contribution to the day's overall position — shown only to Head Office
* users while viewing "All Branches". Each branch's figures are derived the same way
* (opening scaled, credit/debit summed from that branch's own scaled txns), so this
* table can never drift from what you'd see by drilling into that branch individually.
*/
get branchBreakup(): Array<{
code: string;
name: string;
opening: number;
credit: number;
debit: number;
closing: number;
}> {
return this.branches.map((b) => {
const pairs = this.allTabs.map((tab) => ({
rows: this.scaleRows(this.baseRowsFor(tab), b.scale),
txns: this.scaleTxns(this.baseTxnsFor(tab), b)
}));
const opening = pairs.reduce((sum, c) => sum + this.total(c.rows, c.txns, 'opening'), 0);
const credit = pairs.reduce((sum, c) => sum + this.total(c.rows, c.txns, 'credit'), 0);
const debit = pairs.reduce((sum, c) => sum + this.total(c.rows, c.txns, 'debit'), 0);
return { code: b.code, name: b.name, opening, credit, debit, closing: opening + credit - debit };
});
}
/**
* Exports the full report for the currently viewed scope (own branch, a drilled-into branch,
* or "All Branches" consolidated) — every category (Cash/Transfer/Clearing/Digital), not just
* the tab on screen, so the workbook is a complete record. "Summary" mirrors the Opening/
* Credit/Debit/Closing view; "Transaction Details" mirrors the full double-entry journal view
* (same glName/contraLabel/legAmount derivations, so the export can never disagree with what's
* on screen); "Branch Breakup" is added only when Head Office is viewing the consolidated view.
*/
async exportExcel(): Promise<void> {
this.generating = true;
try {
const summaryRows: (string | number | null)[][] = [];
const journalRows: (string | number | null)[][] = [];
for (const tab of this.allTabs) {
const label = this.tabLabels[tab];
const rows = this.effectiveRows(tab);
const txns = this.effectiveTxns(tab);
rows.forEach((r) => {
summaryRows.push([
label,
r.code,
r.name,
r.opening,
this.rowCredit(r, txns),
this.rowDebit(r, txns),
this.closing(r, txns)
]);
});
summaryRows.push([
`${label} Total`,
'',
'',
this.total(rows, txns, 'opening'),
this.total(rows, txns, 'credit'),
this.total(rows, txns, 'debit'),
this.totalClosing(rows, txns)
]);
txns.forEach((t) => {
const glText = rows.find((r) => r.code === t.glCode)?.name || t.glCode;
journalRows.push([
label,
this.asOnDate,
this.display(t.voucherNo),
'Dr',
t.debit > 0 ? t.glCode : '',
t.debit > 0 ? `${glText} — ${this.display(t.accountNo)} (${t.accountName})` : this.contraLabel(t),
this.legAmount(t),
null,
'Balanced'
]);
journalRows.push([
label,
this.asOnDate,
this.display(t.voucherNo),
'Cr',
t.credit > 0 ? t.glCode : '',
t.credit > 0 ? `${glText} — ${this.display(t.accountNo)} (${t.accountName})` : this.contraLabel(t),
null,
this.legAmount(t),
'Balanced'
]);
});
}
summaryRows.push([
'Grand Total',
'',
'',
this.grandOpening,
this.grandCredit,
this.grandDebit,
this.grandClosing
]);
const sheets: ExportSheetSpec[] = [
{
sheetName: 'Summary',
title: 'Cash / Clearing / Transfer - Summary Report',
subtitle: `Scope: ${this.scopeName} · As On: ${this.asOnDate}`,
headers: [
'Category',
'GL Code',
'Particulars',
'Opening',
'Credit',
'Debit',
'Closing'
],
widths: [
12,
12,
34,
16,
16,
16,
16
],
rows: summaryRows
},
{
sheetName: 'Transaction Details',
title: 'Transaction Details — Full Double Entry',
subtitle: `Scope: ${this.scopeName} · As On: ${this.asOnDate}`,
headers: [
'Category',
'Date',
'Voucher / Ref No',
'Dr / Cr',
'GL Code',
'GL / Account',
'Debit',
'Credit',
'Status'
],
widths: [
12,
12,
20,
8,
10,
46,
16,
16,
12
],
rows: journalRows
}
];
if (this.isHeadOffice && this.selectedBranchCode === 'ALL') {
sheets.push({
sheetName: 'Branch Breakup',
title: 'Branch-wise Break-up',
subtitle: `As On: ${this.asOnDate}`,
headers: [
'Branch',
'Opening',
'Credit',
'Debit',
'Closing'
],
widths: [
22,
16,
16,
16,
16
],
rows: [
...this.branchBreakup.map((b) => [
b.name,
b.opening,
b.credit,
b.debit,
b.closing
]),
[
'All Branches',
this.grandOpening,
this.grandCredit,
this.grandDebit,
this.grandClosing
]
]
});
}
await exportWorkbook(sheets, `Cash-Clearing-Transfer-Summary-${this.scopeName}-${this.asOnDate}`);
} catch (err) {
console.error('Cash/Clearing/Transfer export failed', err);
} finally {
this.generating = false;
}
}
}