Files
cbsnexora/src/app/accounting/migrate-cash-denomination/migrate-cash-denomination.component.html
T
niteshandClaude Sonnet 5 7a2cb25538 feat(savings,shares): bidirectional savings/share account linking, new Migrate Cash Denomination screen
- Savings account creation shows a required "Default Share Account" picker once Share Linking is
  enabled, populated from the client's active share accounts; submits shareAccountId alongside it.
- Fixes a real data-loss bug on Share Account creation: the Charges step re-seeded its table from
  the product's default charges on every ngOnChanges firing (e.g. stepping back to Details and
  forward again), silently discarding a charge the clerk had already added, edited, or removed - a
  pristine guard existed for exactly this but wasn't checked in ngOnChanges. Now it is.
- Removes the hard "required" on Share Account creation's Default Savings Account field, matching
  the backend now accepting a share account with no linked savings account.
- New /accounting/migrate-cash-denomination screen: counts a branch's opening cash into notes and
  coins, checks it against (or defines, if unset) the Cash GL account's balance, and opens the
  branch's first business day with that same count - the vault-side counterpart to Migrate Opening
  Balances, kept under Accounting rather than Branch Ops/Cashier so it isn't reachable by mistake.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 22:18:33 +05:30

168 lines
6.1 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) {
<!-- 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"
[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>