From 919b20197f49ebfcb67d7aa3b4b22396e9199ffa Mon Sep 17 00:00:00 2001 From: Ronald Konjer Date: Sat, 29 Jan 2022 17:27:33 +0100 Subject: [PATCH] import csv with account name or id (#654) * import csv with account id --- apps/api/src/app/export/export.service.ts | 1 + .../transactions-page.component.ts | 7 +- .../services/import-transactions.service.ts | 70 +++++++++++++------ package.json | 1 + 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/apps/api/src/app/export/export.service.ts b/apps/api/src/app/export/export.service.ts index 5fd88d4c7..784b34d5e 100644 --- a/apps/api/src/app/export/export.service.ts +++ b/apps/api/src/app/export/export.service.ts @@ -11,6 +11,7 @@ export class ExportService { const orders = await this.prismaService.order.findMany({ orderBy: { date: 'desc' }, select: { + accountId: true, currency: true, dataSource: true, date: true, diff --git a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts index 5f252caea..977cc720b 100644 --- a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts +++ b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts @@ -195,8 +195,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { try { await this.importTransactionsService.importJson({ - content: content.orders, - defaultAccountId: this.defaultAccountId + content: content.orders }); this.handleImportSuccess(); @@ -210,8 +209,8 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { try { await this.importTransactionsService.importCsv({ fileContent, - defaultAccountId: this.defaultAccountId, - primaryDataSource: this.primaryDataSource + primaryDataSource: this.primaryDataSource, + user: this.user }); this.handleImportSuccess(); diff --git a/apps/client/src/app/services/import-transactions.service.ts b/apps/client/src/app/services/import-transactions.service.ts index d3baa13a4..4708a5779 100644 --- a/apps/client/src/app/services/import-transactions.service.ts +++ b/apps/client/src/app/services/import-transactions.service.ts @@ -3,15 +3,17 @@ import { Injectable } from '@angular/core'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { DataSource, Type } from '@prisma/client'; import { parse } from 'date-fns'; -import { isNumber } from 'lodash'; import { parse as csvToJson } from 'papaparse'; +import { isNumber } from 'lodash'; import { EMPTY } from 'rxjs'; import { catchError } from 'rxjs/operators'; +import { User } from '@ghostfolio/common/interfaces'; @Injectable({ providedIn: 'root' }) export class ImportTransactionsService { + private static ACCOUNT_ID = ['account', 'accountid']; private static CURRENCY_KEYS = ['ccy', 'currency']; private static DATE_KEYS = ['date']; private static FEE_KEYS = ['commission', 'fee']; @@ -23,25 +25,29 @@ export class ImportTransactionsService { public constructor(private http: HttpClient) {} public async importCsv({ - defaultAccountId, fileContent, - primaryDataSource + primaryDataSource, + user }: { - defaultAccountId: string; fileContent: string; primaryDataSource: DataSource; + user: User; }) { - const content = csvToJson(fileContent, { + let content: any[] = []; + + csvToJson(fileContent, { dynamicTyping: true, header: true, - skipEmptyLines: true - }).data; + skipEmptyLines: true, + complete: (parsedData) => { + content = parsedData.data.filter((item) => item['date'] != null); + } + }); const orders: CreateOrderDto[] = []; - for (const [index, item] of content.entries()) { orders.push({ - accountId: defaultAccountId, + accountId: this.parseAccount({ content, index, item, user }), currency: this.parseCurrency({ content, index, item }), dataSource: primaryDataSource, 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({ - content, - defaultAccountId - }: { - content: CreateOrderDto[]; - defaultAccountId: string; - }): Promise { + public importJson({ content }: { content: CreateOrderDto[] }): Promise { return new Promise((resolve, reject) => { this.postImport({ - orders: content.map((order) => { - return { ...order, accountId: defaultAccountId }; - }) + orders: content }) .pipe( 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({ content, index, diff --git a/package.json b/package.json index 01f8f938e..4ce1cd3e9 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "affected:test": "nx affected:test", "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:dev": "nx build api && nx build client && yarn replace-placeholders-in-build", "build:storybook": "nx run ui:build-storybook", "clean": "rimraf dist", "database:format-schema": "prisma format",