Files
cbsnexora/src/app/accounting/migrate-cash-denomination/migrate-cash-denomination.component.html
T
niteshandClaude Sonnet 5 7b77166b19 fix: tenant-config fixes, cashier/share/savings UI bugs, and menu-access hardcoding
- 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>
2026-09-13 15:19:21 +05:30

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>