diff --git a/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.html b/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.html index 4786e7d46..b4656e131 100644 --- a/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.html +++ b/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.html @@ -219,7 +219,8 @@ Date Voucher / Ref No - Account / Reference + Account No + Account Name / Reference Particulars Debit Credit @@ -230,12 +231,10 @@ {{ asOnDate }} - {{ t.voucherNo }} - - -
{{ t.accountName }}
-
{{ t.accountNo }}
+ {{ display(t.voucherNo) }} + {{ display(t.accountNo) }} + {{ t.accountName }} {{ t.particulars }} @if (t.debit > 0) { @@ -250,13 +249,13 @@ } @empty { - No transactions for this filter. + No transactions for this filter. } - Total + Total ₹{{ txnTotal(filteredTxns, 'debit') | number: '1.2-2' }} ₹{{ txnTotal(filteredTxns, 'credit') | number: '1.2-2' }} diff --git a/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.ts b/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.ts index 9e636da20..0399b5d66 100644 --- a/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.ts +++ b/src/app/cashier/cash-clearing-transfer-summary/cash-clearing-transfer-summary.component.ts @@ -34,6 +34,9 @@ interface Branch { 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'; @@ -84,8 +87,8 @@ export class CashClearingTransferSummaryComponent { * of its own Transaction Details). */ branches: Branch[] = [ - { code: 'BR1', name: 'Branch 1', scale: 1 }, - { code: 'BR2', name: 'Branch 2', scale: 0.7 } + { 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. */ @@ -131,7 +134,7 @@ export class CashClearingTransferSummaryComponent { { glCode: '100001', voucherNo: 'CSH/2607/0141', - accountNo: 'Teller-01', + accountNo: 'TIL-0001', accountName: 'Teller Counter 1 Cash Deposits (Aggregated)', particulars: 'Cash Deposit - Savings/Current Accounts', debit: 0, @@ -140,7 +143,7 @@ export class CashClearingTransferSummaryComponent { { glCode: '100001', voucherNo: 'CSH/2607/0142', - accountNo: 'Teller-02', + accountNo: 'TIL-0002', accountName: 'Teller Counter 2 Cash Deposits (Aggregated)', particulars: 'Cash Deposit - Savings/Current Accounts', debit: 0, @@ -176,7 +179,7 @@ export class CashClearingTransferSummaryComponent { { glCode: '100001', voucherNo: 'CSH/2607/0146', - accountNo: 'Branch ATM', + accountNo: 'ATM-0001', accountName: 'ATM Excess Cash Retraction', particulars: 'Excess Cash Surrendered by ATM to Vault', debit: 0, @@ -203,7 +206,7 @@ export class CashClearingTransferSummaryComponent { { glCode: '100001', voucherNo: 'CSH/2607/0149', - accountNo: 'Branch ATM', + accountNo: 'ATM-0001', accountName: 'ATM 1 Cash Loading', particulars: 'Vault to ATM Cash Transfer', debit: 700000, @@ -212,7 +215,7 @@ export class CashClearingTransferSummaryComponent { { glCode: '100001', voucherNo: 'CSH/2607/0150', - accountNo: 'Extension Counter ATM', + accountNo: 'ATM-0002', accountName: 'ATM 2 Cash Loading', particulars: 'Vault to ATM Cash Transfer', debit: 500000, @@ -239,7 +242,7 @@ export class CashClearingTransferSummaryComponent { { glCode: '100001', voucherNo: 'CSH/2607/0153', - accountNo: 'Currency Chest', + accountNo: 'CCH-0001', accountName: 'Currency Chest Remittance', particulars: 'Surplus Cash Remitted to Currency Chest', debit: 400000, @@ -266,7 +269,7 @@ export class CashClearingTransferSummaryComponent { { glCode: '100001', voucherNo: 'CSH/2607/0156', - accountNo: 'Vault', + accountNo: 'VLT-0001', accountName: 'Vault Denomination Exchange Adjustment', particulars: 'Shortage Adjustment - Denomination Exchange', debit: 205000, @@ -1171,7 +1174,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'UPI', voucherNo: 'UPI/2607/44521', - accountNo: 'NPCI-UPI-SETL-IN-01', + accountNo: 'RRN-UPI26073100145', accountName: 'UPI Inward Settlement - Morning Batch (00:00-12:00)', particulars: 'UPI Collect - Merchant/P2P Inward (Aggregated)', debit: 0, @@ -1180,7 +1183,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'UPI', voucherNo: 'UPI/2607/44522', - accountNo: 'NPCI-UPI-SETL-IN-02', + accountNo: 'RRN-UPI26073100289', accountName: 'UPI Inward Settlement - Afternoon Batch (12:00-18:00)', particulars: 'UPI Collect - Merchant/P2P Inward (Aggregated)', debit: 0, @@ -1189,7 +1192,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'UPI', voucherNo: 'UPI/2607/44523', - accountNo: 'NPCI-UPI-SETL-IN-03', + accountNo: 'RRN-UPI26073100356', accountName: 'UPI Inward Settlement - Evening Batch (18:00-24:00)', particulars: 'UPI Collect - Merchant/P2P Inward (Aggregated)', debit: 0, @@ -1198,7 +1201,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'UPI', voucherNo: 'UPI/2607/44524', - accountNo: 'NPCI-UPI-SETL-OUT-01', + accountNo: 'RRN-UPI26073100478', accountName: 'UPI Outward Settlement - Morning Batch (00:00-12:00)', particulars: 'UPI Pay - Merchant/P2P Outward (Aggregated)', debit: 1850000, @@ -1207,7 +1210,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'UPI', voucherNo: 'UPI/2607/44525', - accountNo: 'NPCI-UPI-SETL-OUT-02', + accountNo: 'RRN-UPI26073100512', accountName: 'UPI Outward Settlement - Afternoon Batch (12:00-18:00)', particulars: 'UPI Pay - Merchant/P2P Outward (Aggregated)', debit: 2050000, @@ -1216,7 +1219,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'UPI', voucherNo: 'UPI/2607/44526', - accountNo: 'NPCI-UPI-SETL-OUT-03', + accountNo: 'RRN-UPI26073100634', accountName: 'UPI Outward Settlement - Evening Batch (18:00-24:00)', particulars: 'UPI Pay - Merchant/P2P Outward (Aggregated)', debit: 1490000, @@ -1226,7 +1229,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'ATM', voucherNo: 'ATM/2607/9081', - accountNo: 'ATM-SETL-CR-01', + accountNo: 'RRN-ATM26073100118', accountName: 'ATM Cash Deposits - Own Branch ATM (Aggregated)', particulars: 'ATM Cash Deposit - Own Branch ATM', debit: 0, @@ -1235,7 +1238,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'ATM', voucherNo: 'ATM/2607/9082', - accountNo: 'ATM-SETL-CR-02', + accountNo: 'RRN-ATM26073100224', accountName: 'ATM Cash Deposits - Extension Counter ATM (Aggregated)', particulars: 'ATM Cash Deposit - Extension Counter ATM', debit: 0, @@ -1244,7 +1247,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'ATM', voucherNo: 'ATM/2607/9083', - accountNo: 'ATM-SETL-DR-01', + accountNo: 'RRN-ATM26073100337', accountName: 'ATM Cash Withdrawals - Own Branch ATM (Aggregated)', particulars: 'ATM Cash Withdrawal - Own Branch ATM', debit: 420000, @@ -1253,7 +1256,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'ATM', voucherNo: 'ATM/2607/9084', - accountNo: 'ATM-SETL-DR-02', + accountNo: 'RRN-ATM26073100449', accountName: 'ATM Cash Withdrawals - Other Bank ATM, NFS Switch (Aggregated)', particulars: 'ATM Cash Withdrawal - Other Bank ATM (NFS)', debit: 265000, @@ -1263,7 +1266,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'NACH', voucherNo: 'NACH/2607/3304', - accountNo: 'NACH-SETL-CR-01', + accountNo: 'UMRN-NACH26073100112', accountName: 'NACH Credits - Corporate Payroll Disbursement (Aggregated)', particulars: 'NACH Credit - Salary Disbursement Batch', debit: 0, @@ -1272,7 +1275,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'NACH', voucherNo: 'NACH/2607/3305', - accountNo: 'NACH-SETL-CR-02', + accountNo: 'UMRN-NACH26073100156', accountName: 'NACH Credits - Government Subsidy (DBT) Disbursement (Aggregated)', particulars: 'NACH Credit - Subsidy Disbursement Batch', debit: 0, @@ -1281,7 +1284,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'NACH', voucherNo: 'NACH/2607/3306', - accountNo: 'NACH-SETL-DR-01', + accountNo: 'UMRN-NACH26073100198', accountName: 'NACH Debits - Retail Loan EMI Collection (Aggregated)', particulars: 'NACH Debit - Loan EMI Collection Batch', debit: 560000, @@ -1290,7 +1293,7 @@ export class CashClearingTransferSummaryComponent { { glCode: 'NACH', voucherNo: 'NACH/2607/3307', - accountNo: 'NACH-SETL-DR-02', + accountNo: 'UMRN-NACH26073100233', accountName: 'NACH Debits - Gold Loan EMI Collection (Aggregated)', particulars: 'NACH Debit - Gold Loan EMI Collection Batch', debit: 305000, @@ -1298,9 +1301,14 @@ export class CashClearingTransferSummaryComponent { } ]; + /** 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(); @@ -1318,6 +1326,11 @@ export class CashClearingTransferSummaryComponent { } } + /** 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': @@ -1355,15 +1368,36 @@ export class CashClearingTransferSummaryComponent { return rows.map((r) => ({ ...r, opening: this.roundAmt(r.opening * factor) })); } - private scaleTxns(txns: TxnRow[], factor: number, branchTag?: string): TxnRow[] { - if (factor === 1) { + /** + * 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: branchTag ? `${branchTag}-${t.voucherNo}` : t.voucherNo, - debit: this.roundAmt(t.debit * factor), - credit: this.roundAmt(t.credit * factor) + 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) })); } @@ -1387,13 +1421,13 @@ export class CashClearingTransferSummaryComponent { private effectiveTxns(tab: TabKey): TxnRow[] { const base = this.baseTxnsFor(tab); if (!this.isHeadOffice) { - return this.ownBranch ? this.scaleTxns(base, this.ownBranch.scale, this.ownBranch.code) : base; + return this.scaleTxns(base, this.ownBranch); } if (this.selectedBranchCode === 'ALL') { - return this.branches.flatMap((b) => this.scaleTxns(base, b.scale, b.code)); + return this.branches.flatMap((b) => this.scaleTxns(base, b)); } const branch = this.branches.find((b) => b.code === this.selectedBranchCode); - return this.scaleTxns(base, branch ? branch.scale : 1, branch?.code); + return this.scaleTxns(base, branch ?? null); } get rowsForActiveTab(): SummaryRow[] { @@ -1524,7 +1558,7 @@ export class CashClearingTransferSummaryComponent { 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.scale, b.code) + 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); diff --git a/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts index 5cbc3b024..123c9d596 100644 --- a/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts +++ b/src/app/clients/client-stepper/client-general-step/client-general-step.component.ts @@ -533,6 +533,8 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges screeningLogId: number | null = null; /** Snapshot of the name that was last verified — used to invalidate verification when name changes */ private verifiedName: string = ''; + /** True while prefillForEdit() is patching historical monthly/annual values — see linkMonthlyAnnual(). */ + private suppressMonthlyAnnualLink = false; /** * @param {FormBuilder} formBuilder Form Builder @@ -709,6 +711,12 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges const data = this.clientData; this.loadExistingSeniorPwdDocuments(); + // Loading an existing client's independently-saved monthly/annual values must not run + // through the auto-calc in linkMonthlyAnnual() — patching monthlyIncome then annualIncome + // (or vice versa) would otherwise have each overwrite the other via its valueChanges, + // instead of keeping the exact historical values as saved. + this.suppressMonthlyAnnualLink = true; + // Patch all base controls that exist on the form this.createClientForm.patchValue({ officeId: data.officeId, @@ -743,6 +751,7 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges ? new Date(data.timeline.submittedOnDate) : this.settingsService.businessDate }); + this.suppressMonthlyAnnualLink = false; // Patch name fields (already created by buildDependencies via legalFormId subscription) const legalFormId = data.legalForm?.id || LegalFormId.PERSON; @@ -954,6 +963,43 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges Validators.required ] }); + + this.linkMonthlyAnnual('monthlyIncome', 'annualIncome'); + this.linkMonthlyAnnual('monthlyTurnover', 'annualTurnover'); + } + + /** + * Keeps a Monthly/Annual pair of amount fields in sync at a 12x ratio — typing into either + * one auto-fills the other. Uses { emitEvent: false } on the write side so the two + * subscriptions don't trigger each other back and forth. + */ + /** Rounds to at most 2 decimals without padding whole numbers with a trailing ".00". */ + private round2(n: number): string { + return (Math.round(n * 100) / 100).toString(); + } + + private linkMonthlyAnnual(monthlyControlName: string, annualControlName: string): void { + const monthly = this.createClientForm.get(monthlyControlName); + const annual = this.createClientForm.get(annualControlName); + if (!monthly || !annual) { + return; + } + + monthly.valueChanges.subscribe((value) => { + if (this.suppressMonthlyAnnualLink) { + return; + } + const num = parseFloat(value); + annual.setValue(!isNaN(num) && num >= 0 ? this.round2(num * 12) : '', { emitEvent: false }); + }); + + annual.valueChanges.subscribe((value) => { + if (this.suppressMonthlyAnnualLink) { + return; + } + const num = parseFloat(value); + monthly.setValue(!isNaN(num) && num >= 0 ? this.round2(num / 12) : '', { emitEvent: false }); + }); } /**