@@ -11,6 +11,7 @@ export class ExportService {
|
|||||||
const orders = await this.prismaService.order.findMany({
|
const orders = await this.prismaService.order.findMany({
|
||||||
orderBy: { date: 'desc' },
|
orderBy: { date: 'desc' },
|
||||||
select: {
|
select: {
|
||||||
|
accountId: true,
|
||||||
currency: true,
|
currency: true,
|
||||||
dataSource: true,
|
dataSource: true,
|
||||||
date: true,
|
date: true,
|
||||||
|
@@ -195,8 +195,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.importTransactionsService.importJson({
|
await this.importTransactionsService.importJson({
|
||||||
content: content.orders,
|
content: content.orders
|
||||||
defaultAccountId: this.defaultAccountId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.handleImportSuccess();
|
this.handleImportSuccess();
|
||||||
@@ -210,8 +209,8 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
|
|||||||
try {
|
try {
|
||||||
await this.importTransactionsService.importCsv({
|
await this.importTransactionsService.importCsv({
|
||||||
fileContent,
|
fileContent,
|
||||||
defaultAccountId: this.defaultAccountId,
|
primaryDataSource: this.primaryDataSource,
|
||||||
primaryDataSource: this.primaryDataSource
|
user: this.user
|
||||||
});
|
});
|
||||||
|
|
||||||
this.handleImportSuccess();
|
this.handleImportSuccess();
|
||||||
|
@@ -3,15 +3,17 @@ import { Injectable } from '@angular/core';
|
|||||||
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
||||||
import { DataSource, Type } from '@prisma/client';
|
import { DataSource, Type } from '@prisma/client';
|
||||||
import { parse } from 'date-fns';
|
import { parse } from 'date-fns';
|
||||||
import { isNumber } from 'lodash';
|
|
||||||
import { parse as csvToJson } from 'papaparse';
|
import { parse as csvToJson } from 'papaparse';
|
||||||
|
import { isNumber } from 'lodash';
|
||||||
import { EMPTY } from 'rxjs';
|
import { EMPTY } from 'rxjs';
|
||||||
import { catchError } from 'rxjs/operators';
|
import { catchError } from 'rxjs/operators';
|
||||||
|
import { User } from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ImportTransactionsService {
|
export class ImportTransactionsService {
|
||||||
|
private static ACCOUNT_ID = ['account', 'accountid'];
|
||||||
private static CURRENCY_KEYS = ['ccy', 'currency'];
|
private static CURRENCY_KEYS = ['ccy', 'currency'];
|
||||||
private static DATE_KEYS = ['date'];
|
private static DATE_KEYS = ['date'];
|
||||||
private static FEE_KEYS = ['commission', 'fee'];
|
private static FEE_KEYS = ['commission', 'fee'];
|
||||||
@@ -23,25 +25,29 @@ export class ImportTransactionsService {
|
|||||||
public constructor(private http: HttpClient) {}
|
public constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
public async importCsv({
|
public async importCsv({
|
||||||
defaultAccountId,
|
|
||||||
fileContent,
|
fileContent,
|
||||||
primaryDataSource
|
primaryDataSource,
|
||||||
|
user
|
||||||
}: {
|
}: {
|
||||||
defaultAccountId: string;
|
|
||||||
fileContent: string;
|
fileContent: string;
|
||||||
primaryDataSource: DataSource;
|
primaryDataSource: DataSource;
|
||||||
|
user: User;
|
||||||
}) {
|
}) {
|
||||||
const content = csvToJson(fileContent, {
|
let content: any[] = [];
|
||||||
|
|
||||||
|
csvToJson(fileContent, {
|
||||||
dynamicTyping: true,
|
dynamicTyping: true,
|
||||||
header: true,
|
header: true,
|
||||||
skipEmptyLines: true
|
skipEmptyLines: true,
|
||||||
}).data;
|
complete: (parsedData) => {
|
||||||
|
content = parsedData.data.filter((item) => item['date'] != null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const orders: CreateOrderDto[] = [];
|
const orders: CreateOrderDto[] = [];
|
||||||
|
|
||||||
for (const [index, item] of content.entries()) {
|
for (const [index, item] of content.entries()) {
|
||||||
orders.push({
|
orders.push({
|
||||||
accountId: defaultAccountId,
|
accountId: this.parseAccount({ content, index, item, user }),
|
||||||
currency: this.parseCurrency({ content, index, item }),
|
currency: this.parseCurrency({ content, index, item }),
|
||||||
dataSource: primaryDataSource,
|
dataSource: primaryDataSource,
|
||||||
date: this.parseDate({ content, index, item }),
|
date: this.parseDate({ content, index, item }),
|
||||||
@@ -53,21 +59,13 @@ export class ImportTransactionsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.importJson({ defaultAccountId, content: orders });
|
await this.importJson({ content: orders });
|
||||||
}
|
}
|
||||||
|
|
||||||
public importJson({
|
public importJson({ content }: { content: CreateOrderDto[] }): Promise<void> {
|
||||||
content,
|
|
||||||
defaultAccountId
|
|
||||||
}: {
|
|
||||||
content: CreateOrderDto[];
|
|
||||||
defaultAccountId: string;
|
|
||||||
}): Promise<void> {
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.postImport({
|
this.postImport({
|
||||||
orders: content.map((order) => {
|
orders: content
|
||||||
return { ...order, accountId: defaultAccountId };
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
catchError((error) => {
|
catchError((error) => {
|
||||||
@@ -90,6 +88,38 @@ export class ImportTransactionsService {
|
|||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseAccount({
|
||||||
|
content,
|
||||||
|
index,
|
||||||
|
item,
|
||||||
|
user
|
||||||
|
}: {
|
||||||
|
content: any[];
|
||||||
|
index: number;
|
||||||
|
item: any;
|
||||||
|
user: User;
|
||||||
|
}) {
|
||||||
|
item = this.lowercaseKeys(item);
|
||||||
|
for (const key of ImportTransactionsService.ACCOUNT_ID) {
|
||||||
|
if (item[key]) {
|
||||||
|
let accountid = user.accounts.find((account) => {
|
||||||
|
return (
|
||||||
|
account.name.toLowerCase() === item[key].toLowerCase() ||
|
||||||
|
account.id == item[key]
|
||||||
|
);
|
||||||
|
})?.id;
|
||||||
|
if (!accountid) {
|
||||||
|
accountid = user?.accounts.find((account) => {
|
||||||
|
return account.isDefault;
|
||||||
|
})?.id;
|
||||||
|
}
|
||||||
|
return accountid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw { message: `orders.${index}.account is not valid`, orders: content };
|
||||||
|
}
|
||||||
|
|
||||||
private parseCurrency({
|
private parseCurrency({
|
||||||
content,
|
content,
|
||||||
index,
|
index,
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
"affected:test": "nx affected:test",
|
"affected:test": "nx affected:test",
|
||||||
"angular": "node --max_old_space_size=32768 ./node_modules/@angular/cli/bin/ng",
|
"angular": "node --max_old_space_size=32768 ./node_modules/@angular/cli/bin/ng",
|
||||||
"build:all": "ng build --configuration production api && ng build --configuration production client && yarn replace-placeholders-in-build",
|
"build:all": "ng build --configuration production api && ng build --configuration production client && yarn replace-placeholders-in-build",
|
||||||
|
"build:dev": "nx build api && nx build client && yarn replace-placeholders-in-build",
|
||||||
"build:storybook": "nx run ui:build-storybook",
|
"build:storybook": "nx run ui:build-storybook",
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
"database:format-schema": "prisma format",
|
"database:format-schema": "prisma format",
|
||||||
|
Reference in New Issue
Block a user