Merge pull request #1591 from josehernandezfintecheandomx/enhancement/loan_transaction_external_id

Loan transactions using external identifer
This commit is contained in:
Adam Saghy
2022-12-08 11:18:32 +01:00
committed by GitHub
17 changed files with 120 additions and 49 deletions
@@ -14,7 +14,7 @@
<mat-form-field fxFlex="48%">
<mat-label>Loan Term</mat-label>
<input matInput required formControlName="loanTermFrequency">
<input type="number" matInput required formControlName="loanTermFrequency">
<mat-error *ngIf="loansAccountTermsForm.controls.loanTermFrequency.hasError('required')">
Loan Term is <strong>required</strong>
</mat-error>
@@ -27,6 +27,11 @@
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>External Id</mat-label>
<input matInput formControlName="externalId">
</mat-form-field>
<mat-form-field>
<mat-label>Payment Type</mat-label>
<mat-select formControlName="paymentTypeId">
@@ -66,6 +66,7 @@ export class DisburseComponent implements OnInit {
this.disbursementLoanForm = this.formBuilder.group({
'actualDisbursementDate': [new Date(), Validators.required],
'transactionAmount': ['', Validators.required],
'externalId': '',
'paymentTypeId': '',
'note': ''
});
@@ -21,12 +21,17 @@
<mat-form-field>
<mat-label>Transaction Amount</mat-label>
<input matInput required formControlName="transactionAmount">
<input type="number" matInput required formControlName="transactionAmount">
<mat-error *ngIf="repaymentLoanForm.controls.transactionAmount.hasError('required')">
Transaction Amount <strong>is required</strong>
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>External Id</mat-label>
<input matInput formControlName="externalId">
</mat-form-field>
<mat-form-field>
<mat-label>Payment Type</mat-label>
<mat-select formControlName="paymentTypeId">
@@ -65,6 +65,7 @@ export class MakeRepaymentComponent implements OnInit {
this.repaymentLoanForm = this.formBuilder.group({
'transactionDate': [new Date(), Validators.required],
'transactionAmount': ['', Validators.required],
'externalId': '',
'paymentTypeId': '',
'note': ''
});
@@ -21,7 +21,7 @@
<mat-form-field>
<mat-label>Transaction Amount</mat-label>
<input matInput required formControlName="transactionAmount">
<input type="number" matInput required formControlName="transactionAmount">
<mat-error *ngIf="prepayLoanForm.controls.transactionAmount.hasError('required')">
Transaction Amount <strong>is required</strong>
</mat-error>
@@ -40,6 +40,11 @@
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>External Id</mat-label>
<input matInput formControlName="externalId">
</mat-form-field>
<mat-form-field>
<mat-label>Payment Type</mat-label>
<mat-select formControlName="paymentTypeId">
@@ -72,6 +72,7 @@ export class PrepayLoanComponent implements OnInit {
'transactionAmount': ['', Validators.required],
'principal': [{value: '', disabled: true}],
'interestAmount': [{value: '', disabled: true}, Validators.required],
'externalId': [''],
'paymentTypeId': [''],
'note': ['']
});
@@ -33,9 +33,11 @@
<h3> Loan Account OverView </h3>
<span>Current Balance: {{loanDetailsData.currency.displaySymbol}}
{{loanDetailsData.summary.totalOutstanding | number}}</span><br>
<span>Arrears By: {{loanDetailsData.summary.totalOverdue | number}}
<span *ngIf="loanDetailsData.inArrears">Arrears By: {{loanDetailsData.summary.totalOverdue | number}}
<span *ngIf="!(loanDetailsData.summary.totalOverdue>=0)">Not Provided</span>
</span><br>
<span *ngIf="loanDetailsData.inArrears">Arrears Since: {{loanDetailsData.summary.overdueSinceDate | dateFormat}}
</span><br>
</div>
</div>
@@ -22,6 +22,13 @@
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.officeName }} </td>
</ng-container>
<ng-container matColumnDef="externalId">
<th mat-header-cell *matHeaderCellDef> External Id </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)">
<mifosx-external-identifier externalId="{{transaction.externalId}}"></mifosx-external-identifier>
</td>
</ng-container>
<ng-container matColumnDef="transactionDate">
<th mat-header-cell *matHeaderCellDef> Transaction Date </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.date | dateFormat }} </td>
@@ -33,38 +40,38 @@
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef> Amount </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.amount | number }} </td>
<th mat-header-cell class="center" *matHeaderCellDef> Amount </th>
<td mat-cell class="r-amount td-min-space" *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.amount | number }} </td>
</ng-container>
<ng-container matColumnDef="principal">
<th mat-header-cell *matHeaderCellDef> Breakdown </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.principalPortion | number }} </td>
<th mat-header-cell class="center" *matHeaderCellDef> Breakdown </th>
<td mat-cell class="r-amount td-min-space" *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.principalPortion | number }} </td>
</ng-container>
<ng-container matColumnDef="interest">
<th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.interestPortion | number }} </td>
<td mat-cell class="r-amount td-min-space" *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.interestPortion | number }} </td>
</ng-container>
<ng-container matColumnDef="fee">
<th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.feeChargesPortion | number }} </td>
<td mat-cell class="r-amount td-min-space" *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.feeChargesPortion | number }} </td>
</ng-container>
<ng-container matColumnDef="penalties">
<th mat-header-cell *matHeaderCellDef> </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.penaltyChargesPortion | number }} </td>
<td mat-cell class="r-amount td-min-space" *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.penaltyChargesPortion | number }} </td>
</ng-container>
<ng-container matColumnDef="loanBalance">
<th mat-header-cell *matHeaderCellDef> Loan Balance </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.outstandingLoanBalance | number }} </td>
<th mat-header-cell class="r-amount" *matHeaderCellDef> Loan Balance </th>
<td mat-cell class="r-amount td-min-space" *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)"> {{ transaction.outstandingLoanBalance | number }} </td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let transaction" [ngClass]="loanTransactionColor(transaction)">
<th mat-header-cell class="center" *matHeaderCellDef> Actions </th>
<td mat-cell *matCellDef="let transaction" class="center td-min-space">
<button class="account-action-button" *ngIf="!transaction.manuallyReversed" mat-raised-button color="primary" (click)="routeEdit($event)"
[routerLink]="[transaction.id, 'reciept']" matTooltip="View Receipts">
<i class="fa fa-file-text"></i>
@@ -78,27 +85,27 @@
<!-- Sub header -->
<ng-container matColumnDef="header">
<th mat-header-cell *matHeaderCellDef [attr.colspan]="5"> </th>
<th mat-header-cell *matHeaderCellDef [attr.colspan]="6"> </th>
</ng-container>
<ng-container matColumnDef="header-principal">
<th mat-header-cell *matHeaderCellDef [attr.colspan]="1"> Principal </th>
<th mat-header-cell class="center" *matHeaderCellDef [attr.colspan]="1"> Principal </th>
</ng-container>
<ng-container matColumnDef="header-interest">
<th mat-header-cell *matHeaderCellDef [attr.colspan]="1"> Interest </th>
<th mat-header-cell class="center" *matHeaderCellDef [attr.colspan]="1"> Interest </th>
</ng-container>
<ng-container matColumnDef="header-fees">
<th mat-header-cell *matHeaderCellDef [attr.colspan]="1"> Fees </th>
<th mat-header-cell class="center" *matHeaderCellDef [attr.colspan]="1"> Fees </th>
</ng-container>
<ng-container matColumnDef="header-penalties">
<th mat-header-cell *matHeaderCellDef [attr.colspan]="1"> Penalties </th>
<th mat-header-cell class="center" *matHeaderCellDef [attr.colspan]="1"> Penalties </th>
</ng-container>
<ng-container matColumnDef="header-action">
<th mat-header-cell *matHeaderCellDef [attr.colspan]="2"> </th>
<th mat-header-cell *matHeaderCellDef [attr.colspan]="1"> </th>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
@@ -16,7 +16,7 @@
.account-action-button{
min-width: 26px;
padding: 0 0 3px 0;
margin: 0 10%;
margin: 0 2%;
line-height: 25px;
}
.select-row:hover {
@@ -32,3 +32,6 @@
}
}
.td-min-space {
min-width: 60px;
}
@@ -20,7 +20,7 @@ export class TransactionsTabComponent implements OnInit {
/** Stores the status of the loan account */
status: string;
/** Columns to be displayed in original schedule table. */
displayedColumns: string[] = ['id', 'office', 'transactionDate', 'transactionType', 'amount', 'principal', 'interest', 'fee', 'penalties', 'loanBalance', 'actions'];
displayedColumns: string[] = ['id', 'office', 'externalId', 'transactionDate', 'transactionType', 'amount', 'principal', 'interest', 'fee', 'penalties', 'loanBalance', 'actions'];
/**
* Retrieves the loans with associations data from `resolve`.
@@ -25,6 +25,11 @@
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>External Id</mat-label>
<input matInput formControlName="externalId">
</mat-form-field>
<mat-form-field>
<mat-label>Payment Type</mat-label>
<mat-select formControlName="paymentTypeId">
@@ -70,6 +70,7 @@ export class EditTransactionComponent implements OnInit {
this.editTransactionForm.patchValue({
'transactionDate': this.transactionTemplateData.date && new Date(this.transactionTemplateData.date),
'transactionAmount': this.transactionTemplateData.amount,
'externalId': this.transactionTemplateData.externalId,
'paymentTypeId': this.transactionTemplateData.paymentTypeId
});
}
@@ -81,6 +82,7 @@ export class EditTransactionComponent implements OnInit {
this.editTransactionForm = this.formBuilder.group({
'transactionDate': ['', Validators.required],
'transactionAmount': ['', Validators.required],
'externalId': [''],
'paymentTypeId': [''],
});
}
@@ -76,6 +76,14 @@
{{ transactionData.amount | currency:transactionData.currency.code }} {{ transactionData.currency.code }}
</div>
<div fxFlex="50%" class="mat-body-strong" *ngIf="transactionData.externalId">
{{ 'External Id' | translate }}
</div>
<div fxFlex="50%" *ngIf="transactionData.externalId">
{{ transactionData.externalId }}
</div>
<mat-divider *ngIf="existTransactionRelations" [inset]="true"></mat-divider>
<div fxFlex="100%" class="mat-body-strong" *ngIf="existTransactionRelations">
@@ -66,17 +66,20 @@ export class ViewTransactionComponent implements OnInit {
private organizationService: OrganizationService) {
this.route.data.subscribe((data: { loansAccountTransaction: any }) => {
this.transactionData = data.loansAccountTransaction;
this.allowEdition = !this.transactionData.manuallyReversed && !(this.transactionData.type.id === 20 || this.transactionData.type.id === 21 || this.transactionData.type.id === 22
|| this.transactionData.type.id === 23);
this.allowEdition = !this.transactionData.manuallyReversed && !this.allowTransactionEdition(this.transactionData.type.id);
this.allowUndo = !this.transactionData.manuallyReversed;
this.allowChargeback = this.transactionData.type.repayment;
let transactionsChargebackRelated = false;
if (this.transactionData.type.repayment) {
if (this.transactionData.transactionRelations) {
this.transactionRelations.data = this.transactionData.transactionRelations;
this.existTransactionRelations = (this.transactionData.transactionRelations.length > 0);
let amountRelations = 0;
this.transactionData.transactionRelations.forEach((relation: any) => {
amountRelations += relation.amount;
if (relation.relationType === 'CHARGEBACK') {
amountRelations += relation.amount;
transactionsChargebackRelated = true;
}
});
this.amountRelationsAllowed = this.transactionData.amount - amountRelations;
this.isFullRelated = (this.amountRelationsAllowed === 0);
@@ -86,7 +89,7 @@ export class ViewTransactionComponent implements OnInit {
if (!this.allowChargeback) {
this.allowEdition = false;
}
if (this.existTransactionRelations) {
if (this.existTransactionRelations && transactionsChargebackRelated) {
this.allowUndo = false;
}
});
@@ -103,6 +106,15 @@ export class ViewTransactionComponent implements OnInit {
}
}
/**
* Allow edit, undo and chargeback actions
*/
allowTransactionEdition(transactionType: number): boolean {
return (transactionType === 20
|| transactionType === 21 || transactionType === 22
|| transactionType === 23);
}
/**
* Undo the loans transaction
*/
@@ -1,25 +1,29 @@
<div *ngIf="loansAccountData.status.value === 'Active' && chargeData.amountOutstanding !== 0"
fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="accent" *mifosxHasPermission="'PAY_SAVINGSACCOUNTCHARGE'" (click)="payCharge()">
<fa-icon icon="dollar-sign"></fa-icon>&nbsp;&nbsp;
Pay
</button>
<button mat-raised-button color="primary" *mifosxHasPermission="'WAIVE_SAVINGSACCOUNTCHARGE'" (click)="waiveCharge()">
<fa-icon icon="flag"></fa-icon>&nbsp;&nbsp;
Waive
</button>
<div *ngIf="loansAccountData.status.value === 'Active' && chargeData.amountOutstanding !== 0" fxLayout="row"
fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" *mifosxHasPermission="'WAIVE_SAVINGSACCOUNTCHARGE'" (click)="editCharge()">
<fa-icon icon="flag"></fa-icon>&nbsp;&nbsp;
Edit
</button>
<button mat-raised-button color="accent" *mifosxHasPermission="'PAY_SAVINGSACCOUNTCHARGE'" (click)="payCharge()">
<fa-icon icon="dollar-sign"></fa-icon>&nbsp;&nbsp;
Pay
</button>
<button mat-raised-button color="primary" *mifosxHasPermission="'WAIVE_SAVINGSACCOUNTCHARGE'" (click)="waiveCharge()">
<fa-icon icon="flag"></fa-icon>&nbsp;&nbsp;
Waive
</button>
</div>
<div *ngIf="loansAccountData.status.value === 'Submitted and pending approval'"
fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" *mifosxHasPermission="'UPDATE_SAVINGSACCOUNTCHARGE'" (click)="editCharge()">
<fa-icon icon="edit"></fa-icon>&nbsp;&nbsp;
Edit
</button>
<button mat-raised-button color="warn" *mifosxHasPermission="'DELETE_SAVINGSACCOUNTCHARGE'" (click)="deleteCharge()">
<fa-icon icon="trash"></fa-icon>&nbsp;&nbsp;
Delete
</button>
<div *ngIf="loansAccountData.status.value === 'Submitted and pending approval'" fxLayout="row" fxLayoutAlign="end"
fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" *mifosxHasPermission="'UPDATE_SAVINGSACCOUNTCHARGE'" (click)="editCharge()">
<fa-icon icon="edit"></fa-icon>&nbsp;&nbsp;
Edit
</button>
<button mat-raised-button color="warn" *mifosxHasPermission="'DELETE_SAVINGSACCOUNTCHARGE'" (click)="deleteCharge()">
<fa-icon icon="trash"></fa-icon>&nbsp;&nbsp;
Delete
</button>
</div>
<div class="container">
@@ -67,7 +71,7 @@
</div>
<div fxFlex="50%">
{{ chargeData.dueDate | dateFormat }}
{{ chargeData.dueDate | dateFormat }}
</div>
<div fxFlex="50%" class="mat-body-strong">
@@ -124,6 +124,13 @@ export class ViewChargeComponent {
value: this.chargeData.amount || this.chargeData.amountOrPercentage,
type: 'number',
required: true
}),
new DatepickerBase({
controlName: 'dueDate',
label: 'Due Date',
value: new Date(this.chargeData.dueDate),
type: 'date',
required: true
})
];
const data = {
@@ -136,8 +143,11 @@ export class ViewChargeComponent {
if (response.data) {
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const dueDate = this.dateUtils.formatDate(response.data.value.dueDate, dateFormat);
const amount = response.data.value.amount;
const dataObject = {
...response.data.value,
amount,
dueDate,
dateFormat,
locale
};