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.
|
reversed afterwards.
|
||||||
</p>
|
</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">
|
<div class="db-panel__actions">
|
||||||
<button type="button" class="pg-btn pg-btn--primary" (click)="submitClose()" [disabled]="!canSubmitClose">
|
<button type="button" class="pg-btn pg-btn--primary" (click)="submitClose()" [disabled]="!canSubmitClose">
|
||||||
{{ isLateClose ? 'Force Close Day' : 'Close Day' }}
|
{{ 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;
|
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 {
|
get canSubmitClose(): boolean {
|
||||||
const hasFigure = this.closingCash != null || this.closeDenomTotal > 0;
|
const hasFigure = this.closingCash != null || this.closeDenomTotal > 0;
|
||||||
if (!this.closeTarget || !hasFigure || this.closing) return false;
|
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();
|
return !this.isLateClose || !!this.forceReason.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
submitClose(): void {
|
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 day = this.closeTarget;
|
||||||
const denominations = this.denominationPayload(this.closeCounts);
|
const denominations = this.denominationPayload(this.closeCounts);
|
||||||
const figure = denominations.length ? this.closeDenomTotal : this.closingCash;
|
const figure = denominations.length ? this.closeDenomTotal : this.closingCash;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
export default {
|
export default {
|
||||||
'mifos_x': {
|
'mifos_x': {
|
||||||
'version': '260901',
|
'version': '260902',
|
||||||
'hash': '03f2f1d7'
|
'hash': '63134f17'
|
||||||
},
|
},
|
||||||
'allow_switching_backend_instance': true
|
'allow_switching_backend_instance': true
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user