- Add Balance Sheet report and disabled-features accounting config - Point menu-access tenant header and env.js/environment.ts tenant identifier at env config instead of hardcoded 'default'/'lincpay' literals - Surface the real backend error (not a generic "no reference id") when an OTP send fails in contact-otp-block - Fix branch-day "₹null" display bug (null vs undefined) and add a guard blocking a branch's first-ever day open before cash denomination migration - Dynamically size the denomination count input and block re-opening an already-open business day from Migrate Cash Denomination - Lock the denomination-managed cash GL row in Migrate Opening Balances (read-only, with a link back to Migrate Cash Denomination) - Remove duplicate Cancel/Submit action bars on the Share Product and Savings Account creation preview steps; align Previous into the same row - Hide "Apply Collection" on a share account once its advance collection is already settled (further purchases go through Apply Additional Shares) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
193 lines
7.5 KiB
HTML
193 lines
7.5 KiB
HTML
<!--
|
|
Copyright since 2025 Mifos Initiative
|
|
|
|
This Source Code Form is subject to the terms of the Mozilla Public
|
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
-->
|
|
|
|
<div class="container mcd">
|
|
<!-- Step 1: which branch's vault is being counted. -->
|
|
<mat-card class="mcd-card">
|
|
<mat-card-content>
|
|
<div class="mcd-intro">
|
|
<mat-icon>inventory_2</mat-icon>
|
|
<span>
|
|
Count the branch's opening cash into notes and coins. If the Cash GL account has no balance yet, this count
|
|
defines it; if it already carries one, the count must match it exactly. Either way, this also opens the
|
|
branch's first business day with the same count.
|
|
</span>
|
|
</div>
|
|
<div class="mcd-row">
|
|
<mat-form-field class="mcd-office">
|
|
<mat-label>Branch</mat-label>
|
|
<mat-select required [(ngModel)]="officeId" (selectionChange)="onOfficeChange()">
|
|
@for (office of officeData; track office) {
|
|
<mat-option [value]="office.id">{{ office.name }}</mat-option>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
<mat-form-field class="mcd-field" (click)="datePicker.open()">
|
|
<mat-label>Business Date</mat-label>
|
|
<input matInput [max]="maxDate" [matDatepicker]="datePicker" [(ngModel)]="businessDate" readonly />
|
|
<mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle>
|
|
<mat-datepicker #datePicker></mat-datepicker>
|
|
</mat-form-field>
|
|
</div>
|
|
</mat-card-content>
|
|
</mat-card>
|
|
|
|
@if (glLoading) {
|
|
<div class="mcd-loading">Checking the cash GL account…</div>
|
|
}
|
|
|
|
@if (glError) {
|
|
<div class="mcd-gl mcd-gl--error">
|
|
<mat-icon>error</mat-icon>
|
|
<div>{{ glError }}</div>
|
|
</div>
|
|
}
|
|
|
|
@if (glStatus && !glError) {
|
|
@if (todayAlreadyOpen) {
|
|
<!-- This screen is a one-time "day 1" tool - today's business day already exists, so counting
|
|
a drawer here would only be rejected at submit (BranchDayRegisterApiResource rejects
|
|
reopening an already-open day). Day Register is where every day after the first belongs. -->
|
|
<div class="mcd-gl mcd-gl--error">
|
|
<mat-icon>lock</mat-icon>
|
|
<div>
|
|
<div class="mcd-gl__label">Business Day Already Open</div>
|
|
<div class="mcd-gl__value">
|
|
{{ todayOpenInfo.businessDate }} — opened by {{ todayOpenInfo.openedByName }}, opening cash ₹{{
|
|
todayOpenInfo.openingCash | number: '1.2-2'
|
|
}}
|
|
</div>
|
|
<div class="mcd-gl__hint">
|
|
This branch has already opened today's business day — counting the drawer again here would only be rejected
|
|
on submit. Deposits, withdrawals and closing today's day happen from Day Register instead.
|
|
</div>
|
|
<button type="button" mat-stroked-button (click)="goToBranchDay()">Go to Day Register</button>
|
|
</div>
|
|
</div>
|
|
} @else {
|
|
<!-- Step 2: what the ledger already says, so the count is counted against something concrete. -->
|
|
<div class="mcd-gl" [class.mcd-gl--fresh]="!glHasBalance">
|
|
<mat-icon>account_balance</mat-icon>
|
|
<div>
|
|
<div class="mcd-gl__label">Cash GL Account</div>
|
|
<div class="mcd-gl__value">({{ glStatus.glCode }}) {{ glStatus.glAccountName }}</div>
|
|
@if (glHasBalance) {
|
|
<div class="mcd-gl__hint">
|
|
Already holds
|
|
<strong>₹{{ glTarget.toLocaleString('en-IN') }}</strong>
|
|
— count the drawer to exactly this amount.
|
|
</div>
|
|
} @else {
|
|
<div class="mcd-gl__hint">
|
|
No opening balance posted yet — whatever you count below becomes this account's opening balance.
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: the actual count. -->
|
|
<mat-card class="mcd-card mcd-card--flush">
|
|
<mat-card-content>
|
|
<div class="mcd-grid mcd-grid--head">
|
|
<div class="mcd-grid__denom">Denomination</div>
|
|
<div class="mcd-grid__count">Count</div>
|
|
<div class="mcd-grid__amt">Amount</div>
|
|
</div>
|
|
@for (d of allDenoms; track d.id) {
|
|
<div class="mcd-grid mcd-grid--row">
|
|
<div class="mcd-grid__denom">
|
|
{{ d.label }} <span class="mcd-grid__kind">{{ d.kind }}</span>
|
|
</div>
|
|
<div class="mcd-grid__count">
|
|
<button type="button" mat-icon-button (click)="bumpCount(d, -1)"><mat-icon>remove</mat-icon></button>
|
|
<input
|
|
type="number"
|
|
min="0"
|
|
class="mcd-grid__input"
|
|
[style.width.ch]="countInputWidthCh(d)"
|
|
[ngModel]="countFor(d)"
|
|
(ngModelChange)="onCountChange(d, $event)"
|
|
/>
|
|
<button type="button" mat-icon-button (click)="bumpCount(d, 1)"><mat-icon>add</mat-icon></button>
|
|
</div>
|
|
<div class="mcd-grid__amt">₹{{ subtotalFor(d).toLocaleString('en-IN') }}</div>
|
|
</div>
|
|
}
|
|
</mat-card-content>
|
|
</mat-card>
|
|
|
|
<!-- The one number that matters. -->
|
|
<div class="mcd-balance" [class.mcd-balance--ok]="isReconciled">
|
|
<div class="mcd-balance__figure">
|
|
<span class="mcd-balance__label">Counted Total</span>
|
|
<span class="mcd-balance__amt">₹{{ countedTotal.toLocaleString('en-IN') }}</span>
|
|
</div>
|
|
@if (glHasBalance) {
|
|
<div class="mcd-balance__figure">
|
|
<span class="mcd-balance__label">GL Target</span>
|
|
<span class="mcd-balance__amt">₹{{ glTarget.toLocaleString('en-IN') }}</span>
|
|
</div>
|
|
}
|
|
<div class="mcd-balance__status">
|
|
@if (countedTotal <= 0) {
|
|
<mat-icon>edit_note</mat-icon>
|
|
<span>Count each denomination above</span>
|
|
} @else if (isReconciled) {
|
|
<mat-icon>check_circle</mat-icon>
|
|
<span>{{ glHasBalance ? 'Matches the GL balance' : 'Will define the GL opening balance' }}</span>
|
|
} @else {
|
|
<mat-icon>error</mat-icon>
|
|
<span
|
|
>Off by ₹{{
|
|
(countedTotal - glTarget < 0 ? glTarget - countedTotal : countedTotal - glTarget).toLocaleString(
|
|
'en-IN'
|
|
)
|
|
}}</span
|
|
>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
@if (needsDifferenceReason) {
|
|
<mat-card class="mcd-card">
|
|
<mat-card-content>
|
|
<mat-form-field class="mcd-reason">
|
|
<mat-label>Why does the count differ from the GL balance?</mat-label>
|
|
<input matInput [(ngModel)]="differenceReason" />
|
|
</mat-form-field>
|
|
</mat-card-content>
|
|
</mat-card>
|
|
}
|
|
|
|
@if (errorMsg) {
|
|
<div class="mcd-error">{{ errorMsg }}</div>
|
|
}
|
|
|
|
@if (successMsg) {
|
|
<div class="mcd-success">
|
|
<mat-icon>check_circle</mat-icon>
|
|
<div>
|
|
{{ successMsg }}
|
|
<div class="mcd-success__actions">
|
|
<button type="button" mat-stroked-button (click)="goToBranchDay()">Go to Day Register</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (!successMsg) {
|
|
<div class="mcd-actions">
|
|
<button mat-raised-button color="primary" [disabled]="!canSubmit" (click)="submit()">
|
|
{{ submitting ? 'Submitting…' : 'Migrate & Open Business Day' }}
|
|
</button>
|
|
</div>
|
|
}
|
|
}
|
|
}
|
|
</div>
|