fix(branchday): block day close when cashiers holding cash are not reconciled or settled
This commit is contained in:
@@ -580,6 +580,33 @@
|
||||
reversed afterwards.
|
||||
</p>
|
||||
|
||||
@if (hasUnreconciledOrUnsettledCashiers) {
|
||||
<div class="pg-alert pg-alert--error bd-reconcile-block">
|
||||
<mat-icon>block</mat-icon>
|
||||
<div>
|
||||
<strong>Cannot close the day — cashiers still holding cash have not been reconciled or settled.</strong>
|
||||
<ul class="bd-reconcile-block__list">
|
||||
@for (detail of unreconciledOrUnsettledCashierDetails; track detail.name) {
|
||||
<li>
|
||||
<strong>{{ detail.name }}</strong> —
|
||||
@if (detail.notReconciled && detail.notSettled) {
|
||||
Not Reconciled, Not Settled
|
||||
} @else if (detail.notReconciled) {
|
||||
Not Reconciled
|
||||
} @else {
|
||||
Not Settled
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<span class="db-muted"
|
||||
>Each cashier must reconcile their cash and settle it back to the vault before the branch day can be
|
||||
closed.</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="db-panel__actions">
|
||||
<button type="button" class="pg-btn pg-btn--primary" (click)="submitClose()" [disabled]="!canSubmitClose">
|
||||
{{ isLateClose ? 'Force Close Day' : 'Close Day' }}
|
||||
|
||||
@@ -295,3 +295,36 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Alert shown when cashiers holding cash have not been reconciled or settled — blocks day close. */
|
||||
.bd-reconcile-block {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
margin: 14px 0;
|
||||
padding: 12px 16px;
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
border-left: 4px solid #dc2626;
|
||||
border-radius: 8px;
|
||||
color: #991b1b;
|
||||
font-size: 12px;
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #dc2626;
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
&__list {
|
||||
margin: 6px 0 6px 16px;
|
||||
padding: 0;
|
||||
list-style: disc;
|
||||
font-size: 12px;
|
||||
color: #b91c1c;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,13 +539,53 @@ export class BranchDayComponent implements OnInit {
|
||||
return !!this.closeTarget && this.closeTarget.businessDate < this.today;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when any cashier who was given cash (currentTotal > 0) is still NOT reconciled
|
||||
* or NOT settled — meaning outstanding cash hasn't been accounted for yet.
|
||||
*/
|
||||
get hasUnreconciledOrUnsettledCashiers(): boolean {
|
||||
for (const t of this.eodTellers) {
|
||||
for (const c of t.cashiers) {
|
||||
if (c.currentTotal > 0 && (c.reconciled === false || c.alreadySettled === false)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Names of cashiers blocking the close, for the error message. */
|
||||
get unreconciledOrUnsettledCashierDetails(): { name: string; notReconciled: boolean; notSettled: boolean }[] {
|
||||
const result: { name: string; notReconciled: boolean; notSettled: boolean }[] = [];
|
||||
for (const t of this.eodTellers) {
|
||||
for (const c of t.cashiers) {
|
||||
if (c.currentTotal > 0 && (c.reconciled === false || c.alreadySettled === false)) {
|
||||
result.push({
|
||||
name: c.cashierName,
|
||||
notReconciled: c.reconciled === false,
|
||||
notSettled: c.alreadySettled === false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
get canSubmitClose(): boolean {
|
||||
const hasFigure = this.closingCash != null || this.closeDenomTotal > 0;
|
||||
if (!this.closeTarget || !hasFigure || this.closing) return false;
|
||||
// Block while cashier/vault data is still loading — the reconciliation check can't run yet.
|
||||
if (this.eodLoading || this.previewLoading) return false;
|
||||
if (this.hasUnreconciledOrUnsettledCashiers) return false;
|
||||
return !this.isLateClose || !!this.forceReason.trim();
|
||||
}
|
||||
|
||||
submitClose(): void {
|
||||
// Safety: re-check reconciliation even if the button was somehow enabled.
|
||||
if (this.hasUnreconciledOrUnsettledCashiers || this.eodLoading || this.previewLoading) {
|
||||
this.errorMsg = 'Cannot close — cashiers holding cash have not been reconciled or settled.';
|
||||
return;
|
||||
}
|
||||
const day = this.closeTarget;
|
||||
const denominations = this.denominationPayload(this.closeCounts);
|
||||
const figure = denominations.length ? this.closeDenomTotal : this.closingCash;
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
/* tslint:disable */
|
||||
export default {
|
||||
'mifos_x': {
|
||||
'version': '260901',
|
||||
'hash': '03f2f1d7'
|
||||
'version': '260902',
|
||||
'hash': '63134f17'
|
||||
},
|
||||
'allow_switching_backend_instance': true
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user