diff --git a/src/app/components/Portfolio/Report/ReportIndex.tsx b/src/app/components/Portfolio/Report/ReportIndex.tsx index 6f57b58..a11bbb6 100644 --- a/src/app/components/Portfolio/Report/ReportIndex.tsx +++ b/src/app/components/Portfolio/Report/ReportIndex.tsx @@ -1,10 +1,15 @@ import { Link, Table, TableCaption, TableContainer, Tbody, Td, Thead, Tr } from "@chakra-ui/react"; import { FunctionComponent, default as React } from "react"; import { Link as RouterLink, useLoaderData, useParams } from "react-router-dom"; +import { ReportEntry } from "../../../../routers/reports.types"; +import Number from "../../Number/Number"; +import { StatementLink } from "../Statement/links"; import { ReportLink } from "./links"; type Props = Record; +const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + /** * Statements list component * @param param0 @@ -12,7 +17,29 @@ type Props = Record; */ const ReportsIndex: FunctionComponent = ({ ..._rest }): React.ReactNode => { const { portfolioId } = useParams(); - const theReports = useLoaderData() as number[]; + const theReports = useLoaderData() as ReportEntry[]; + const years = theReports.reduce((p, v) => { + if (!p.includes(v.year)) p.push(v.year); + return p; + }, [] as number[]); + + const Cell = ({ year, month }: { year: number; month: number }): React.ReactNode => { + const report = theReports.find((item) => item.year == year && item.month == month); + const value = report + ? report.dividendsSummary.reduce((p, v) => p + v.grossAmountInBase, 0) + + report.interestsSummary.totalAmountInBase + + report.feesSummary.totalAmountInBase + : 0; + return ( + <> + {report && ( + + + + )} + + ); + }; return ( <> @@ -22,16 +49,30 @@ const ReportsIndex: FunctionComponent = ({ ..._rest }): React.ReactNode = Year + {months.map((month) => { + return ( + + {month} + + ); + })} - {theReports.map((item) => ( - + {years.map((year) => ( + - - {item} + + {year} + {months.map((month, i) => { + return ( + + + + ); + })} ))} diff --git a/src/app/components/Portfolio/Report/ReportSummary.tsx b/src/app/components/Portfolio/Report/ReportSummary.tsx index 807ba56..1043cd2 100644 --- a/src/app/components/Portfolio/Report/ReportSummary.tsx +++ b/src/app/components/Portfolio/Report/ReportSummary.tsx @@ -3,7 +3,6 @@ import { FunctionComponent, default as React } from "react"; import { Link as RouterLink, useLoaderData, useParams } from "react-router-dom"; import { DididendSummary, ReportEntry } from "../../../../routers/reports.types"; import Number from "../../Number/Number"; -import StatementsTable from "../Statement/StatementsTable"; import { ReportLink } from "./links"; type Props = Record; @@ -15,7 +14,7 @@ type Props = Record; */ const ReportSummary: FunctionComponent = ({ ..._rest }): React.ReactNode => { const { portfolioId } = useParams(); - const theReport = useLoaderData() as ReportEntry; + const theReport = useLoaderData()[0] as ReportEntry; return ( <> @@ -26,7 +25,6 @@ const ReportSummary: FunctionComponent = ({ ..._rest }): React.ReactNode - {/* */}

Dividends

@@ -65,8 +63,7 @@ const ReportSummary: FunctionComponent = ({ ..._rest }): React.ReactNode - - {/* */} + {/* */}

Interests

Gross credit @@ -86,7 +83,7 @@ const ReportSummary: FunctionComponent = ({ ..._rest }): React.ReactNode - + {/* */}

Fees

Total @@ -95,7 +92,7 @@ const ReportSummary: FunctionComponent = ({ ..._rest }): React.ReactNode - + {/* */} ); }; diff --git a/src/app/components/Portfolio/Report/loaders.ts b/src/app/components/Portfolio/Report/loaders.ts index c2733fd..83136bc 100644 --- a/src/app/components/Portfolio/Report/loaders.ts +++ b/src/app/components/Portfolio/Report/loaders.ts @@ -6,21 +6,33 @@ import { ReportEntry } from "../../../../routers/reports.types"; * @param param0 * @returns */ -export const reportsIndexLoader = ({ params }: LoaderFunctionArgs): Promise => { +export const reportsIndexLoader0 = ({ params }: LoaderFunctionArgs): Promise => { const { portfolioId } = params; return fetch(`/api/portfolio/${portfolioId}/reports/index`) .then((response) => response.json()) .then((data) => data.reports as number[]); }; +/** + * Fetch all reports + * @param param0 + * @returns + */ +export const reportsIndexLoader = ({ params }: LoaderFunctionArgs): Promise => { + const { portfolioId } = params; + return fetch(`/api/portfolio/${portfolioId}/reports/summary/all`) + .then((response) => response.json()) + .then((data) => data.reports as ReportEntry[]); +}; + /** * Fetch a report * @param param0 * @returns */ -export const reportSummaryLoader = ({ params }: LoaderFunctionArgs): Promise => { +export const reportSummaryLoader = ({ params }: LoaderFunctionArgs): Promise => { const { portfolioId, year } = params; return fetch(`/api/portfolio/${portfolioId}/reports/year/${year}`) .then((response) => response.json()) - .then((data) => data.report as ReportEntry); + .then((data) => data.reports as ReportEntry[]); }; diff --git a/src/app/components/Portfolio/Statement/BaseStatement.tsx b/src/app/components/Portfolio/Statement/BaseStatement.tsx index 6c748b0..4fe8594 100644 --- a/src/app/components/Portfolio/Statement/BaseStatement.tsx +++ b/src/app/components/Portfolio/Statement/BaseStatement.tsx @@ -32,7 +32,7 @@ const BaseStatement = ({ portfolioId, statement }: Props): React.ReactNode => { Date: - + {new Date(statement.date).toLocaleString()} diff --git a/src/app/components/Portfolio/Statement/links.ts b/src/app/components/Portfolio/Statement/links.ts index e729c48..6eb9af1 100644 --- a/src/app/components/Portfolio/Statement/links.ts +++ b/src/app/components/Portfolio/Statement/links.ts @@ -2,10 +2,13 @@ export const StatementLink = { toIndex: (portfolioId: number | string): string => `/portfolio/${portfolioId}/statements/summary/ytd/`, toItem: (portfolioId: number | string, statementId: number | string): string => `/portfolio/${portfolioId}/statements/id/${statementId}/`, - toMonth: (portfolioId: number | string, date: Date | string): string => { + toDate: (portfolioId: number | string, date: Date | string): string => { if (typeof date != "string") date = date.toISOString(); return `/portfolio/${portfolioId}/statements/month/${date.substring(0, 4)}/${date.substring(5, 7)}/`; }, + toMonth: (portfolioId: number | string, year: number | string, month: number | string): string => { + return `/portfolio/${portfolioId}/statements/month/${year}/${month}/`; + }, edit: (portfolioId: number | string, itemId: number | string): string => `/portfolio/${portfolioId}/statements/id/${itemId}/edit`, }; diff --git a/src/app/components/Portfolio/Trade/TradeShow.tsx b/src/app/components/Portfolio/Trade/TradeShow.tsx index 793104f..c489d85 100644 --- a/src/app/components/Portfolio/Trade/TradeShow.tsx +++ b/src/app/components/Portfolio/Trade/TradeShow.tsx @@ -35,7 +35,7 @@ const TradeShow: FunctionComponent = ({ ..._rest }): React.ReactNode => { Open date: - + {new Date(item.openingDate).toLocaleString()} @@ -66,7 +66,7 @@ const TradeShow: FunctionComponent = ({ ..._rest }): React.ReactNode => { {item.closingDate && ( - + {new Date(item.closingDate).toLocaleString()} )} diff --git a/src/routers/reports.router.ts b/src/routers/reports.router.ts index 99ab9d7..b94c3d4 100644 --- a/src/routers/reports.router.ts +++ b/src/routers/reports.router.ts @@ -1,9 +1,13 @@ import { default as express } from "express"; import { Op } from "sequelize"; -import { Balance, Currency, Portfolio } from "../models"; +import { LogLevel, default as logger } from "../logger"; +import { Balance, Currency, Portfolio, Statement } from "../models"; import { prepareReport } from "./statements.utils"; -// const MODULE = "ReportsRouter"; +const MODULE = "ReportsRouter"; + +// eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unused-vars +const sequelize_logging = (...args: any[]): void => logger.trace(MODULE + ".squelize", ...args); const router = express.Router({ mergeParams: true }); @@ -53,16 +57,41 @@ router.get("/year/:year(\\d+)", (req, res): void => { }) .then((portfolio) => { if (portfolio) { - return prepareReport(portfolio, parseInt(year), 0); + return prepareReport(portfolio.statements); } else { throw Error("portfolio doesn't exist"); } }) - .then((report) => { - res.status(200).json({ report }); + .then((reports) => { + res.status(200).json({ reports }); + }) + .catch((error) => { + console.error(error); + res.status(500).json({ error }); + }); +}); + +/** + * Get all monthly reports + */ +router.get("/summary/all", (req, res): void => { + const { portfolioId } = req.params as typeof req.params & parentParams; + + Statement.findAll({ + where: { + portfolio_id: portfolioId, + date: { + [Op.gte]: new Date(2021, 0, 1), + }, + }, + }) + .then((statements) => prepareReport(statements)) + .then((reports) => { + res.status(200).json({ reports }); }) .catch((error) => { console.error(error); + logger.log(LogLevel.Error, MODULE + ".All", undefined, JSON.stringify(error)); res.status(500).json({ error }); }); }); diff --git a/src/routers/reports.types.ts b/src/routers/reports.types.ts index 3abe6d9..6f74b80 100644 --- a/src/routers/reports.types.ts +++ b/src/routers/reports.types.ts @@ -53,7 +53,7 @@ export type FeesSummary = { * Report entry data transfered between frontend and backend */ export type ReportEntry = { - portfolioId: number; + // portfolioId: number; year: number; month: number; dividendsSummary: DididendSummary[]; diff --git a/src/routers/statements.router.ts b/src/routers/statements.router.ts index 7ad73e7..234a7ad 100644 --- a/src/routers/statements.router.ts +++ b/src/routers/statements.router.ts @@ -1,6 +1,6 @@ import express from "express"; import { Op } from "sequelize"; -import logger, { LogLevel } from "../logger"; +import { default as logger, LogLevel } from "../logger"; import { Contract, DividendStatement, diff --git a/src/routers/statements.types.ts b/src/routers/statements.types.ts index cc09595..f3d4b65 100644 --- a/src/routers/statements.types.ts +++ b/src/routers/statements.types.ts @@ -48,7 +48,7 @@ export type DividendStatementEntry = BaseStatement & { country: string; }; export type TaxStatementEntry = BaseStatement & { statementType: "Tax"; country: string }; -export type InterestStatementEntry = BaseStatement & { statementType: "Interest"; country: string }; +export type InterestStatementEntry = BaseStatement & { statementType: "Interest"; country: string | null }; export type WithHoldingStatementEntry = BaseStatement & { statementType: "WithHolding" }; export type FeeStatementEntry = BaseStatement & { statementType: "OtherFee" }; export type CorporateStatementEntry = BaseStatement & { statementType: "CorporateStatement" }; diff --git a/src/routers/statements.utils.ts b/src/routers/statements.utils.ts index 8f667d6..7d1b29b 100644 --- a/src/routers/statements.utils.ts +++ b/src/routers/statements.utils.ts @@ -6,7 +6,6 @@ import { EquityStatement, OptionContract, OptionStatement, - Portfolio, Statement, TaxStatement, } from "../models"; @@ -19,7 +18,6 @@ export const statementModelToStatementEntry = (item: Statement): Promise { baseStatement.quantity = thisStatement?.quantity; @@ -131,6 +135,7 @@ export const statementModelToStatementEntry = (item: Statement): Promise => { - return prepareStatements(portfolio.statements).then((statements) => { - const report: ReportEntry = { - portfolioId: portfolio.id, - year, - month, - dividendsSummary: [], - dividendsDetails: [], - interestsSummary: { totalAmountInBase: 0, grossCredit: 0, netDebit: 0, withHolding: 0 }, - interestsDetails: [], - feesSummary: { totalAmountInBase: 0 }, - feesDetails: [], - }; +export const prepareReport = (statements: Statement[]): Promise => { + return prepareStatements(statements).then((statements) => { + const result: ReportEntry[] = []; statements.forEach((statement) => { - // const date = statement.date.toString(); - // const year = parseInt(date.substring(0, 4)); - // const month = parseInt(date.substring(5, 7)); + const year = new Date(statement.date).getUTCFullYear(); + const month = new Date(statement.date).getUTCMonth() + 1; let entry: DididendSummary | undefined; + let report = result.find((item) => item.year == year && item.month == month); + if (!report) { + // console.log(date, "creating report for ", year, month); + report = { + year, + month, + dividendsSummary: [], + dividendsDetails: [], + interestsSummary: { totalAmountInBase: 0, grossCredit: 0, netDebit: 0, withHolding: 0 }, + interestsDetails: [], + feesSummary: { totalAmountInBase: 0 }, + feesDetails: [], + }; + result.push(report); + } switch (statement.statementType) { case StatementTypes.DividendStatement: entry = report.dividendsSummary.find((item) => item.country == statement.country); @@ -214,7 +223,7 @@ export const prepareReport = (portfolio: Portfolio, year: number, month: number) break; } }); - console.log(report.dividendsSummary); - return report; + // console.log(report.dividendsSummary); + return result; }); };