feat(cashier,savings,tasks): clerk-manager-cashier account closure flow
Replaces the one-click Close action on the savings account page (removed from the Danger Zone menu) with a maker-checker flow, since closing an account pays real cash out of a teller's drawer. Clerk: an Action button on Transaction Entry, shown once an ACTIVE savings account is loaded. Ticking "Close this account" previews the payout -- balance plus interest calculated to today by the same code the closure itself runs -- and Save drops that figure into the slip's AMOUNT. The clerk then fills TOK. NO. on the slip and presses Save, which raises the request. Token and narration deliberately live on the slip, not in the popup, so the closure is entered like any other transaction. Manager: a Closure Requests tab in the Approval Center, reviewed in a popup dialog (matching Disbursement Requests) rather than a separate route. Cashier: the closure token is recognised at the counter alongside withdrawal and dividend tokens, auto-filling the rounded cash figure, locking it, and closing the account once the denomination is counted. Save-path fixes, all of which silently blocked a valid closure: - amount came from the denomination total whenever clerkMode was false -- which it is for any route other than the Clerk Dashboard wrapper -- so the calculated payout was replaced by zero - the balance check rejected the payout because it legitimately exceeds today's withdrawable amount: it includes interest close() posts before withdrawing. Min-balance, withdrawal-facility and clerk-side denomination checks do not apply to a closure either - on the cashier side the account is already CLOSED, so accountDetails is a stub with no balance and the same check reported "Withdrawable 0.00" - the closure branch had to precede the plain clerk-withdrawal branch: a closure is a full-balance withdrawal, so that branch would have swallowed it and the account would never have closed - onSave's one silent return now says what is missing, instead of leaving Save looking like a broken button Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -457,6 +457,21 @@ $ws-fld-bd: #c8d8eb;
|
||||
}
|
||||
}
|
||||
|
||||
&--action {
|
||||
background: linear-gradient(135deg, #6366f1, #4f46e5);
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2px 8px rgb(79 70 229 / 30%);
|
||||
|
||||
svg {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.08);
|
||||
}
|
||||
}
|
||||
|
||||
&--post {
|
||||
background: $ws-accent;
|
||||
color: #fff;
|
||||
@@ -2335,12 +2350,171 @@ select.te-cell {
|
||||
&--disbursement {
|
||||
background: linear-gradient(135deg, #0284c7, #0369a1);
|
||||
}
|
||||
|
||||
&--action {
|
||||
background: linear-gradient(135deg, #6366f1, #4f46e5);
|
||||
}
|
||||
}
|
||||
|
||||
.te-confirm-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
// ── Account Actions popup ─────────────────────────────────────────────────────
|
||||
.te-aa-box {
|
||||
width: 460px;
|
||||
}
|
||||
|
||||
.te-aa-account {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 10px 12px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&__no {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #1e40af;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #0d1b2a;
|
||||
}
|
||||
}
|
||||
|
||||
.te-aa-check {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
border: 1.5px solid #fecaca;
|
||||
background: #fef2f2;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s;
|
||||
|
||||
&:hover {
|
||||
border-color: #f87171;
|
||||
}
|
||||
|
||||
input {
|
||||
margin-top: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
accent-color: #dc2626;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 11.5px;
|
||||
color: #b91c1c;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.te-aa-calc {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #bbf7d0;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
|
||||
&--loading,
|
||||
&--error {
|
||||
display: block;
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
padding: 14px 12px;
|
||||
}
|
||||
|
||||
&--loading {
|
||||
background: #f8fafc;
|
||||
border-color: #e2e8f0;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
&--error {
|
||||
background: #fef2f2;
|
||||
border-color: #fecaca;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #166534;
|
||||
|
||||
&--total {
|
||||
margin-top: 4px;
|
||||
padding-top: 8px;
|
||||
border-top: 1.5px dashed #bbf7d0;
|
||||
font-weight: 800;
|
||||
font-size: 15px;
|
||||
color: #15803d;
|
||||
}
|
||||
}
|
||||
|
||||
&__note {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
line-height: 1.45;
|
||||
color: #4d7c56;
|
||||
}
|
||||
|
||||
&__warn {
|
||||
margin-top: 4px;
|
||||
padding: 7px 9px;
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fde68a;
|
||||
border-radius: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1.45;
|
||||
color: #92400e;
|
||||
}
|
||||
}
|
||||
|
||||
.te-aa-next {
|
||||
margin-top: 4px;
|
||||
padding: 9px 11px;
|
||||
background: #eef2ff;
|
||||
border: 1px solid #c7d2fe;
|
||||
border-radius: 7px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: #3730a3;
|
||||
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.te-confirm-body {
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
@@ -3107,6 +3281,21 @@ select.te-cell {
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 1px 4px rgb(16 185 129 / 12%);
|
||||
|
||||
/* A closure is destructive, so it must not read like the ordinary green "all good" banner. */
|
||||
&--closure {
|
||||
background: linear-gradient(135deg, #eef2ff, #f5f3ff);
|
||||
border-color: #a5b4fc;
|
||||
box-shadow: 0 1px 4px rgb(79 70 229 / 14%);
|
||||
|
||||
.ws-withdrawal-banner__icon {
|
||||
color: #4f46e5;
|
||||
}
|
||||
|
||||
.ws-withdrawal-banner__title {
|
||||
color: #3730a3;
|
||||
}
|
||||
}
|
||||
|
||||
&--loading {
|
||||
background: #f8fafc;
|
||||
border-color: #cbd5e1;
|
||||
|
||||
Reference in New Issue
Block a user