Feature/improve support for draft transactions (#265)
* Improve support for draft transactions * Update changelog
This commit is contained in:
parent
2bd9309827
commit
bb76ace95d
10
CHANGELOG.md
10
CHANGELOG.md
@ -5,6 +5,16 @@ 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 support for future transactions (drafts)
|
||||||
|
|
||||||
|
### Todo
|
||||||
|
|
||||||
|
- Apply data migration (`yarn database:push`)
|
||||||
|
|
||||||
## 1.34.0 - 07.08.2021
|
## 1.34.0 - 07.08.2021
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -69,8 +69,6 @@ export class AccountService {
|
|||||||
where: Prisma.AccountWhereUniqueInput,
|
where: Prisma.AccountWhereUniqueInput,
|
||||||
aUserId: string
|
aUserId: string
|
||||||
): Promise<Account> {
|
): Promise<Account> {
|
||||||
this.redisCacheService.remove(`${aUserId}.portfolio`);
|
|
||||||
|
|
||||||
return this.prisma.account.delete({
|
return this.prisma.account.delete({
|
||||||
where
|
where
|
||||||
});
|
});
|
||||||
|
2
apps/api/src/app/cache/cache.controller.ts
vendored
2
apps/api/src/app/cache/cache.controller.ts
vendored
@ -21,6 +21,6 @@ export class CacheController {
|
|||||||
public async flushCache(): Promise<void> {
|
public async flushCache(): Promise<void> {
|
||||||
this.redisCacheService.reset();
|
this.redisCacheService.reset();
|
||||||
|
|
||||||
return this.cacheService.flush(this.request.user.id);
|
return this.cacheService.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
apps/api/src/app/cache/cache.service.ts
vendored
2
apps/api/src/app/cache/cache.service.ts
vendored
@ -5,7 +5,7 @@ import { Injectable } from '@nestjs/common';
|
|||||||
export class CacheService {
|
export class CacheService {
|
||||||
public constructor(private prisma: PrismaService) {}
|
public constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
public async flush(aUserId: string): Promise<void> {
|
public async flush(): Promise<void> {
|
||||||
await this.prisma.property.deleteMany({
|
await this.prisma.property.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
OR: [{ key: 'LAST_DATA_GATHERING' }, { key: 'LOCKED_DATA_GATHERING' }]
|
OR: [{ key: 'LAST_DATA_GATHERING' }, { key: 'LOCKED_DATA_GATHERING' }]
|
||||||
|
@ -24,20 +24,17 @@ export class ImportService {
|
|||||||
type,
|
type,
|
||||||
unitPrice
|
unitPrice
|
||||||
} of orders) {
|
} of orders) {
|
||||||
await this.orderService.createOrder(
|
await this.orderService.createOrder({
|
||||||
{
|
currency,
|
||||||
currency,
|
dataSource,
|
||||||
dataSource,
|
fee,
|
||||||
fee,
|
quantity,
|
||||||
quantity,
|
symbol,
|
||||||
symbol,
|
type,
|
||||||
type,
|
unitPrice,
|
||||||
unitPrice,
|
date: parseISO(<string>(<unknown>date)),
|
||||||
date: parseISO(<string>(<unknown>date)),
|
User: { connect: { id: userId } }
|
||||||
User: { connect: { id: userId } }
|
});
|
||||||
},
|
|
||||||
userId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,15 +52,12 @@ export class OrderController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.orderService.deleteOrder(
|
return this.orderService.deleteOrder({
|
||||||
{
|
id_userId: {
|
||||||
id_userId: {
|
id,
|
||||||
id,
|
userId: this.request.user.id
|
||||||
userId: this.request.user.id
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
this.request.user.id
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ -135,33 +132,30 @@ export class OrderController {
|
|||||||
const accountId = data.accountId;
|
const accountId = data.accountId;
|
||||||
delete data.accountId;
|
delete data.accountId;
|
||||||
|
|
||||||
return this.orderService.createOrder(
|
return this.orderService.createOrder({
|
||||||
{
|
...data,
|
||||||
...data,
|
Account: {
|
||||||
Account: {
|
connect: {
|
||||||
connect: {
|
id_userId: { id: accountId, userId: this.request.user.id }
|
||||||
id_userId: { id: accountId, userId: this.request.user.id }
|
}
|
||||||
}
|
},
|
||||||
},
|
date,
|
||||||
date,
|
SymbolProfile: {
|
||||||
SymbolProfile: {
|
connectOrCreate: {
|
||||||
connectOrCreate: {
|
where: {
|
||||||
where: {
|
dataSource_symbol: {
|
||||||
dataSource_symbol: {
|
|
||||||
dataSource: data.dataSource,
|
|
||||||
symbol: data.symbol
|
|
||||||
}
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
dataSource: data.dataSource,
|
dataSource: data.dataSource,
|
||||||
symbol: data.symbol
|
symbol: data.symbol
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
dataSource: data.dataSource,
|
||||||
|
symbol: data.symbol
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
User: { connect: { id: this.request.user.id } }
|
|
||||||
},
|
},
|
||||||
this.request.user.id
|
User: { connect: { id: this.request.user.id } }
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
@ -198,26 +192,23 @@ export class OrderController {
|
|||||||
const accountId = data.accountId;
|
const accountId = data.accountId;
|
||||||
delete data.accountId;
|
delete data.accountId;
|
||||||
|
|
||||||
return this.orderService.updateOrder(
|
return this.orderService.updateOrder({
|
||||||
{
|
data: {
|
||||||
data: {
|
...data,
|
||||||
...data,
|
date,
|
||||||
date,
|
Account: {
|
||||||
Account: {
|
connect: {
|
||||||
connect: {
|
id_userId: { id: accountId, userId: this.request.user.id }
|
||||||
id_userId: { id: accountId, userId: this.request.user.id }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
User: { connect: { id: this.request.user.id } }
|
|
||||||
},
|
|
||||||
where: {
|
|
||||||
id_userId: {
|
|
||||||
id,
|
|
||||||
userId: this.request.user.id
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
User: { connect: { id: this.request.user.id } }
|
||||||
},
|
},
|
||||||
this.request.user.id
|
where: {
|
||||||
);
|
id_userId: {
|
||||||
|
id,
|
||||||
|
userId: this.request.user.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,12 @@ import { DataSource, Order, Prisma } from '@prisma/client';
|
|||||||
import { endOfToday, isAfter } from 'date-fns';
|
import { endOfToday, isAfter } from 'date-fns';
|
||||||
|
|
||||||
import { CacheService } from '../cache/cache.service';
|
import { CacheService } from '../cache/cache.service';
|
||||||
import { RedisCacheService } from '../redis-cache/redis-cache.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class OrderService {
|
export class OrderService {
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly cacheService: CacheService,
|
private readonly cacheService: CacheService,
|
||||||
private readonly dataGatheringService: DataGatheringService,
|
private readonly dataGatheringService: DataGatheringService,
|
||||||
private readonly redisCacheService: RedisCacheService,
|
|
||||||
private prisma: PrismaService
|
private prisma: PrismaService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -45,13 +43,10 @@ export class OrderService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createOrder(
|
public async createOrder(data: Prisma.OrderCreateInput): Promise<Order> {
|
||||||
data: Prisma.OrderCreateInput,
|
const isDraft = isAfter(data.date as Date, endOfToday());
|
||||||
aUserId: string
|
|
||||||
): Promise<Order> {
|
|
||||||
this.redisCacheService.remove(`${aUserId}.portfolio`);
|
|
||||||
|
|
||||||
if (!isAfter(data.date as Date, endOfToday())) {
|
if (!isDraft) {
|
||||||
// Gather symbol data of order in the background, if not draft
|
// Gather symbol data of order in the background, if not draft
|
||||||
this.dataGatheringService.gatherSymbols([
|
this.dataGatheringService.gatherSymbols([
|
||||||
{
|
{
|
||||||
@ -64,48 +59,75 @@ export class OrderService {
|
|||||||
|
|
||||||
this.dataGatheringService.gatherProfileData([data.symbol]);
|
this.dataGatheringService.gatherProfileData([data.symbol]);
|
||||||
|
|
||||||
await this.cacheService.flush(aUserId);
|
await this.cacheService.flush();
|
||||||
|
|
||||||
return this.prisma.order.create({
|
return this.prisma.order.create({
|
||||||
data
|
data: {
|
||||||
|
...data,
|
||||||
|
isDraft
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteOrder(
|
public async deleteOrder(
|
||||||
where: Prisma.OrderWhereUniqueInput,
|
where: Prisma.OrderWhereUniqueInput
|
||||||
aUserId: string
|
|
||||||
): Promise<Order> {
|
): Promise<Order> {
|
||||||
this.redisCacheService.remove(`${aUserId}.portfolio`);
|
|
||||||
|
|
||||||
return this.prisma.order.delete({
|
return this.prisma.order.delete({
|
||||||
where
|
where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateOrder(
|
public getOrders({
|
||||||
params: {
|
includeDrafts = false,
|
||||||
where: Prisma.OrderWhereUniqueInput;
|
userId
|
||||||
data: Prisma.OrderUpdateInput;
|
}: {
|
||||||
},
|
includeDrafts?: boolean;
|
||||||
aUserId: string
|
userId: string;
|
||||||
): Promise<Order> {
|
}) {
|
||||||
|
const where: Prisma.OrderWhereInput = { userId };
|
||||||
|
|
||||||
|
if (includeDrafts === false) {
|
||||||
|
where.isDraft = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.orders({
|
||||||
|
where,
|
||||||
|
include: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
Account: true,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
SymbolProfile: true
|
||||||
|
},
|
||||||
|
orderBy: { date: 'asc' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateOrder(params: {
|
||||||
|
where: Prisma.OrderWhereUniqueInput;
|
||||||
|
data: Prisma.OrderUpdateInput;
|
||||||
|
}): Promise<Order> {
|
||||||
const { data, where } = params;
|
const { data, where } = params;
|
||||||
|
|
||||||
this.redisCacheService.remove(`${aUserId}.portfolio`);
|
const isDraft = isAfter(data.date as Date, endOfToday());
|
||||||
|
|
||||||
// Gather symbol data of order in the background
|
if (!isDraft) {
|
||||||
this.dataGatheringService.gatherSymbols([
|
// Gather symbol data of order in the background, if not draft
|
||||||
{
|
this.dataGatheringService.gatherSymbols([
|
||||||
dataSource: <DataSource>data.dataSource,
|
{
|
||||||
date: <Date>data.date,
|
dataSource: <DataSource>data.dataSource,
|
||||||
symbol: <string>data.symbol
|
date: <Date>data.date,
|
||||||
}
|
symbol: <string>data.symbol
|
||||||
]);
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
await this.cacheService.flush(aUserId);
|
await this.cacheService.flush();
|
||||||
|
|
||||||
return this.prisma.order.update({
|
return this.prisma.order.update({
|
||||||
data,
|
data: {
|
||||||
|
...data,
|
||||||
|
isDraft
|
||||||
|
},
|
||||||
where
|
where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,12 @@ import {
|
|||||||
} from '@ghostfolio/common/types';
|
} from '@ghostfolio/common/types';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { REQUEST } from '@nestjs/core';
|
import { REQUEST } from '@nestjs/core';
|
||||||
import { Currency, DataSource, Type as TypeOfOrder } from '@prisma/client';
|
import {
|
||||||
|
Currency,
|
||||||
|
DataSource,
|
||||||
|
Prisma,
|
||||||
|
Type as TypeOfOrder
|
||||||
|
} from '@prisma/client';
|
||||||
import Big from 'big.js';
|
import Big from 'big.js';
|
||||||
import {
|
import {
|
||||||
endOfToday,
|
endOfToday,
|
||||||
@ -83,7 +88,10 @@ export class PortfolioService {
|
|||||||
this.request.user.Settings.currency
|
this.request.user.Settings.currency
|
||||||
);
|
);
|
||||||
|
|
||||||
const { transactionPoints } = await this.getTransactionPoints(userId);
|
const { transactionPoints } = await this.getTransactionPoints({
|
||||||
|
userId,
|
||||||
|
includeDrafts: true
|
||||||
|
});
|
||||||
portfolioCalculator.setTransactionPoints(transactionPoints);
|
portfolioCalculator.setTransactionPoints(transactionPoints);
|
||||||
if (transactionPoints.length === 0) {
|
if (transactionPoints.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
@ -108,7 +116,7 @@ export class PortfolioService {
|
|||||||
this.request.user.Settings.currency
|
this.request.user.Settings.currency
|
||||||
);
|
);
|
||||||
|
|
||||||
const { transactionPoints } = await this.getTransactionPoints(userId);
|
const { transactionPoints } = await this.getTransactionPoints({ userId });
|
||||||
portfolioCalculator.setTransactionPoints(transactionPoints);
|
portfolioCalculator.setTransactionPoints(transactionPoints);
|
||||||
if (transactionPoints.length === 0) {
|
if (transactionPoints.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
@ -151,7 +159,7 @@ export class PortfolioService {
|
|||||||
userId,
|
userId,
|
||||||
currency
|
currency
|
||||||
);
|
);
|
||||||
const orders = await this.getOrders(userId);
|
const orders = await this.orderService.getOrders({ userId });
|
||||||
const fees = this.getFees(orders);
|
const fees = this.getFees(orders);
|
||||||
|
|
||||||
const totalBuy = this.getTotalByType(orders, currency, TypeOfOrder.BUY);
|
const totalBuy = this.getTotalByType(orders, currency, TypeOfOrder.BUY);
|
||||||
@ -178,9 +186,9 @@ export class PortfolioService {
|
|||||||
userCurrency
|
userCurrency
|
||||||
);
|
);
|
||||||
|
|
||||||
const { transactionPoints, orders } = await this.getTransactionPoints(
|
const { orders, transactionPoints } = await this.getTransactionPoints({
|
||||||
userId
|
userId
|
||||||
);
|
});
|
||||||
|
|
||||||
if (transactionPoints?.length <= 0) {
|
if (transactionPoints?.length <= 0) {
|
||||||
return {};
|
return {};
|
||||||
@ -258,7 +266,7 @@ export class PortfolioService {
|
|||||||
): Promise<PortfolioPositionDetail> {
|
): Promise<PortfolioPositionDetail> {
|
||||||
const userId = await this.getUserId(aImpersonationId);
|
const userId = await this.getUserId(aImpersonationId);
|
||||||
|
|
||||||
const orders = (await this.getOrders(userId)).filter(
|
const orders = (await this.orderService.getOrders({ userId })).filter(
|
||||||
(order) => order.symbol === aSymbol
|
(order) => order.symbol === aSymbol
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -453,7 +461,7 @@ export class PortfolioService {
|
|||||||
this.request.user.Settings.currency
|
this.request.user.Settings.currency
|
||||||
);
|
);
|
||||||
|
|
||||||
const { transactionPoints } = await this.getTransactionPoints(userId);
|
const { transactionPoints } = await this.getTransactionPoints({ userId });
|
||||||
|
|
||||||
if (transactionPoints?.length <= 0) {
|
if (transactionPoints?.length <= 0) {
|
||||||
return {
|
return {
|
||||||
@ -514,7 +522,7 @@ export class PortfolioService {
|
|||||||
this.request.user.Settings.currency
|
this.request.user.Settings.currency
|
||||||
);
|
);
|
||||||
|
|
||||||
const { transactionPoints } = await this.getTransactionPoints(userId);
|
const { transactionPoints } = await this.getTransactionPoints({ userId });
|
||||||
|
|
||||||
if (transactionPoints?.length <= 0) {
|
if (transactionPoints?.length <= 0) {
|
||||||
return {
|
return {
|
||||||
@ -576,9 +584,9 @@ export class PortfolioService {
|
|||||||
const userId = await this.getUserId(impersonationId);
|
const userId = await this.getUserId(impersonationId);
|
||||||
const baseCurrency = this.request.user.Settings.currency;
|
const baseCurrency = this.request.user.Settings.currency;
|
||||||
|
|
||||||
const { transactionPoints, orders } = await this.getTransactionPoints(
|
const { orders, transactionPoints } = await this.getTransactionPoints({
|
||||||
userId
|
userId
|
||||||
);
|
});
|
||||||
|
|
||||||
if (isEmpty(orders)) {
|
if (isEmpty(orders)) {
|
||||||
return {
|
return {
|
||||||
@ -674,11 +682,17 @@ export class PortfolioService {
|
|||||||
return portfolioStart;
|
return portfolioStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getTransactionPoints(userId: string): Promise<{
|
private async getTransactionPoints({
|
||||||
|
includeDrafts = false,
|
||||||
|
userId
|
||||||
|
}: {
|
||||||
|
includeDrafts?: boolean;
|
||||||
|
userId: string;
|
||||||
|
}): Promise<{
|
||||||
transactionPoints: TransactionPoint[];
|
transactionPoints: TransactionPoint[];
|
||||||
orders: OrderWithAccount[];
|
orders: OrderWithAccount[];
|
||||||
}> {
|
}> {
|
||||||
const orders = await this.getOrders(userId);
|
const orders = await this.orderService.getOrders({ includeDrafts, userId });
|
||||||
|
|
||||||
if (orders.length <= 0) {
|
if (orders.length <= 0) {
|
||||||
return { transactionPoints: [], orders: [] };
|
return { transactionPoints: [], orders: [] };
|
||||||
@ -750,19 +764,6 @@ export class PortfolioService {
|
|||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrders(aUserId: string) {
|
|
||||||
return this.orderService.orders({
|
|
||||||
include: {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
Account: true,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
||||||
SymbolProfile: true
|
|
||||||
},
|
|
||||||
orderBy: { date: 'asc' },
|
|
||||||
where: { userId: aUserId }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getUserId(aImpersonationId: string) {
|
private async getUserId(aImpersonationId: string) {
|
||||||
const impersonationUserId =
|
const impersonationUserId =
|
||||||
await this.impersonationService.validateImpersonationId(
|
await this.impersonationService.validateImpersonationId(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Account, Currency, SymbolProfile } from '@prisma/client';
|
import { Account, Currency, SymbolProfile } from '@prisma/client';
|
||||||
import { endOfToday, isAfter, parseISO } from 'date-fns';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { IOrder } from '../services/interfaces/interfaces';
|
import { IOrder } from '../services/interfaces/interfaces';
|
||||||
@ -11,6 +10,7 @@ export class Order {
|
|||||||
private fee: number;
|
private fee: number;
|
||||||
private date: string;
|
private date: string;
|
||||||
private id: string;
|
private id: string;
|
||||||
|
private isDraft: boolean;
|
||||||
private quantity: number;
|
private quantity: number;
|
||||||
private symbol: string;
|
private symbol: string;
|
||||||
private symbolProfile: SymbolProfile;
|
private symbolProfile: SymbolProfile;
|
||||||
@ -24,6 +24,7 @@ export class Order {
|
|||||||
this.fee = data.fee;
|
this.fee = data.fee;
|
||||||
this.date = data.date;
|
this.date = data.date;
|
||||||
this.id = data.id || uuidv4();
|
this.id = data.id || uuidv4();
|
||||||
|
this.isDraft = data.isDraft;
|
||||||
this.quantity = data.quantity;
|
this.quantity = data.quantity;
|
||||||
this.symbol = data.symbol;
|
this.symbol = data.symbol;
|
||||||
this.symbolProfile = data.symbolProfile;
|
this.symbolProfile = data.symbolProfile;
|
||||||
@ -54,7 +55,7 @@ export class Order {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getIsDraft() {
|
public getIsDraft() {
|
||||||
return isAfter(parseISO(this.date), endOfToday());
|
return this.isDraft;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getQuantity() {
|
public getQuantity() {
|
||||||
|
@ -23,6 +23,7 @@ export interface IOrder {
|
|||||||
date: string;
|
date: string;
|
||||||
fee: number;
|
fee: number;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
isDraft: boolean;
|
||||||
quantity: number;
|
quantity: number;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
symbolProfile: SymbolProfile;
|
symbolProfile: SymbolProfile;
|
||||||
|
@ -154,8 +154,8 @@ export class InvestmentChartComponent implements OnChanges, OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private isInFuture(aContext: any, aValue: any) {
|
private isInFuture<T>(aContext: any, aValue: T) {
|
||||||
return isAfter(new Date(aContext?.p0?.parsed?.x), new Date())
|
return isAfter(new Date(aContext?.p1?.parsed?.x), new Date())
|
||||||
? aValue
|
? aValue
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
@ -96,10 +96,7 @@
|
|||||||
<td *matCellDef="let element" class="px-1" mat-cell>
|
<td *matCellDef="let element" class="px-1" mat-cell>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
{{ element.symbol | gfSymbol }}
|
{{ element.symbol | gfSymbol }}
|
||||||
<span
|
<span *ngIf="element.isDraft" class="badge badge-secondary ml-1" i18n
|
||||||
*ngIf="isAfter(element.date, endOfToday)"
|
|
||||||
class="badge badge-secondary ml-1"
|
|
||||||
i18n
|
|
||||||
>Draft</span
|
>Draft</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Order" ADD COLUMN "isDraft" BOOLEAN NOT NULL DEFAULT false;
|
@ -81,6 +81,7 @@ model Order {
|
|||||||
date DateTime
|
date DateTime
|
||||||
fee Float
|
fee Float
|
||||||
id String @default(uuid())
|
id String @default(uuid())
|
||||||
|
isDraft Boolean @default(false)
|
||||||
quantity Float
|
quantity Float
|
||||||
symbol String
|
symbol String
|
||||||
SymbolProfile SymbolProfile? @relation(fields: [symbolProfileId], references: [id])
|
SymbolProfile SymbolProfile? @relation(fields: [symbolProfileId], references: [id])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user