Merge branch 'feature/dds' into merge/dds-into-dev-latest

Brings the DDS (Daily Deposit Scheme) UI from feature/dds onto the
current dev-latest:
- Agent settlement (Agent Tran), Close Party account, DDS reports,
  Party-Savings link field
- Agent collection entry screen, per-agent GL account, maturity auto
  close
- Approvals tab, vouchers, payout & close, Sundry GL search, closure
  tab and fixes
- Menu: DDS Party Master, DDS Accounts, DDS Collection, DDS Reports and
  Locker links placed in the Deposits mega panel groups; Agent Master,
  Commission Slab and DDS Batch Processing under Organization; DDS
  Approvals tab in the checker inbox

No content conflicts. Nothing from dev-latest is removed.

Merge adjustments:
- package-lock.json: kept dev-latest's lock file. feature/dds does not
  add or change any dependency in package.json; its lock file was only
  regenerated (about 18k lines removed and 12k added), which would have
  changed resolved versions for packages already on dev-latest.
- Dropped src/app/deposits/party-master/CBS-Backend.code-workspace, a
  VS Code workspace file holding local folder paths, not app code.
- angular.json: bundle budget raised to 18mb warning / 20mb error, as on
  feature/dds.

Lint fixes required by the pre-commit hook (no behaviour change):
- Prettier formatting on 12 DDS files.
- create-party.component.scss: the dense data-entry section deliberately
  re-targets the base selectors to override them, so
  no-duplicate-selectors is disabled for that section instead of
  merging rules and changing the cascade.
- notice-dialog / create-dds-account-type scss: stylelint-disable-line
  for the "ch" width unit.
- commission-slab-master.component.scss: stylelint auto-fix.

Verified with tsc -p src/tsconfig.app.json --noEmit, stylelint, prettier
and eslint on all merged files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 15:10:37 +05:30
co-authored by Claude Opus 5.5
84 changed files with 17814 additions and 9 deletions
@@ -53,7 +53,18 @@
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.Value' | translate }}</th>
<td mat-cell *matCellDef="let configuration">{{ configuration.value }}</td>
<td mat-cell *matCellDef="let configuration">
{{ configuration.value }}
<!--
A setting that points at a ledger account holds only its id, which means nothing to the
officer checking that money is posting where it should. Named the way the product screens
name one, and shown beside the value rather than instead of it - the id is what gets
typed when the setting is edited.
-->
@if (glAccountLabel(configuration); as glAccount) {
<span class="gc-gl-name">{{ glAccount }}</span>
}
</td>
</ng-container>
<ng-container matColumnDef="stringValue">
@@ -9,3 +9,14 @@
table {
width: 100%;
}
/*
* The ledger account a setting points at. Quieter than the value itself, because the id is what the
* setting actually holds and what gets typed when it is edited - the name is here to say what that
* id means, not to replace it.
*/
.gc-gl-name {
margin-left: 8px;
color: rgb(0 0 0 / 60%);
font-size: 0.82rem;
}
@@ -29,6 +29,7 @@ import { ActivatedRoute, Router, RouterLink } from '@angular/router';
/** Custom Services */
import { SystemService } from '../../system.service';
import { AccountingService } from 'app/accounting/accounting.service';
import { PopoverService } from '../../../configuration-wizard/popover/popover.service';
import { ConfigurationWizardService } from '../../../configuration-wizard/configuration-wizard.service';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
@@ -71,9 +72,22 @@ export class GlobalConfigurationsTabComponent implements OnInit, AfterViewInit {
private router = inject(Router);
private configurationWizardService = inject(ConfigurationWizardService);
private popoverService = inject(PopoverService);
private accountingService = inject(AccountingService);
/** Configuration data. */
configurationData: any;
/**
* The chart of accounts, so a setting that holds a ledger account's id can be shown by its name.
*
* Several settings store nothing but a number - "44" says which account the money moves through,
* and says it only to the database. An officer checking that the scheme is posting where it should
* would otherwise have to carry the number to the Chart of Accounts and look it up.
*
* Left empty if the list cannot be read - a user without permission on the chart of accounts still
* sees every setting and its value, just without the name beside it.
*/
private glAccountsById = new Map<number, any>();
/** Columns to be displayed in configurations table. */
displayedColumns: string[] = [
'name',
@@ -119,6 +133,36 @@ export class GlobalConfigurationsTabComponent implements OnInit, AfterViewInit {
*/
ngOnInit() {
this.setConfigurationData();
this.loadGlAccounts();
}
private loadGlAccounts(): void {
this.accountingService.getGlAccounts().subscribe({
next: (accounts: any) => {
for (const account of accounts || []) {
this.glAccountsById.set(Number(account.id), account);
}
},
// The settings are the point of this screen; a chart of accounts that will not load must not
// take them down with it. The values still show, only the names beside them are missing.
error: () => this.glAccountsById.clear()
});
}
/**
* The ledger account a setting points at, as "name (code)" - the same way the product screens show
* one. Empty for every setting that does not hold a ledger account id, which is most of them.
*
* Only settings whose name marks them as an account mapping are resolved. Reading every numeric
* setting as an account id would put a name against figures that are nothing of the sort - a
* retry count of 3 would come back as whatever account 3 happens to be.
*/
glAccountLabel(configuration: any): string {
if (!configuration?.name?.endsWith('-account')) {
return '';
}
const account = this.glAccountsById.get(Number(configuration.value));
return account ? `${account.name} (${account.glCode})` : '';
}
/**