Feature/add currency to order database schema (#3251)
* Add currency to Order database schema * Update changelog
This commit is contained in:
parent
719bbe156e
commit
07c0e5a612
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Added the asset profile icon to the asset profile details dialog of the admin control
|
- Added the asset profile icon to the asset profile details dialog of the admin control
|
||||||
- Added the platform icon to the create or update platform dialog of the admin control
|
- Added the platform icon to the create or update platform dialog of the admin control
|
||||||
- Extended the rules in the _X-ray_ section by a `key`
|
- Extended the rules in the _X-ray_ section by a `key`
|
||||||
|
- Added `currency` to the `Order` database schema as a preparation to set a custom currency
|
||||||
- Extended the content of the _Self-Hosting_ section by the data providers on the Frequently Asked Questions (FAQ) page
|
- Extended the content of the _Self-Hosting_ section by the data providers on the Frequently Asked Questions (FAQ) page
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -112,6 +112,7 @@ export class ImportService {
|
|||||||
accountId: Account?.id,
|
accountId: Account?.id,
|
||||||
accountUserId: undefined,
|
accountUserId: undefined,
|
||||||
comment: undefined,
|
comment: undefined,
|
||||||
|
currency: undefined,
|
||||||
createdAt: undefined,
|
createdAt: undefined,
|
||||||
fee: 0,
|
fee: 0,
|
||||||
feeInBaseCurrency: 0,
|
feeInBaseCurrency: 0,
|
||||||
@ -261,6 +262,7 @@ export class ImportService {
|
|||||||
{
|
{
|
||||||
accountId,
|
accountId,
|
||||||
comment,
|
comment,
|
||||||
|
currency,
|
||||||
date,
|
date,
|
||||||
error,
|
error,
|
||||||
fee,
|
fee,
|
||||||
@ -285,7 +287,6 @@ export class ImportService {
|
|||||||
assetSubClass,
|
assetSubClass,
|
||||||
countries,
|
countries,
|
||||||
createdAt,
|
createdAt,
|
||||||
currency,
|
|
||||||
dataSource,
|
dataSource,
|
||||||
figi,
|
figi,
|
||||||
figiComposite,
|
figiComposite,
|
||||||
@ -342,6 +343,7 @@ export class ImportService {
|
|||||||
if (isDryRun) {
|
if (isDryRun) {
|
||||||
order = {
|
order = {
|
||||||
comment,
|
comment,
|
||||||
|
currency,
|
||||||
date,
|
date,
|
||||||
fee,
|
fee,
|
||||||
quantity,
|
quantity,
|
||||||
@ -357,7 +359,6 @@ export class ImportService {
|
|||||||
assetSubClass,
|
assetSubClass,
|
||||||
countries,
|
countries,
|
||||||
createdAt,
|
createdAt,
|
||||||
currency,
|
|
||||||
dataSource,
|
dataSource,
|
||||||
figi,
|
figi,
|
||||||
figiComposite,
|
figiComposite,
|
||||||
@ -371,6 +372,7 @@ export class ImportService {
|
|||||||
symbolMapping,
|
symbolMapping,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
url,
|
url,
|
||||||
|
currency: assetProfile.currency,
|
||||||
comment: assetProfile.comment
|
comment: assetProfile.comment
|
||||||
},
|
},
|
||||||
Account: validatedAccount,
|
Account: validatedAccount,
|
||||||
@ -394,9 +396,9 @@ export class ImportService {
|
|||||||
SymbolProfile: {
|
SymbolProfile: {
|
||||||
connectOrCreate: {
|
connectOrCreate: {
|
||||||
create: {
|
create: {
|
||||||
currency,
|
|
||||||
dataSource,
|
dataSource,
|
||||||
symbol
|
symbol,
|
||||||
|
currency: assetProfile.currency
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
dataSource_symbol: {
|
dataSource_symbol: {
|
||||||
@ -420,14 +422,14 @@ export class ImportService {
|
|||||||
value,
|
value,
|
||||||
feeInBaseCurrency: this.exchangeRateDataService.toCurrency(
|
feeInBaseCurrency: this.exchangeRateDataService.toCurrency(
|
||||||
fee,
|
fee,
|
||||||
currency,
|
assetProfile.currency,
|
||||||
userCurrency
|
userCurrency
|
||||||
),
|
),
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
SymbolProfile: assetProfile,
|
SymbolProfile: assetProfile,
|
||||||
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
|
valueInBaseCurrency: this.exchangeRateDataService.toCurrency(
|
||||||
value,
|
value,
|
||||||
currency,
|
assetProfile.currency,
|
||||||
userCurrency
|
userCurrency
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -42,6 +42,10 @@ export class CreateOrderDto {
|
|||||||
@IsISO4217CurrencyCode()
|
@IsISO4217CurrencyCode()
|
||||||
currency: string;
|
currency: string;
|
||||||
|
|
||||||
|
@IsISO4217CurrencyCode()
|
||||||
|
@IsOptional()
|
||||||
|
customCurrency?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsEnum(DataSource, { each: true })
|
@IsEnum(DataSource, { each: true })
|
||||||
dataSource?: DataSource;
|
dataSource?: DataSource;
|
||||||
|
@ -126,13 +126,22 @@ export class OrderController {
|
|||||||
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
@UseGuards(AuthGuard('jwt'), HasPermissionGuard)
|
||||||
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
@UseInterceptors(TransformDataSourceInRequestInterceptor)
|
||||||
public async createOrder(@Body() data: CreateOrderDto): Promise<OrderModel> {
|
public async createOrder(@Body() data: CreateOrderDto): Promise<OrderModel> {
|
||||||
|
const currency = data.currency;
|
||||||
|
const customCurrency = data.customCurrency;
|
||||||
|
|
||||||
|
if (customCurrency) {
|
||||||
|
data.currency = customCurrency;
|
||||||
|
|
||||||
|
delete data.customCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
const order = await this.orderService.createOrder({
|
const order = await this.orderService.createOrder({
|
||||||
...data,
|
...data,
|
||||||
date: parseISO(data.date),
|
date: parseISO(data.date),
|
||||||
SymbolProfile: {
|
SymbolProfile: {
|
||||||
connectOrCreate: {
|
connectOrCreate: {
|
||||||
create: {
|
create: {
|
||||||
currency: data.currency,
|
currency,
|
||||||
dataSource: data.dataSource,
|
dataSource: data.dataSource,
|
||||||
symbol: data.symbol
|
symbol: data.symbol
|
||||||
},
|
},
|
||||||
@ -182,8 +191,16 @@ export class OrderController {
|
|||||||
const date = parseISO(data.date);
|
const date = parseISO(data.date);
|
||||||
|
|
||||||
const accountId = data.accountId;
|
const accountId = data.accountId;
|
||||||
|
const customCurrency = data.customCurrency;
|
||||||
|
|
||||||
delete data.accountId;
|
delete data.accountId;
|
||||||
|
|
||||||
|
if (customCurrency) {
|
||||||
|
data.currency = customCurrency;
|
||||||
|
|
||||||
|
delete data.customCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
return this.orderService.updateOrder({
|
return this.orderService.updateOrder({
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
|
@ -26,6 +26,7 @@ import { endOfToday, isAfter } from 'date-fns';
|
|||||||
import { groupBy, uniqBy } from 'lodash';
|
import { groupBy, uniqBy } from 'lodash';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { CreateOrderDto } from './create-order.dto';
|
||||||
import { Activities } from './interfaces/activities.interface';
|
import { Activities } from './interfaces/activities.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -65,7 +66,6 @@ export class OrderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const accountId = data.accountId;
|
const accountId = data.accountId;
|
||||||
let currency = data.currency;
|
|
||||||
const tags = data.tags ?? [];
|
const tags = data.tags ?? [];
|
||||||
const updateAccountBalance = data.updateAccountBalance ?? false;
|
const updateAccountBalance = data.updateAccountBalance ?? false;
|
||||||
const userId = data.userId;
|
const userId = data.userId;
|
||||||
@ -73,7 +73,6 @@ export class OrderService {
|
|||||||
if (['FEE', 'INTEREST', 'ITEM', 'LIABILITY'].includes(data.type)) {
|
if (['FEE', 'INTEREST', 'ITEM', 'LIABILITY'].includes(data.type)) {
|
||||||
const assetClass = data.assetClass;
|
const assetClass = data.assetClass;
|
||||||
const assetSubClass = data.assetSubClass;
|
const assetSubClass = data.assetSubClass;
|
||||||
currency = data.SymbolProfile.connectOrCreate.create.currency;
|
|
||||||
const dataSource: DataSource = 'MANUAL';
|
const dataSource: DataSource = 'MANUAL';
|
||||||
const id = uuidv4();
|
const id = uuidv4();
|
||||||
const name = data.SymbolProfile.connectOrCreate.create.symbol;
|
const name = data.SymbolProfile.connectOrCreate.create.symbol;
|
||||||
@ -81,7 +80,6 @@ export class OrderService {
|
|||||||
data.id = id;
|
data.id = id;
|
||||||
data.SymbolProfile.connectOrCreate.create.assetClass = assetClass;
|
data.SymbolProfile.connectOrCreate.create.assetClass = assetClass;
|
||||||
data.SymbolProfile.connectOrCreate.create.assetSubClass = assetSubClass;
|
data.SymbolProfile.connectOrCreate.create.assetSubClass = assetSubClass;
|
||||||
data.SymbolProfile.connectOrCreate.create.currency = currency;
|
|
||||||
data.SymbolProfile.connectOrCreate.create.dataSource = dataSource;
|
data.SymbolProfile.connectOrCreate.create.dataSource = dataSource;
|
||||||
data.SymbolProfile.connectOrCreate.create.name = name;
|
data.SymbolProfile.connectOrCreate.create.name = name;
|
||||||
data.SymbolProfile.connectOrCreate.create.symbol = id;
|
data.SymbolProfile.connectOrCreate.create.symbol = id;
|
||||||
@ -116,7 +114,6 @@ export class OrderService {
|
|||||||
delete data.comment;
|
delete data.comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete data.currency;
|
|
||||||
delete data.dataSource;
|
delete data.dataSource;
|
||||||
delete data.symbol;
|
delete data.symbol;
|
||||||
delete data.tags;
|
delete data.tags;
|
||||||
@ -155,8 +152,8 @@ export class OrderService {
|
|||||||
await this.accountService.updateAccountBalance({
|
await this.accountService.updateAccountBalance({
|
||||||
accountId,
|
accountId,
|
||||||
amount,
|
amount,
|
||||||
currency,
|
|
||||||
userId,
|
userId,
|
||||||
|
currency: data.SymbolProfile.connectOrCreate.create.currency,
|
||||||
date: data.date as Date
|
date: data.date as Date
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -442,7 +439,6 @@ export class OrderService {
|
|||||||
|
|
||||||
delete data.assetClass;
|
delete data.assetClass;
|
||||||
delete data.assetSubClass;
|
delete data.assetSubClass;
|
||||||
delete data.currency;
|
|
||||||
delete data.dataSource;
|
delete data.dataSource;
|
||||||
delete data.symbol;
|
delete data.symbol;
|
||||||
delete data.tags;
|
delete data.tags;
|
||||||
|
@ -41,6 +41,10 @@ export class UpdateOrderDto {
|
|||||||
@IsISO4217CurrencyCode()
|
@IsISO4217CurrencyCode()
|
||||||
currency: string;
|
currency: string;
|
||||||
|
|
||||||
|
@IsISO4217CurrencyCode()
|
||||||
|
@IsOptional()
|
||||||
|
customCurrency?: string;
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
dataSource: DataSource;
|
dataSource: DataSource;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ export const activityDummyData = {
|
|||||||
accountUserId: undefined,
|
accountUserId: undefined,
|
||||||
comment: undefined,
|
comment: undefined,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
currency: undefined,
|
||||||
feeInBaseCurrency: undefined,
|
feeInBaseCurrency: undefined,
|
||||||
id: undefined,
|
id: undefined,
|
||||||
isDraft: false,
|
isDraft: false,
|
||||||
|
@ -98,10 +98,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
this.data.activity?.SymbolProfile?.currency,
|
this.data.activity?.SymbolProfile?.currency,
|
||||||
Validators.required
|
Validators.required
|
||||||
],
|
],
|
||||||
currencyOfFee: [
|
|
||||||
this.data.activity?.SymbolProfile?.currency,
|
|
||||||
Validators.required
|
|
||||||
],
|
|
||||||
currencyOfUnitPrice: [
|
currencyOfUnitPrice: [
|
||||||
this.data.activity?.SymbolProfile?.currency,
|
this.data.activity?.SymbolProfile?.currency,
|
||||||
Validators.required
|
Validators.required
|
||||||
@ -149,45 +145,16 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
takeUntil(this.unsubscribeSubject)
|
takeUntil(this.unsubscribeSubject)
|
||||||
)
|
)
|
||||||
.subscribe(async () => {
|
.subscribe(async () => {
|
||||||
let exchangeRateOfFee = 1;
|
|
||||||
let exchangeRateOfUnitPrice = 1;
|
let exchangeRateOfUnitPrice = 1;
|
||||||
|
|
||||||
this.activityForm.controls['feeInCustomCurrency'].setErrors(null);
|
this.activityForm.controls['feeInCustomCurrency'].setErrors(null);
|
||||||
this.activityForm.controls['unitPriceInCustomCurrency'].setErrors(null);
|
this.activityForm.controls['unitPriceInCustomCurrency'].setErrors(null);
|
||||||
|
|
||||||
const currency = this.activityForm.controls['currency'].value;
|
const currency = this.activityForm.controls['currency'].value;
|
||||||
const currencyOfFee = this.activityForm.controls['currencyOfFee'].value;
|
|
||||||
const currencyOfUnitPrice =
|
const currencyOfUnitPrice =
|
||||||
this.activityForm.controls['currencyOfUnitPrice'].value;
|
this.activityForm.controls['currencyOfUnitPrice'].value;
|
||||||
const date = this.activityForm.controls['date'].value;
|
const date = this.activityForm.controls['date'].value;
|
||||||
|
|
||||||
if (currency && currencyOfFee && currency !== currencyOfFee && date) {
|
|
||||||
try {
|
|
||||||
const { marketPrice } = await lastValueFrom(
|
|
||||||
this.dataService
|
|
||||||
.fetchExchangeRateForDate({
|
|
||||||
date,
|
|
||||||
symbol: `${currencyOfFee}-${currency}`
|
|
||||||
})
|
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
|
||||||
);
|
|
||||||
|
|
||||||
exchangeRateOfFee = marketPrice;
|
|
||||||
} catch {
|
|
||||||
this.activityForm.controls['feeInCustomCurrency'].setErrors({
|
|
||||||
invalid: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const feeInCustomCurrency =
|
|
||||||
this.activityForm.controls['feeInCustomCurrency'].value *
|
|
||||||
exchangeRateOfFee;
|
|
||||||
|
|
||||||
this.activityForm.controls['fee'].setValue(feeInCustomCurrency, {
|
|
||||||
emitEvent: false
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
currency &&
|
currency &&
|
||||||
currencyOfUnitPrice &&
|
currencyOfUnitPrice &&
|
||||||
@ -212,10 +179,18 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const feeInCustomCurrency =
|
||||||
|
this.activityForm.controls['feeInCustomCurrency'].value *
|
||||||
|
exchangeRateOfUnitPrice;
|
||||||
|
|
||||||
const unitPriceInCustomCurrency =
|
const unitPriceInCustomCurrency =
|
||||||
this.activityForm.controls['unitPriceInCustomCurrency'].value *
|
this.activityForm.controls['unitPriceInCustomCurrency'].value *
|
||||||
exchangeRateOfUnitPrice;
|
exchangeRateOfUnitPrice;
|
||||||
|
|
||||||
|
this.activityForm.controls['fee'].setValue(feeInCustomCurrency, {
|
||||||
|
emitEvent: false
|
||||||
|
});
|
||||||
|
|
||||||
this.activityForm.controls['unitPrice'].setValue(
|
this.activityForm.controls['unitPrice'].setValue(
|
||||||
unitPriceInCustomCurrency,
|
unitPriceInCustomCurrency,
|
||||||
{
|
{
|
||||||
@ -258,7 +233,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
})?.currency ?? this.data.user.settings.baseCurrency;
|
})?.currency ?? this.data.user.settings.baseCurrency;
|
||||||
|
|
||||||
this.activityForm.controls['currency'].setValue(currency);
|
this.activityForm.controls['currency'].setValue(currency);
|
||||||
this.activityForm.controls['currencyOfFee'].setValue(currency);
|
|
||||||
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
||||||
|
|
||||||
if (['FEE', 'INTEREST'].includes(type)) {
|
if (['FEE', 'INTEREST'].includes(type)) {
|
||||||
@ -328,7 +302,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
})?.currency ?? this.data.user.settings.baseCurrency;
|
})?.currency ?? this.data.user.settings.baseCurrency;
|
||||||
|
|
||||||
this.activityForm.controls['currency'].setValue(currency);
|
this.activityForm.controls['currency'].setValue(currency);
|
||||||
this.activityForm.controls['currencyOfFee'].setValue(currency);
|
|
||||||
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
||||||
|
|
||||||
this.activityForm.controls['dataSource'].removeValidators(
|
this.activityForm.controls['dataSource'].removeValidators(
|
||||||
@ -361,7 +334,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
})?.currency ?? this.data.user.settings.baseCurrency;
|
})?.currency ?? this.data.user.settings.baseCurrency;
|
||||||
|
|
||||||
this.activityForm.controls['currency'].setValue(currency);
|
this.activityForm.controls['currency'].setValue(currency);
|
||||||
this.activityForm.controls['currencyOfFee'].setValue(currency);
|
|
||||||
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
||||||
|
|
||||||
this.activityForm.controls['dataSource'].removeValidators(
|
this.activityForm.controls['dataSource'].removeValidators(
|
||||||
@ -486,6 +458,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
assetSubClass: this.activityForm.controls['assetSubClass'].value,
|
assetSubClass: this.activityForm.controls['assetSubClass'].value,
|
||||||
comment: this.activityForm.controls['comment'].value,
|
comment: this.activityForm.controls['comment'].value,
|
||||||
currency: this.activityForm.controls['currency'].value,
|
currency: this.activityForm.controls['currency'].value,
|
||||||
|
customCurrency: this.activityForm.controls['currencyOfUnitPrice'].value,
|
||||||
date: this.activityForm.controls['date'].value,
|
date: this.activityForm.controls['date'].value,
|
||||||
dataSource: this.activityForm.controls['dataSource'].value,
|
dataSource: this.activityForm.controls['dataSource'].value,
|
||||||
fee: this.activityForm.controls['fee'].value,
|
fee: this.activityForm.controls['fee'].value,
|
||||||
@ -549,7 +522,6 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
)
|
)
|
||||||
.subscribe(({ currency, dataSource, marketPrice }) => {
|
.subscribe(({ currency, dataSource, marketPrice }) => {
|
||||||
this.activityForm.controls['currency'].setValue(currency);
|
this.activityForm.controls['currency'].setValue(currency);
|
||||||
this.activityForm.controls['currencyOfFee'].setValue(currency);
|
|
||||||
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
this.activityForm.controls['currencyOfUnitPrice'].setValue(currency);
|
||||||
this.activityForm.controls['dataSource'].setValue(dataSource);
|
this.activityForm.controls['dataSource'].setValue(dataSource);
|
||||||
|
|
||||||
|
@ -290,11 +290,7 @@
|
|||||||
matTextSuffix
|
matTextSuffix
|
||||||
[ngClass]="{ 'd-none': !activityForm.controls['currency']?.value }"
|
[ngClass]="{ 'd-none': !activityForm.controls['currency']?.value }"
|
||||||
>
|
>
|
||||||
<mat-select formControlName="currencyOfFee">
|
{{ activityForm.controls['currencyOfUnitPrice'].value }}
|
||||||
<mat-option *ngFor="let currency of currencies" [value]="currency">
|
|
||||||
{{ currency }}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</div>
|
</div>
|
||||||
<mat-error
|
<mat-error
|
||||||
*ngIf="
|
*ngIf="
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Order" ADD COLUMN "currency" TEXT;
|
@ -109,6 +109,7 @@ model Order {
|
|||||||
accountUserId String?
|
accountUserId String?
|
||||||
comment String?
|
comment String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
currency String?
|
||||||
date DateTime
|
date DateTime
|
||||||
fee Float
|
fee Float
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user