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>
This commit is contained in:
2026-09-13 15:19:21 +05:30
co-authored by Claude Sonnet 5
parent 42424dff8b
commit 7b77166b19
39 changed files with 1800 additions and 266 deletions
@@ -66,6 +66,14 @@ export class MigrateCashDenominationComponent implements OnInit {
glStatus: any = null;
glError = '';
/** Set once per office change: whether TODAY's business day is already open for this office. This
* screen is a one-time "day 1" tool (see the class doc) - once a day has actually been opened,
* reopening the same one is exactly what BranchDayRegisterApiResource#migrateCashDenomination
* itself refuses (its own openDayInternal call rejects a day that is already open), so this is
* checked here too, before the user counts an entire drawer only to have the submit rejected. */
todayAlreadyOpen = false;
todayOpenInfo: any = null;
notes: Denom[] = [];
coins: Denom[] = [];
counts: Record<number, number> = {};
@@ -118,6 +126,8 @@ export class MigrateCashDenominationComponent implements OnInit {
this.result = null;
this.successMsg = '';
this.errorMsg = '';
this.todayAlreadyOpen = false;
this.todayOpenInfo = null;
if (!this.officeId) return;
this.glLoading = true;
@@ -131,6 +141,15 @@ export class MigrateCashDenominationComponent implements OnInit {
this.glError = res.message || 'No cash GL account is configured for this tenant yet.';
}
});
this.service
.branchDayRegister(this.officeId)
.pipe(catchError(() => of(null)))
.subscribe((res: any) => {
const openDay = res?.openDay;
this.todayAlreadyOpen = !!openDay && openDay.businessDate === res?.today;
this.todayOpenInfo = this.todayAlreadyOpen ? openDay : null;
});
}
countFor(d: Denom): number {
@@ -149,6 +168,12 @@ export class MigrateCashDenominationComponent implements OnInit {
this.counts[d.id] = Math.max(0, (this.counts[d.id] || 0) + delta);
}
/** Widens the count box as the typed count grows more digits, so a large count (hundreds,
* thousands) is never clipped or hard to read inside a field sized for one or two digits. */
countInputWidthCh(d: Denom): number {
return Math.max(3, String(this.countFor(d)).length + 2);
}
get countedTotal(): number {
return this.allDenoms.reduce((s, d) => s + this.subtotalFor(d), 0);
}