DDS: Agent settlement (Agent Tran), Close Party account UI, reports UI, Party-Savings link field
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* 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 { UntypedFormBuilder, UntypedFormGroup, UntypedFormArray, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/** Custom Services */
|
||||
import { OrganizationService } from 'app/organization/organization.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
|
||||
/**
|
||||
* Daily Transaction Entry component.
|
||||
*
|
||||
* The screen a collection agent uses at the end of a day's round: pick a
|
||||
* transaction type, then batch-enter that day's collections against
|
||||
* assigned parties (Party Tran), settle collected cash to the branch
|
||||
* (Agent Tran), or record a withdrawal/transfer between accounts (Withdraw
|
||||
* Deposite). Modelled directly on the legacy Daily Transaction Detail
|
||||
* screen this replaces.
|
||||
*
|
||||
* Backed by the DDS Party Transaction API (`/dds/partytransactions`) for
|
||||
* Party Tran, Agent Tran and Withdraw Deposite - the three types this
|
||||
* screen actually collects fields for; Party Payment / OD Entry / OD
|
||||
* Payment remain placeholders until the backend distinguishes them too.
|
||||
* "Retrieve" (a ready-made list of today's assigned parties) isn't wired -
|
||||
* that needs a dedicated per-agent Party lookup this MVP doesn't have yet.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-daily-transaction-entry',
|
||||
templateUrl: './daily-transaction-entry.component.html',
|
||||
styleUrls: ['./daily-transaction-entry.component.scss'],
|
||||
imports: [...STANDALONE_SHARED_IMPORTS]
|
||||
})
|
||||
export class DailyTransactionEntryComponent {
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
private router = inject(Router);
|
||||
private organizationService = inject(OrganizationService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private dateUtils = inject(Dates);
|
||||
|
||||
private static readonly TRANSACTION_TYPE_CODES: Record<string, string> = {
|
||||
'Party Tran': 'PARTY_TRAN',
|
||||
'Agent Tran': 'AGENT_TRAN',
|
||||
'Withdraw Deposite': 'WITHDRAW_DEPOSITE'
|
||||
};
|
||||
|
||||
transactionTypes = [
|
||||
'Party Tran',
|
||||
'Agent Tran',
|
||||
'Party Payment',
|
||||
'OD Entry',
|
||||
'OD Payment',
|
||||
'Withdraw Deposite'
|
||||
];
|
||||
|
||||
transactionModes = [
|
||||
'Cash',
|
||||
'Transfer',
|
||||
'Clearing'
|
||||
];
|
||||
|
||||
entryForm: UntypedFormGroup = this.formBuilder.group({
|
||||
agentId: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
transactionType: [
|
||||
'Party Tran',
|
||||
Validators.required
|
||||
],
|
||||
transactionDate: [
|
||||
new Date(),
|
||||
Validators.required
|
||||
],
|
||||
// Withdraw Deposite fields
|
||||
creditorPartyCode: [''],
|
||||
creditorPartyName: [
|
||||
{
|
||||
value: '',
|
||||
disabled: true
|
||||
}
|
||||
],
|
||||
creditorPartyBalance: [
|
||||
{
|
||||
value: 0,
|
||||
disabled: true
|
||||
}
|
||||
],
|
||||
debitPartyCode: [''],
|
||||
debitPartyName: [
|
||||
{
|
||||
value: '',
|
||||
disabled: true
|
||||
}
|
||||
],
|
||||
debitPartyBalance: [
|
||||
{
|
||||
value: 0,
|
||||
disabled: true
|
||||
}
|
||||
],
|
||||
withdrawAmount: [0],
|
||||
withdrawDescription: [''],
|
||||
// Agent Tran fields
|
||||
settlementAmount: [0],
|
||||
settlementMode: ['Cash'],
|
||||
settlementAccountType: [''],
|
||||
settlementAccountNo: [''],
|
||||
settlementDescription: [''],
|
||||
collections: this.formBuilder.array([])
|
||||
});
|
||||
|
||||
agentInfo = {
|
||||
name: '',
|
||||
address: '',
|
||||
city: '',
|
||||
accountNo: '',
|
||||
guarantor1: '',
|
||||
guarantor2: '',
|
||||
currentBalance: 0
|
||||
};
|
||||
|
||||
get isPartyTran(): boolean {
|
||||
return this.entryForm.get('transactionType').value === 'Party Tran';
|
||||
}
|
||||
|
||||
get isWithdrawDeposite(): boolean {
|
||||
return this.entryForm.get('transactionType').value === 'Withdraw Deposite';
|
||||
}
|
||||
|
||||
get isAgentTran(): boolean {
|
||||
return this.entryForm.get('transactionType').value === 'Agent Tran';
|
||||
}
|
||||
|
||||
get collections(): UntypedFormArray {
|
||||
return this.entryForm.get('collections') as UntypedFormArray;
|
||||
}
|
||||
|
||||
private buildCollectionRow(): UntypedFormGroup {
|
||||
return this.formBuilder.group({
|
||||
partyCode: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
transactionMode: [
|
||||
'Cash',
|
||||
Validators.required
|
||||
],
|
||||
accountType: [''],
|
||||
accountNo: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
amount: [
|
||||
0,
|
||||
Validators.required
|
||||
],
|
||||
chequeNo: [''],
|
||||
tokenNo: [''],
|
||||
description: [''],
|
||||
partyName: [
|
||||
{
|
||||
value: '',
|
||||
disabled: true
|
||||
}
|
||||
],
|
||||
balanceAfterTransaction: [
|
||||
{
|
||||
value: 0,
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
addCollectionRow(): void {
|
||||
this.collections.push(this.buildCollectionRow());
|
||||
}
|
||||
|
||||
removeCollectionRow(index: number): void {
|
||||
this.collections.removeAt(index);
|
||||
}
|
||||
|
||||
loadAgent(): void {
|
||||
const agentId = this.entryForm.get('agentId')?.value;
|
||||
if (!agentId) {
|
||||
return;
|
||||
}
|
||||
this.organizationService.getAgent(agentId).subscribe((agent: any) => {
|
||||
const guarantors = agent.guarantors || [];
|
||||
this.agentInfo = {
|
||||
name: agent.agentNameId || '',
|
||||
address: agent.agentAddress1 || '',
|
||||
city: agent.agentCity || '',
|
||||
accountNo: agent.agentSavingAccountNo || '',
|
||||
guarantor1: guarantors[0]?.guarantorName || '',
|
||||
guarantor2: guarantors[1]?.guarantorName || '',
|
||||
currentBalance: agent.agentCurrentBalance || 0
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
retrieve(): void {
|
||||
// No "my assigned parties" read API exists yet — this will pre-fill
|
||||
// the collections grid with today's assigned parties once available.
|
||||
}
|
||||
|
||||
viewLedger(): void {
|
||||
// No ledger view exists yet for this agent.
|
||||
}
|
||||
|
||||
signature(): void {
|
||||
// No signature capture/verification flow exists yet.
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.entryForm.reset({
|
||||
transactionType: 'Party Tran',
|
||||
transactionDate: new Date()
|
||||
});
|
||||
this.collections.clear();
|
||||
}
|
||||
|
||||
exit(): void {
|
||||
this.router.navigate(['/accounts/savings']);
|
||||
}
|
||||
|
||||
save(): void {
|
||||
const raw = this.entryForm.getRawValue();
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const locale = this.settingsService.language.code;
|
||||
const transactionType = DailyTransactionEntryComponent.TRANSACTION_TYPE_CODES[raw.transactionType];
|
||||
|
||||
const payload: any = {
|
||||
agentId: Number(raw.agentId),
|
||||
transactionType,
|
||||
transactionDate: this.dateUtils.formatDate(raw.transactionDate, dateFormat),
|
||||
dateFormat,
|
||||
locale
|
||||
};
|
||||
|
||||
if (this.isPartyTran) {
|
||||
payload.collections = (raw.collections || []).map((row: any) => ({
|
||||
partyCode: row.partyCode,
|
||||
transactionMode: row.transactionMode,
|
||||
accountType: row.accountType || undefined,
|
||||
accountNo: row.accountNo,
|
||||
amount: row.amount,
|
||||
chequeNo: row.chequeNo || undefined,
|
||||
tokenNo: row.tokenNo || undefined,
|
||||
description: row.description || undefined
|
||||
}));
|
||||
} else if (this.isWithdrawDeposite) {
|
||||
payload.creditorPartyCode = raw.creditorPartyCode;
|
||||
payload.debitPartyCode = raw.debitPartyCode;
|
||||
payload.withdrawAmount = raw.withdrawAmount;
|
||||
payload.withdrawDescription = raw.withdrawDescription || undefined;
|
||||
} else if (this.isAgentTran) {
|
||||
payload.amount = raw.settlementAmount;
|
||||
payload.transactionMode = raw.settlementMode;
|
||||
payload.accountType = raw.settlementAccountType || undefined;
|
||||
payload.accountNo = raw.settlementAccountNo || undefined;
|
||||
payload.description = raw.settlementDescription || undefined;
|
||||
}
|
||||
|
||||
this.organizationService.createPartyTransaction(payload).subscribe(() => {
|
||||
this.collections.clear();
|
||||
this.entryForm.patchValue({
|
||||
creditorPartyCode: '',
|
||||
debitPartyCode: '',
|
||||
withdrawAmount: 0,
|
||||
withdrawDescription: '',
|
||||
settlementAmount: 0,
|
||||
settlementAccountType: '',
|
||||
settlementAccountNo: '',
|
||||
settlementDescription: ''
|
||||
});
|
||||
this.loadAgent();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user