Browse Source

import csv with account name or id

pull/654/head
Ronald Konjer 3 years ago
parent
commit
0520145776
  1. 1
      apps/api/src/app/export/export.service.ts
  2. 3
      apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts
  3. 71
      apps/client/src/app/services/import-transactions.service.ts
  4. 3
      package.json
  5. 10
      yarn.lock

1
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,

3
apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts

@ -191,7 +191,6 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
try {
await this.importTransactionsService.importJson({
content: content.orders,
defaultAccountId: this.defaultAccountId
});
this.handleImportSuccess();
@ -204,8 +203,8 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
} else if (file.name.endsWith('.csv')) {
try {
await this.importTransactionsService.importCsv({
user: this.user,
fileContent,
defaultAccountId: this.defaultAccountId,
primaryDataSource: this.primaryDataSource
});

71
apps/client/src/app/services/import-transactions.service.ts

@ -4,14 +4,16 @@ 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 { Papa } from 'ngx-papaparse';
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'];
@ -20,28 +22,32 @@ export class ImportTransactionsService {
private static TYPE_KEYS = ['action', 'type'];
private static UNIT_PRICE_KEYS = ['price', 'unitprice', 'value'];
public constructor(private http: HttpClient) {}
public constructor(private http: HttpClient, private papa: Papa) {}
public async importCsv({
defaultAccountId,
user,
fileContent,
primaryDataSource
}: {
defaultAccountId: string;
user: User;
fileContent: string;
primaryDataSource: DataSource;
}) {
const content = csvToJson(fileContent, {
let content;
this.papa.parse(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,14 @@ export class ImportTransactionsService {
});
}
await this.importJson({ defaultAccountId, content: orders });
await this.importJson({ content: orders });
}
public importJson({
content,
defaultAccountId
}: {
content: CreateOrderDto[];
defaultAccountId: string;
}): Promise<void> {
public importJson({ content }: { content: CreateOrderDto[] }): Promise<void> {
return new Promise((resolve, reject) => {
this.postImport({
orders: content.map((order) => {
return { ...order, accountId: defaultAccountId };
})
orders: content
})
.pipe(
catchError((error) => {
@ -90,6 +89,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,

3
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",
@ -102,7 +103,7 @@
"ngx-markdown": "13.0.0",
"ngx-skeleton-loader": "2.9.1",
"ngx-stripe": "13.0.0",
"papaparse": "5.3.1",
"ngx-papaparse": "5.0.0",
"passport": "0.4.1",
"passport-google-oauth20": "2.0.0",
"passport-jwt": "4.0.0",

10
yarn.lock

@ -13425,6 +13425,14 @@ ngx-markdown@13.0.0:
prismjs "^1.25.0"
tslib "^2.3.0"
ngx-papaparse@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ngx-papaparse/-/ngx-papaparse-5.0.0.tgz#30c5a73658cdb443e78e0b3f8c89d646d1ae5b2e"
integrity sha512-BA5vEtHPrh3UXLLbcvbHAIVEdMhQrkdMS5SpciuRDQqN1PSK71qQbVFws9AuqHoBXPwXPMUokiAzYs69VFhaPA==
dependencies:
papaparse "^5.3.0"
tslib "^1.10.0"
ngx-skeleton-loader@2.9.1:
version "2.9.1"
resolved "https://registry.yarnpkg.com/ngx-skeleton-loader/-/ngx-skeleton-loader-2.9.1.tgz#1e419ef66696a2017afc9c8cd0bc129d2c680ffb"
@ -14144,7 +14152,7 @@ pako@^1.0.3, pako@~1.0.5:
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
papaparse@5.3.1:
papaparse@^5.3.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.1.tgz#770b7a9124d821d4b2132132b7bd7dce7194b5b1"
integrity sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==

Loading…
Cancel
Save