Prepare for multi accounts support: store account for new transactions (#46)

pull/47/head
Thomas 3 years ago committed by GitHub
parent ef317a86ed
commit 8cb5fd64dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
- Prepared further for multi accounts support: store account for new transactions
### Fixed
- Fixed an issue in the header with outdated data

@ -1,3 +1,6 @@
import { Settings, User } from '@prisma/client';
import { Account, Settings, User } from '@prisma/client';
export type UserWithSettings = User & { Settings: Settings };
export type UserWithSettings = User & {
Account: Account[];
Settings: Settings;
};

@ -2,6 +2,9 @@ import { Currency, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
export class CreateOrderDto {
@IsString()
accountId: string;
@IsString()
currency: Currency;

@ -118,6 +118,9 @@ export class OrderController {
const date = parseISO(data.date);
const accountId = data.accountId;
delete data.accountId;
if (data.platformId) {
const platformId = data.platformId;
delete data.platformId;
@ -126,6 +129,11 @@ export class OrderController {
{
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
Platform: { connect: { id: platformId } },
User: { connect: { id: this.request.user.id } }
},
@ -138,6 +146,11 @@ export class OrderController {
{
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
User: { connect: { id: this.request.user.id } }
},
this.request.user.id
@ -169,6 +182,9 @@ export class OrderController {
const date = parseISO(data.date);
const accountId = data.accountId;
delete data.accountId;
if (data.platformId) {
const platformId = data.platformId;
delete data.platformId;
@ -178,6 +194,11 @@ export class OrderController {
data: {
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
Platform: { connect: { id: platformId } },
User: { connect: { id: this.request.user.id } }
},
@ -199,6 +220,11 @@ export class OrderController {
data: {
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
Platform: originalOrder.platformId
? { disconnect: true }
: undefined,

@ -2,6 +2,9 @@ import { Currency, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
export class UpdateOrderDto {
@IsString()
accountId: string;
@IsString()
currency: Currency;

@ -1,9 +1,10 @@
import { Currency } from '@prisma/client';
import { Account, Currency } from '@prisma/client';
import { Access } from './access.interface';
export interface User {
access: Access[];
accounts: Account[];
alias?: string;
id: string;
permissions: string[];

@ -25,6 +25,7 @@ export class UserService {
) {}
public async getUser({
Account,
alias,
id,
role,
@ -53,6 +54,7 @@ export class UserService {
id: accessItem.id
};
}),
accounts: Account,
permissions: currentPermissions,
settings: {
baseCurrency: Settings?.currency || UserService.DEFAULT_CURRENCY,
@ -69,7 +71,7 @@ export class UserService {
userWhereUniqueInput: Prisma.UserWhereUniqueInput
): Promise<UserWithSettings | null> {
const user = await this.prisma.user.findUnique({
include: { Settings: true },
include: { Account: true, Settings: true },
where: userWhereUniqueInput
});

@ -8,7 +8,7 @@ import { FormControl, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { Currency, Order as OrderModel } from '@prisma/client';
import { Currency } from '@prisma/client';
import { Observable, Subject } from 'rxjs';
import {
debounceTime,
@ -19,6 +19,7 @@ import {
} from 'rxjs/operators';
import { DataService } from '../../../services/data.service';
import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces';
@Component({
host: { class: 'h-100' },
@ -33,7 +34,7 @@ export class CreateOrUpdateTransactionDialog {
public isLoading = false;
public platforms: { id: string; name: string }[];
public searchSymbolCtrl = new FormControl(
this.data.symbol,
this.data.transaction.symbol,
Validators.required
);
@ -43,7 +44,7 @@ export class CreateOrUpdateTransactionDialog {
private cd: ChangeDetectorRef,
private dataService: DataService,
public dialogRef: MatDialogRef<CreateOrUpdateTransactionDialog>,
@Inject(MAT_DIALOG_DATA) public data: OrderModel
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateTransactionDialogParams
) {}
ngOnInit() {
@ -72,14 +73,14 @@ export class CreateOrUpdateTransactionDialog {
public onUpdateSymbol(event: MatAutocompleteSelectedEvent) {
this.isLoading = true;
this.data.symbol = event.option.value;
this.data.transaction.symbol = event.option.value;
this.dataService
.fetchSymbolItem(this.data.symbol)
.fetchSymbolItem(this.data.transaction.symbol)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currency, marketPrice }) => {
this.data.currency = currency;
this.data.unitPrice = marketPrice;
this.data.transaction.currency = currency;
this.data.transaction.unitPrice = marketPrice;
this.isLoading = false;
@ -88,10 +89,10 @@ export class CreateOrUpdateTransactionDialog {
}
public onUpdateSymbolByTyping(value: string) {
this.data.currency = null;
this.data.unitPrice = null;
this.data.transaction.currency = null;
this.data.transaction.unitPrice = null;
this.data.symbol = value;
this.data.transaction.symbol = value;
}
public ngOnDestroy() {

@ -1,6 +1,6 @@
<form #addTransactionForm="ngForm" class="d-flex flex-column h-100">
<h1 *ngIf="data.id" mat-dialog-title i18n>Update transaction</h1>
<h1 *ngIf="!data.id" mat-dialog-title i18n>Add transaction</h1>
<h1 *ngIf="data.transaction.id" mat-dialog-title i18n>Update transaction</h1>
<h1 *ngIf="!data.transaction.id" mat-dialog-title i18n>Add transaction</h1>
<div class="flex-grow-1" mat-dialog-content>
<div>
<mat-form-field appearance="outline" class="w-100">
@ -36,7 +36,7 @@
<div>
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Type</mat-label>
<mat-select name="type" required [(value)]="data.type">
<mat-select name="type" required [(value)]="data.transaction.type">
<mat-option value="BUY" i18n> BUY </mat-option>
<mat-option value="SELL" i18n> SELL </mat-option>
</mat-select>
@ -50,7 +50,7 @@
disabled
name="currency"
required
[(value)]="data.currency"
[(value)]="data.transaction.currency"
>
<mat-option *ngFor="let currency of currencies" [value]="currency"
>{{ currency }}</mat-option
@ -67,7 +67,7 @@
name="date"
required
[matDatepicker]="date"
[(ngModel)]="data.date"
[(ngModel)]="data.transaction.date"
/>
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
<mat-datepicker #date disabled="false"></mat-datepicker>
@ -81,7 +81,7 @@
name="fee"
required
type="number"
[(ngModel)]="data.fee"
[(ngModel)]="data.transaction.fee"
/>
</mat-form-field>
</div>
@ -93,7 +93,7 @@
name="quantity"
required
type="number"
[(ngModel)]="data.quantity"
[(ngModel)]="data.transaction.quantity"
/>
</mat-form-field>
</div>
@ -105,14 +105,28 @@
name="unitPrice"
required
type="number"
[(ngModel)]="data.unitPrice"
[(ngModel)]="data.transaction.unitPrice"
/>
</mat-form-field>
</div>
<div class="d-none">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Account</mat-label>
<mat-select
name="accountId"
required
[(value)]="data.transaction.accountId"
>
<mat-option *ngFor="let account of data.accounts" [value]="account.id"
>{{ account.name }}</mat-option
>
</mat-select>
</mat-form-field>
</div>
<div>
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Platform</mat-label>
<mat-select name="platformId" [(value)]="data.platformId">
<mat-select name="platformId" [(value)]="data.transaction.platformId">
<mat-option [value]="null"></mat-option>
<mat-option *ngFor="let platform of platforms" [value]="platform.id"
>{{ platform.name }}</mat-option
@ -127,7 +141,7 @@
color="primary"
i18n
mat-flat-button
[disabled]="!(addTransactionForm.form.valid && data.symbol)"
[disabled]="!(addTransactionForm.form.valid && data.transaction.symbol)"
[mat-dialog-close]="data"
>
Save

@ -0,0 +1,8 @@
import { Order } from '../../interfaces/order.interface';
import { Account } from '@prisma/client';
export interface CreateOrUpdateTransactionDialogParams {
accountId: string;
accounts: Account[];
transaction: Order;
}

@ -1,8 +1,11 @@
export interface Order {
accountId: string;
currency: string;
date: Date;
fee: number;
id: string;
quantity: number;
platformId: string;
symbol: string;
type: string;
unitPrice: number;

@ -122,25 +122,43 @@ export class TransactionsPageComponent implements OnInit {
});
}
private openCreateTransactionDialog(): void {
public openUpdateTransactionDialog({
accountId,
currency,
date,
fee,
id,
platformId,
quantity,
symbol,
type,
unitPrice
}: OrderModel): void {
const dialogRef = this.dialog.open(CreateOrUpdateTransactionDialog, {
data: {
currency: null,
date: new Date(),
fee: 0,
platformId: null,
quantity: null,
symbol: null,
type: 'BUY',
unitPrice: null
accounts: this.user.accounts,
transaction: {
accountId,
currency,
date,
fee,
id,
platformId,
quantity,
symbol,
type,
unitPrice
}
},
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((order: UpdateOrderDto) => {
if (order) {
this.dataService.postOrder(order).subscribe({
dialogRef.afterClosed().subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction;
if (transaction) {
this.dataService.putOrder(transaction).subscribe({
next: () => {
this.fetchOrders();
}
@ -151,36 +169,38 @@ export class TransactionsPageComponent implements OnInit {
});
}
public openUpdateTransactionDialog({
currency,
date,
fee,
id,
platformId,
quantity,
symbol,
type,
unitPrice
}: OrderModel): void {
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private openCreateTransactionDialog(): void {
const dialogRef = this.dialog.open(CreateOrUpdateTransactionDialog, {
data: {
currency,
date,
fee,
id,
platformId,
quantity,
symbol,
type,
unitPrice
accounts: this.user.accounts,
transaction: {
accountId: this.user.accounts.find((account) => {
return account.isDefault;
})?.id,
currency: null,
date: new Date(),
fee: 0,
platformId: null,
quantity: null,
symbol: null,
type: 'BUY',
unitPrice: null
}
},
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((order: UpdateOrderDto) => {
if (order) {
this.dataService.putOrder(order).subscribe({
dialogRef.afterClosed().subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction;
if (transaction) {
this.dataService.postOrder(transaction).subscribe({
next: () => {
this.fetchOrders();
}
@ -190,9 +210,4 @@ export class TransactionsPageComponent implements OnInit {
this.router.navigate(['.'], { relativeTo: this.route });
});
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

@ -82,6 +82,16 @@ async function main() {
create: {
accessToken:
'c689bcc894e4a420cb609ee34271f3e07f200594f7d199c50d75add7102889eb60061a04cd2792ebc853c54e37308271271e7bf588657c9e0c37faacbc28c3c6',
Account: {
create: [
{
accountType: AccountType.SECURITIES,
id: 'f4425b66-9ba9-4ac4-93d7-fdf9a145e8cb',
isDefault: true,
name: 'Default Account'
}
]
},
alias: 'Admin',
id: '4e1af723-95f6-44f8-92a7-464df17f6ec3',
role: Role.ADMIN

Loading…
Cancel
Save