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:
@@ -0,0 +1,79 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title class="nd-title">
|
||||
Nominees
|
||||
<span class="nd-party">{{ data.partyCode }} · {{ data.partyName }}</span>
|
||||
</h2>
|
||||
|
||||
<mat-dialog-content class="nd-body">
|
||||
@if (nominees.length === 0) {
|
||||
<p class="nd-empty">Nobody is nominated on this account.</p>
|
||||
} @else {
|
||||
<p class="nd-mode">
|
||||
@if (bySequence) {
|
||||
Paid in the order listed — the first named who can be found receives the deposit.
|
||||
} @else {
|
||||
Split by share of the deposit.
|
||||
}
|
||||
</p>
|
||||
|
||||
<table class="nd-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
@if (bySequence) {
|
||||
Order
|
||||
} @else {
|
||||
#
|
||||
}
|
||||
</th>
|
||||
<th>Name</th>
|
||||
<th>Relation</th>
|
||||
<th>Nominated</th>
|
||||
@if (!bySequence) {
|
||||
<th class="nd-num">Share</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (nominee of nominees; track nominee.id; let i = $index) {
|
||||
<tr>
|
||||
<!-- The stated order where there is one; otherwise just the position in the list. -->
|
||||
<td class="nd-seq">{{ nominee.sequenceNo ?? i + 1 }}</td>
|
||||
<td>
|
||||
{{ nominee.name }}
|
||||
@if (nominee.minor) {
|
||||
<span class="nd-minor">minor · guardian {{ nominee.guardianName || '—' }}</span>
|
||||
}
|
||||
</td>
|
||||
<td>{{ nominee.relation }}</td>
|
||||
<td>{{ nominee.nominationDate | dateFormat }}</td>
|
||||
@if (!bySequence) {
|
||||
<td class="nd-num">
|
||||
@if (nominee.sharePercent !== null && nominee.sharePercent !== undefined) {
|
||||
{{ nominee.sharePercent }}%
|
||||
} @else {
|
||||
—
|
||||
}
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@if (shareWarning) {
|
||||
<p class="nd-warn">{{ shareWarning }}</p>
|
||||
}
|
||||
}
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-raised-button color="primary" mat-dialog-close>Close</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
.nd-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nd-party {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: rgb(0 0 0 / 55%);
|
||||
}
|
||||
|
||||
.nd-body {
|
||||
min-width: 420px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.nd-mode {
|
||||
margin: 0 0 12px;
|
||||
font-size: 13px;
|
||||
color: rgb(0 0 0 / 60%);
|
||||
}
|
||||
|
||||
.nd-empty {
|
||||
margin: 8px 0;
|
||||
color: rgb(0 0 0 / 55%);
|
||||
}
|
||||
|
||||
.nd-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 14px;
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: rgb(0 0 0 / 50%);
|
||||
padding: 0 8px 6px 0;
|
||||
border-bottom: 1px solid rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 8px 8px 8px 0;
|
||||
border-bottom: 1px solid rgb(0 0 0 / 6%);
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shares line up to be read down the column, not across the row. */
|
||||
.nd-num {
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.nd-seq {
|
||||
width: 32px;
|
||||
color: rgb(0 0 0 / 45%);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.nd-minor {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: rgb(0 0 0 / 50%);
|
||||
}
|
||||
|
||||
.nd-warn {
|
||||
margin: 12px 0 0;
|
||||
font-size: 13px;
|
||||
color: #b3261e;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
/** Custom Pipes */
|
||||
import { DateFormatPipe } from 'app/pipes/date-format.pipe';
|
||||
|
||||
/**
|
||||
* Who this account is nominated to, read without opening it for editing.
|
||||
*
|
||||
* A clerk asked "who is on this account" has one question, not an intention to change anything, and
|
||||
* the edit form answers it only after a load, a tab and a scroll - with every field live the whole
|
||||
* time. This states it and closes.
|
||||
*
|
||||
* What is shown follows the split the account was nominated under: a share per nominee under
|
||||
* Percentage, and the order they are paid in under Sequence, because that ordering is the whole of
|
||||
* what Sequence records.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-nominee-list-dialog',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatDialogModule,
|
||||
MatButtonModule,
|
||||
DateFormatPipe
|
||||
],
|
||||
templateUrl: './nominee-list-dialog.component.html',
|
||||
styleUrls: ['./nominee-list-dialog.component.scss']
|
||||
})
|
||||
export class NomineeListDialogComponent {
|
||||
/** { partyCode, partyName, splitMode, nominees } */
|
||||
data = inject<any>(MAT_DIALOG_DATA);
|
||||
|
||||
get bySequence(): boolean {
|
||||
return this.data?.splitMode === 'Sequence';
|
||||
}
|
||||
|
||||
get nominees(): any[] {
|
||||
return this.data?.nominees || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* What the shares come to. Shown only when it is not the whole deposit - a nomination recorded
|
||||
* before shares were captured reads as complete otherwise, and the remainder has nobody named
|
||||
* to receive it.
|
||||
*/
|
||||
get shareTotal(): number | null {
|
||||
if (this.bySequence) {
|
||||
return null;
|
||||
}
|
||||
const stated = this.nominees.filter((n) => n?.sharePercent !== null && n?.sharePercent !== undefined);
|
||||
if (stated.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return stated.reduce((sum, n) => sum + Number(n.sharePercent || 0), 0);
|
||||
}
|
||||
|
||||
get shareWarning(): string | null {
|
||||
if (this.bySequence) {
|
||||
// Under Sequence the order is the whole of the nomination, so an order nobody stated is the
|
||||
// same gap a missing share is under Percentage.
|
||||
const stated = this.nominees.filter((n) => n?.sequenceNo !== null && n?.sequenceNo !== undefined);
|
||||
return stated.length === this.nominees.length ? null : 'No order was recorded against these nominees.';
|
||||
}
|
||||
const total = this.shareTotal;
|
||||
if (total === null) {
|
||||
return 'No shares were recorded against these nominees.';
|
||||
}
|
||||
return total === 100 ? null : `The shares come to ${total}%, not 100%.`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user