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>
116 lines
4.3 KiB
TypeScript
116 lines
4.3 KiB
TypeScript
/**
|
|
* 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 { FormsModule } from '@angular/forms';
|
|
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
|
import { MatButtonModule } from '@angular/material/button';
|
|
import { MatSelectModule } from '@angular/material/select';
|
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
import { MatInputModule } from '@angular/material/input';
|
|
|
|
/**
|
|
* Collecting for days other than today - either paid ahead, or owed from days gone by.
|
|
*
|
|
* Both are the same act from the counter's side: the customer is paying for a number of days, and
|
|
* what that comes to is the scheme's business, not the clerk's. So the clerk picks days and the
|
|
* amount follows. Typing an amount instead would let the daily figure be worked around a little at a
|
|
* time, and would quote the customer something the scheme does not charge.
|
|
*
|
|
* The two differ only in how many days are on offer. Paying ahead is capped by the bank's allowance.
|
|
* Paying arrears is capped by what is still inside the catch-up window - older instalments are gone,
|
|
* and offering them would have the clerk asking for money the bank has already written off.
|
|
*/
|
|
@Component({
|
|
selector: 'mifosx-day-collection-dialog',
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
FormsModule,
|
|
MatDialogModule,
|
|
MatButtonModule,
|
|
MatSelectModule,
|
|
MatFormFieldModule,
|
|
MatInputModule
|
|
],
|
|
templateUrl: './day-collection-dialog.component.html',
|
|
styleUrls: ['./day-collection-dialog.component.scss']
|
|
})
|
|
export class DayCollectionDialogComponent {
|
|
private dialogRef = inject(MatDialogRef<DayCollectionDialogComponent>);
|
|
/** { mode: 'advance' | 'previous', parties: [] } - the agent's parties, as the screen already has them. */
|
|
data = inject<any>(MAT_DIALOG_DATA);
|
|
|
|
partyCode = '';
|
|
days: number | '' = '';
|
|
|
|
get isAdvance(): boolean {
|
|
return this.data?.mode === 'advance';
|
|
}
|
|
|
|
get title(): string {
|
|
return this.isAdvance ? 'Collect in advance' : 'Collect previous days';
|
|
}
|
|
|
|
get party(): any {
|
|
return (this.data?.parties || []).find((p: any) => String(p.partyCode) === this.partyCode) || null;
|
|
}
|
|
|
|
/**
|
|
* The parties this can be used on.
|
|
*
|
|
* Paying ahead is offered to anyone. Paying arrears is offered only where there are arrears still
|
|
* inside the window - on an account that is up to date there is nothing to collect for past days,
|
|
* and listing it would invite an entry that is really an advance in disguise.
|
|
*/
|
|
get eligibleParties(): any[] {
|
|
const parties = this.data?.parties || [];
|
|
return this.isAdvance ? parties : parties.filter((p: any) => Number(p.recoverableAmount || 0) > 0);
|
|
}
|
|
|
|
/**
|
|
* How many days may be picked. Five ahead, matching the bank's allowance and the server's own
|
|
* check; for arrears, only what has not yet fallen out of the catch-up window.
|
|
*/
|
|
get dayOptions(): number[] {
|
|
const daily = Number(this.party?.dailyDepositAmount || 0);
|
|
if (!this.party || daily <= 0) {
|
|
return [];
|
|
}
|
|
const limit = this.isAdvance ? 5 : Math.floor(Number(this.party.recoverableAmount || 0) / daily);
|
|
return Array.from({ length: Math.max(0, limit) }, (_, index) => index + 1);
|
|
}
|
|
|
|
get amount(): number {
|
|
return Number(this.party?.dailyDepositAmount || 0) * Number(this.days || 0);
|
|
}
|
|
|
|
/** Clearing the days when the party changes - a count valid for one account is not for another. */
|
|
partyChanged(): void {
|
|
this.days = '';
|
|
}
|
|
|
|
get canAdd(): boolean {
|
|
return !!this.partyCode && Number(this.days) > 0 && this.amount > 0;
|
|
}
|
|
|
|
add(): void {
|
|
this.dialogRef.close({
|
|
partyCode: this.partyCode,
|
|
days: Number(this.days),
|
|
amount: this.amount,
|
|
// Sent as days, not just an amount: the server recomputes what they come to and checks it
|
|
// against this account, so the figure quoted here is the one that gets posted.
|
|
advanceDays: this.isAdvance ? Number(this.days) : undefined,
|
|
arrearsDays: this.isAdvance ? undefined : Number(this.days)
|
|
});
|
|
}
|
|
}
|