feat(clerk): block clerk cash deposits with UI banner and save guard
Clerk cash deposits are not permitted — notes/coins denomination is cashier vault responsibility. Shows warning banner above footer and disables Save button when clerk selects cash payment type on a deposit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
8e64f82c0a
commit
fb1f79c889
@@ -1747,6 +1747,11 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
|
||||
return !!pt?.isCashPayment;
|
||||
}
|
||||
|
||||
/** Clerk attempting a cash deposit — blocked because cash/denomination is cashier's responsibility */
|
||||
get isClerkCashDeposit(): boolean {
|
||||
return this.isClerk && this.isCashPayment && this.selectedRow?.dbCr === 'Cr';
|
||||
}
|
||||
|
||||
/** For cash: amount comes ONLY from denomination total */
|
||||
get effectiveAmount(): number {
|
||||
if (this.isCashPayment) return this.denominationTotal;
|
||||
@@ -2028,6 +2033,15 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
|
||||
|
||||
const isDeposit = row.dbCr === 'Cr';
|
||||
|
||||
// Clerk cash deposit is not permitted — cash handling (notes/coins) is cashier's responsibility
|
||||
if (this.isClerk && isDeposit && isCash) {
|
||||
this.showAlert('error', 'Cash Deposit Not Permitted', [
|
||||
'Cash deposits cannot be processed by a clerk.',
|
||||
'Only the cashier can accept cash deposits (notes/coins denomination is managed at cashier vault).'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Token number is required for all withdrawals (clerk must enter, cashier must enter approved token)
|
||||
if (!this.reviewMode && !isDeposit && !row.tokNo?.trim()) {
|
||||
this.showAlert('error', 'Token Number Required', [
|
||||
@@ -2281,9 +2295,11 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
|
||||
private openConfirmDialog(row: TxnRow, amount: number, isDeposit: boolean, selectedPt: any): void {
|
||||
// Token-based cashier disbursement: balance was already deducted at manager approval
|
||||
const isDisbursement = !isDeposit && !this.isClerk && !!this.activeWithdrawalRequest;
|
||||
// Cashier deposit (savings/current) goes via maker-checker: balance credits only after manager approval
|
||||
// Cashier deposit (savings/current/GL) goes via maker-checker: balance credits only after manager approval
|
||||
const isDepositRequest =
|
||||
isDeposit && !this.isClerk && (row.acTyp === 'SD' || row.acTyp === 'CA' || row.acTyp === 'CB');
|
||||
isDeposit &&
|
||||
!this.isClerk &&
|
||||
(row.acTyp === 'SD' || row.acTyp === 'CA' || row.acTyp === 'CB' || row.acTyp === 'GL');
|
||||
const balanceAfterTxn =
|
||||
isDisbursement || isDepositRequest
|
||||
? this.currentBalance // no immediate balance change
|
||||
@@ -2323,8 +2339,9 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
|
||||
const accountId = this.accountDetails.id;
|
||||
const cfg = this.accountTypeConfig(row.acTyp);
|
||||
|
||||
// GL type → post as manual journal entry
|
||||
if (row.acTyp === 'GL') {
|
||||
// GL withdrawal → direct journal entry (no maker-checker)
|
||||
// GL deposit → falls through to maker-checker block below
|
||||
if (row.acTyp === 'GL' && !isDeposit) {
|
||||
this.submitGlJournalEntry(row, amount, isDeposit);
|
||||
return;
|
||||
}
|
||||
@@ -2335,13 +2352,13 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cashier deposit (savings/current) → route through maker-checker (create PENDING request, no direct credit)
|
||||
// Cashier deposit (savings/current/GL) → route through maker-checker (create PENDING request, no direct credit)
|
||||
// Use the dialog-confirmed amount — for cash that is the denomination total
|
||||
if (
|
||||
!this.isClerk &&
|
||||
!this.reviewMode &&
|
||||
isDeposit &&
|
||||
(row.acTyp === 'SD' || row.acTyp === 'CA' || row.acTyp === 'CB')
|
||||
(row.acTyp === 'SD' || row.acTyp === 'CA' || row.acTyp === 'CB' || row.acTyp === 'GL')
|
||||
) {
|
||||
this.submitCashierDepositRequest(row, this.confirmData?.amount ?? amount);
|
||||
return;
|
||||
@@ -2700,12 +2717,20 @@ export class TransactionEntryComponent implements OnInit, OnDestroy {
|
||||
// deposit would fail the backend denomination-total check
|
||||
const isCash = !!selectedPt?.isCashPayment;
|
||||
|
||||
const isGl = row.acTyp === 'GL';
|
||||
return {
|
||||
accountNo: this.accountDetails.accountNo,
|
||||
accountType: row.acTyp === 'CA' || row.acTyp === 'CB' ? 'CURRENT' : 'SAVINGS',
|
||||
savingsAccountId: this.accountDetails.id,
|
||||
clientId: this.accountDetails.clientId ?? this.clientDetails?.id ?? null,
|
||||
clientName: this.clientDetails?.displayName ?? this.accountDetails?.clientName ?? null,
|
||||
accountNo: isGl ? (this.accountDetails.glCode ?? this.accountDetails.name) : this.accountDetails.accountNo,
|
||||
accountType: isGl ? 'GL' : row.acTyp === 'CA' || row.acTyp === 'CB' ? 'CURRENT' : 'SAVINGS',
|
||||
savingsAccountId: isGl ? null : this.accountDetails.id,
|
||||
// GL-specific fields — ignored by backend for non-GL types
|
||||
glAccountId: isGl ? this.accountDetails.id : null,
|
||||
contraGlId: isGl ? this.cashVaultGlId : null,
|
||||
officeId: isGl ? row.br || 1 : null,
|
||||
currencyCode: isGl ? (this.accountDetails?.currency?.code ?? 'INR') : null,
|
||||
clientId: isGl ? null : (this.accountDetails.clientId ?? this.clientDetails?.id ?? null),
|
||||
clientName: isGl
|
||||
? (this.accountDetails?.name ?? null)
|
||||
: (this.clientDetails?.displayName ?? this.accountDetails?.clientName ?? null),
|
||||
amount,
|
||||
txnDate: txnDateIso,
|
||||
paymentTypeId: row.paymentTypeId || null,
|
||||
|
||||
Reference in New Issue
Block a user