Improve usability and validation in cash balance transfer (#2552)
* Improve usability and validation in cash balance transfer * Update changelog --------- Co-authored-by: Thomas <4159106+dtslvr@users.noreply.github.com>
This commit is contained in:
parent
379c651ce0
commit
20cefaba19
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved the usability and validation in the cash balance transfer from one to another account
|
||||||
|
|
||||||
## 2.15.0 - 2023-10-26
|
## 2.15.0 - 2023-10-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -190,36 +190,46 @@ export class AccountController {
|
|||||||
this.request.user.id
|
this.request.user.id
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentAccountIds = accountsOfUser.map(({ id }) => {
|
const accountFrom = accountsOfUser.find(({ id }) => {
|
||||||
return id;
|
return id === accountIdFrom;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
const accountTo = accountsOfUser.find(({ id }) => {
|
||||||
![accountIdFrom, accountIdTo].every((accountId) => {
|
return id === accountIdTo;
|
||||||
return currentAccountIds.includes(accountId);
|
});
|
||||||
})
|
|
||||||
) {
|
if (!accountFrom || !accountTo) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
getReasonPhrase(StatusCodes.NOT_FOUND),
|
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||||
StatusCodes.NOT_FOUND
|
StatusCodes.NOT_FOUND
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { currency } = accountsOfUser.find(({ id }) => {
|
if (accountFrom.id === accountTo.id) {
|
||||||
return id === accountIdFrom;
|
throw new HttpException(
|
||||||
});
|
getReasonPhrase(StatusCodes.BAD_REQUEST),
|
||||||
|
StatusCodes.BAD_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accountFrom.balance < balance) {
|
||||||
|
throw new HttpException(
|
||||||
|
getReasonPhrase(StatusCodes.BAD_REQUEST),
|
||||||
|
StatusCodes.BAD_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
await this.accountService.updateAccountBalance({
|
await this.accountService.updateAccountBalance({
|
||||||
currency,
|
accountId: accountFrom.id,
|
||||||
accountId: accountIdFrom,
|
|
||||||
amount: -balance,
|
amount: -balance,
|
||||||
|
currency: accountFrom.currency,
|
||||||
userId: this.request.user.id
|
userId: this.request.user.id
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.accountService.updateAccountBalance({
|
await this.accountService.updateAccountBalance({
|
||||||
currency,
|
accountId: accountTo.id,
|
||||||
accountId: accountIdTo,
|
|
||||||
amount: balance,
|
amount: balance,
|
||||||
|
currency: accountFrom.currency,
|
||||||
userId: this.request.user.id
|
userId: this.request.user.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<button
|
<button
|
||||||
class="align-items-center d-flex"
|
class="align-items-center d-flex"
|
||||||
mat-stroked-button
|
mat-stroked-button
|
||||||
|
[disabled]="dataSource?.data.length < 2"
|
||||||
(click)="onTransferBalance()"
|
(click)="onTransferBalance()"
|
||||||
>
|
>
|
||||||
<ion-icon class="mr-2" name="arrow-redo-outline"></ion-icon>
|
<ion-icon class="mr-2" name="arrow-redo-outline"></ion-icon>
|
||||||
|
@ -13,8 +13,8 @@ import { User } from '@ghostfolio/common/interfaces';
|
|||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import { Account as AccountModel } from '@prisma/client';
|
import { Account as AccountModel } from '@prisma/client';
|
||||||
import { DeviceDetectorService } from 'ngx-device-detector';
|
import { DeviceDetectorService } from 'ngx-device-detector';
|
||||||
import { Subject, Subscription } from 'rxjs';
|
import { EMPTY, Subject, Subscription } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { catchError, takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog/create-or-update-account-dialog.component';
|
import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog/create-or-update-account-dialog.component';
|
||||||
import { TransferBalanceDialog } from './transfer-balance/transfer-balance-dialog.component';
|
import { TransferBalanceDialog } from './transfer-balance/transfer-balance-dialog.component';
|
||||||
@ -283,7 +283,6 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
|
|||||||
data: {
|
data: {
|
||||||
accounts: this.accounts
|
accounts: this.accounts
|
||||||
},
|
},
|
||||||
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
|
|
||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -301,7 +300,14 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
|
|||||||
accountIdTo,
|
accountIdTo,
|
||||||
balance
|
balance
|
||||||
})
|
})
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(
|
||||||
|
catchError(() => {
|
||||||
|
alert($localize`Oops, transfer cash balance has failed.`);
|
||||||
|
|
||||||
|
return EMPTY;
|
||||||
|
}),
|
||||||
|
takeUntil(this.unsubscribeSubject)
|
||||||
|
)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.fetchAccounts();
|
this.fetchAccounts();
|
||||||
});
|
});
|
||||||
|
@ -4,7 +4,13 @@ import {
|
|||||||
Inject,
|
Inject,
|
||||||
OnDestroy
|
OnDestroy
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import {
|
||||||
|
AbstractControl,
|
||||||
|
FormBuilder,
|
||||||
|
FormGroup,
|
||||||
|
ValidationErrors,
|
||||||
|
Validators
|
||||||
|
} from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
|
import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
|
||||||
import { Account } from '@prisma/client';
|
import { Account } from '@prisma/client';
|
||||||
@ -35,11 +41,16 @@ export class TransferBalanceDialog implements OnDestroy {
|
|||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.accounts = this.data.accounts;
|
this.accounts = this.data.accounts;
|
||||||
|
|
||||||
this.transferBalanceForm = this.formBuilder.group({
|
this.transferBalanceForm = this.formBuilder.group(
|
||||||
balance: [0, Validators.required],
|
{
|
||||||
fromAccount: ['', Validators.required],
|
balance: ['', Validators.required],
|
||||||
toAccount: ['', Validators.required]
|
fromAccount: ['', Validators.required],
|
||||||
});
|
toAccount: ['', Validators.required]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validators: this.compareAccounts
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this.transferBalanceForm.get('fromAccount').valueChanges.subscribe((id) => {
|
this.transferBalanceForm.get('fromAccount').valueChanges.subscribe((id) => {
|
||||||
this.currency = this.accounts.find((account) => {
|
this.currency = this.accounts.find((account) => {
|
||||||
@ -66,4 +77,13 @@ export class TransferBalanceDialog implements OnDestroy {
|
|||||||
this.unsubscribeSubject.next();
|
this.unsubscribeSubject.next();
|
||||||
this.unsubscribeSubject.complete();
|
this.unsubscribeSubject.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private compareAccounts(control: AbstractControl): ValidationErrors {
|
||||||
|
const accountFrom = control.get('fromAccount');
|
||||||
|
const accountTo = control.get('toAccount');
|
||||||
|
|
||||||
|
if (accountFrom.value === accountTo.value) {
|
||||||
|
return { invalid: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user