Skip to content

Commit

Permalink
Sales taxes import implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
rylorin committed Oct 26, 2024
1 parent 466661f commit fdbb907
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 15 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"start": "node dist/index.js -server",
"start:dev": "concurrently \"yarn server:dev\" \"yarn client:dev\"",
"storybook": "storybook dev -p 6006",
"storybook:build": "storybook build -o dist/storybook"
"storybook:build": "storybook build -o dist/storybook",
"updater": "node dist/index.js -watch -update"
},
"dependencies": {
"@chakra-ui/icons": "^2.2.4",
Expand Down
98 changes: 94 additions & 4 deletions src/bots/importer.bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
InterestStatement,
OptionStatement,
Portfolio,
SalesTaxes,
Statement,
StatementStatus,
TaxStatement,
Expand Down Expand Up @@ -70,8 +71,10 @@ const transactionStatusFromElement = (element: any): StatementStatus => {
else if (element.notes == "Ca")
return StatementStatus.CANCELLED_STATUS; // Cancelled
else if (element.openCloseIndicator == "O") return StatementStatus.OPEN_STATUS;
else if (element.openCloseIndicator == "C;O") return StatementStatus.OPEN_STATUS;
else if (element.openCloseIndicator == "C") return StatementStatus.CLOSE_STATUS;
else if (element.assetCategory == "CASH") return StatementStatus.UNDEFINED_STATUS;
else if (element.openCloseIndicator === undefined) return StatementStatus.UNDEFINED_STATUS;
else {
logger.error(MODULE + ".transactionStatusFromElement", "unknown status: ", element);
throw Error("undefined status");
Expand All @@ -95,7 +98,7 @@ const transactionDescriptionFromElement = (element: any): string => {
let description = "";
switch (transactionStatusFromElement(element)) {
case StatementStatus.ASSIGNED_STATUS:
console.log(MODULE + ".transactionDescriptionFromElement", element);
// console.log(MODULE + ".transactionDescriptionFromElement", element);
description += "Assigned";
break;
case StatementStatus.EXPIRED_STATUS:
Expand Down Expand Up @@ -304,6 +307,7 @@ export class ImporterBot extends ITradingBot {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
protected async processStockTrade(element: any): Promise<void> {
logger.log(LogLevel.Trace, MODULE + ".processStockTrade", element.symbol as string, element);
// if (element.symbol == "SHYL") console.log(element);
return this.findOrCreateContract(ibContractFromElement(element)).then(async (contract) =>
Statement.findOrCreate({
where: { transactionId: element.transactionID },
Expand Down Expand Up @@ -814,17 +818,103 @@ export class ImporterBot extends ITradingBot {
});
}

/*
{
"accountId": "***",
"acctAlias": "",
"model": "",
"currency": "EUR",
"fxRateToBase": "1",
"assetCategory": "",
"subCategory": "",
"symbol": "",
"description": "",
"conid": "",
"securityID": "",
"securityIDType": "",
"cusip": "",
"isin": "",
"figi": "",
"listingExchange": "",
"underlyingConid": "",
"underlyingSymbol": "",
"underlyingSecurityID": "",
"underlyingListingExchange": "",
"issuer": "",
"issuerCountryCode": "",
"multiplier": "",
"strike": "",
"expiry": "",
"putCall": "",
"principalAdjustFactor": "",
"date": "2024-08-02",
"country": "France",
"taxType": "VAT",
"payer": "U7802803",
"taxableDescription": "r******99:COMEX(Globex)(NP,L2)",
"taxableAmount": "-10.16",
"taxRate": "0.2",
"salesTax": "-2.032",
"taxableTransactionID": "2947603793",
"transactionID": "3220505865",
"code": "",
"serialNumber": "",
"deliveryType": "",
"commodityType": "",
"fineness": "",
"weight": ""
}
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
protected async processSalesTax(element: any): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
logger.log(LogLevel.Trace, MODULE + ".processSalesTaxes", element.symbol, element);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
logger.log(LogLevel.Warning, MODULE + ".processSalesTaxes", element.symbol, "not implemented", element);
const taxableTransaction = await Statement.findOne({
where: { transactionId: element.taxableTransactionID },
});
if (taxableTransaction) {
return Statement.findOrCreate({
where: { transactionId: element.transactionID },
defaults: {
portfolio_id: this.portfolio.id,
statementType: StatementTypes.SalesTaxStatement,
date: taxableTransaction.date,
currency: element.currency,
netCash: element.salesTax,
description: `${element.country} ${element.taxType} tax for ${element.taxableDescription}`,
transactionId: element.transactionID,
fxRateToBase: element.fxRateToBase,
},
})
.then(async ([statement, _created]) => {
return SalesTaxes.findOrCreate({
where: { id: statement.id },
defaults: {
id: statement.id,
taxableTransactionID: taxableTransaction.id,
country: element.country,
taxType: element.taxType,
taxableAmount: element.taxableAmount,
taxRate: element.taxRate,
salesTax: element.salesTax,
},
});
})
.then(([_taxStatement, _created]): void => undefined);
} else {
logger.log(
LogLevel.Error,
MODULE + ".processSalesTaxes",
element.symbol as string,
"taxableTransactionID not found",
element.taxableTransactionID,
);
}

return Promise.resolve();
}

private async processAllSalesTaxes(flexReport: FlexStatement): Promise<FlexStatement> {
console.debug("processAllSalesTaxes", flexReport);
let elements: any[];
if (!flexReport.SalesTaxes || !flexReport.SalesTaxes.SalesTax) {
elements = [];
Expand Down
28 changes: 22 additions & 6 deletions src/models/sales_taxes.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CreationOptional, InferAttributes, InferCreationAttributes } from "sequelize";
import { BelongsTo, Column, DataType, Model, Table } from "sequelize-typescript";
import { BelongsTo, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript";
import { Statement } from "./statement.model";

@Table({})
Expand All @@ -12,10 +12,26 @@ export class SalesTaxes extends Model<InferAttributes<SalesTaxes>, InferCreation
// updatedAt can be undefined during creation
declare updatedAt: CreationOptional<Date>;

@Column({ type: DataType.INTEGER })
declare transactionId: number;

/** Underlying transaction */
@BelongsTo(() => Statement, "id")
declare underlying: Statement;
// declare taxableTransactionID: ForeignKey<Contract["Statement"]>;
@ForeignKey(() => Statement)
@Column
declare taxableTransactionID: number;
@BelongsTo(() => Statement, "taxableTransactionID")
declare taxableTransaction: CreationOptional<Statement>;

@Column({ type: DataType.STRING })
declare country: string;

@Column({ type: DataType.STRING })
declare taxType: string;

@Column({ type: DataType.FLOAT })
declare taxableAmount: number;

@Column({ type: DataType.FLOAT })
declare taxRate: number;

@Column({ type: DataType.FLOAT })
declare salesTax: number;
}
1 change: 1 addition & 0 deletions src/models/statement.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export const StatementTypes = {
CorporateStatement: "CorporateStatement",
CashStatement: "Cash",
BondStatement: "Bond",
SalesTaxStatement: "SalesTax",
} as const;
export type StatementTypes = (typeof StatementTypes)[keyof typeof StatementTypes];
6 changes: 5 additions & 1 deletion src/routers/statements.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type StatementUnderlyingOption = StatementUnderlyingEntry & {
export type BaseStatement = {
id: number;
transactionId: number;
// statementType: StatementTypes;
date: number;
currency: string;
fxRateToBase: number;
Expand Down Expand Up @@ -84,6 +85,8 @@ export type CorporateStatementEntry = BaseStatement & {

export type CashStatementEntry = BaseStatement & { statementType: "Cash" };

export type SalesTaxStatementEntry = BaseStatement & { statementType: "SalesTax" };

export type StatementEntry =
| EquityStatementEntry
| OptionStatementEntry
Expand All @@ -94,4 +97,5 @@ export type StatementEntry =
| FeeStatementEntry
| CorporateStatementEntry
| CashStatementEntry
| BondStatementEntry;
| BondStatementEntry
| SalesTaxStatementEntry;
12 changes: 9 additions & 3 deletions src/routers/statements.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const statementModelToStatementEntry = async (item: Statement): Promise<S
id: item.id,
transactionId: item.transactionId,
date: item.date.getTime(),
// statementType: item.statementType,
currency: item.currency,
amount: item.netCash,
fxRateToBase: item.fxRateToBase,
Expand Down Expand Up @@ -147,10 +148,10 @@ export const statementModelToStatementEntry = async (item: Statement): Promise<S
});

case StatementTypes.CashStatement:
return Promise.resolve({
return {
statementType: StatementTypes.CashStatement,
...baseStatement,
});
};

case StatementTypes.BondStatement:
return BondStatement.findByPk(item.id).then((thisStatement) => {
Expand All @@ -164,13 +165,18 @@ export const statementModelToStatementEntry = async (item: Statement): Promise<S
...baseStatement,
country,
underlying: item.stock,
// accruedInterests: thisStatement.accruedInterests,
quantity: thisStatement.quantity,
pnl: thisStatement.realizedPnL,
fees: thisStatement.fees,
};
});

case StatementTypes.SalesTaxStatement:
return {
statementType: StatementTypes.SalesTaxStatement,
...baseStatement,
};

default:
throw Error("Undefined statement type");
}
Expand Down

0 comments on commit fdbb907

Please sign in to comment.