@tasenor/common-node 1.9.31 → 1.9.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/index.ts +0 -1
- package/src/testing/ProcessingSystemMock.ts +0 -45
- package/src/testing/UnitTestImportConnector.ts +0 -86
- package/src/testing/UnitTester.ts +0 -231
- package/src/testing/index.ts +0 -4
- package/src/testing/test-handlers.ts +0 -47
- package/tests/TransferAnalyzer-account-address.spec.ts +0 -87
- package/tests/TransferAnalyzer-buying-and-selling.spec.ts +0 -354
- package/tests/TransferAnalyzer-loans.spec.ts +0 -197
- package/tests/TransferAnalyzer-multiple-null-amounts.spec.ts +0 -181
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tasenor/common-node",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.33",
|
|
4
4
|
"description": "Common Node-parts of Tasenor project",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"sqlite3": "^5.1.6",
|
|
37
37
|
"tar": "^6.1.15",
|
|
38
38
|
"ts-opaque": "^3.0.1",
|
|
39
|
-
"@tasenor/common": "1.9.
|
|
39
|
+
"@tasenor/common": "1.9.33"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@jest/globals": "^29.6.1",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"jest": "^29.6.1",
|
|
46
46
|
"ts-jest": "^29.1.1",
|
|
47
47
|
"typescript": "^5.1.6",
|
|
48
|
-
"eslint-config-tasenor": "0.0.0",
|
|
49
48
|
"jestconfig": "0.0.0",
|
|
49
|
+
"eslint-config-tasenor": "0.0.0",
|
|
50
50
|
"tsconfig": "0.0.0"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
import { ID, ProcessConfig, ProcessName } from '@tasenor/common'
|
|
3
|
-
import { UnitTestImportConnector } from './UnitTestImportConnector'
|
|
4
|
-
import { KnexDatabase, Process, ProcessConnector, ProcessFile, ProcessFileData, ProcessHandler, ProcessHandlerMap, ProcessStep } from '..'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Processing system mock for unit testing.
|
|
8
|
-
*/
|
|
9
|
-
export class SystemMock {
|
|
10
|
-
db: KnexDatabase
|
|
11
|
-
handlers: ProcessHandlerMap = {}
|
|
12
|
-
connector: ProcessConnector
|
|
13
|
-
logger: {
|
|
14
|
-
info: (...msg) => void
|
|
15
|
-
error: (...msg) => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
this.connector = new UnitTestImportConnector()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async getTranslation(text: string, language: string): Promise<string> {
|
|
23
|
-
return this.connector.getTranslation(text, language)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
register(handler: ProcessHandler): void {
|
|
27
|
-
//
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async createProcess(name: ProcessName, filed: ProcessFileData[], config: ProcessConfig): Promise<Process> {
|
|
31
|
-
return new Process(this, name, config)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async checkFinishAndFindDirections(handler: ProcessHandler, step: ProcessStep): Promise<void> {
|
|
35
|
-
//
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
getHandler(name: string): ProcessHandler {
|
|
39
|
-
return this.handlers[name]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async loadProcess(id: ID): Promise<Process> {
|
|
43
|
-
return this.createProcess('dummy', [new ProcessFile({ name: 'file', encoding: 'utf-8', data: 'Hello' })], {})
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
import { AccountAddress, AccountNumber, Asset, AssetExchange, AssetTransfer, AssetType, BalanceBookkeeping, Currency, Language, StockValueData, TradeableAsset, ImportState, ProcessConfig, Directions, ID } from '@tasenor/common'
|
|
3
|
-
import { ImportPlugin, TransactionImportConnector, TransactionImportHandler, ISPDemoServer } from '..'
|
|
4
|
-
import { CoinbaseHandler } from '../../../tasenor-common-plugins/src/CoinbaseImport/backend/CoinbaseHandler'
|
|
5
|
-
import IncomeAndExpenses from '../../../tasenor-common-plugins/src/IncomeAndExpenses/backend'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A connector mock for unit testing.
|
|
9
|
-
*/
|
|
10
|
-
export class UnitTestImportConnector implements TransactionImportConnector {
|
|
11
|
-
|
|
12
|
-
private balance: Record<AccountNumber, number> = {}
|
|
13
|
-
private number: Record<AccountAddress, AccountNumber> = {}
|
|
14
|
-
|
|
15
|
-
async initialize(server: ISPDemoServer) {
|
|
16
|
-
//
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
addMoney(address: AccountAddress, number: AccountNumber, amount: number) {
|
|
20
|
-
this.balance[address] = (this.balance[address] || 0) + amount
|
|
21
|
-
this.number[address] = number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async initializeBalances(time: Date, balances: BalanceBookkeeping, config: ProcessConfig): Promise<void> {
|
|
25
|
-
balances.configureNames(config)
|
|
26
|
-
Object.keys(this.balance).forEach(address => balances.set(this.number[address], this.balance[address]))
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async getAccountCanditates(): Promise<AccountNumber[]> {
|
|
30
|
-
return []
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async getStock(time: Date, account: AccountNumber, symbol: TradeableAsset): Promise<StockValueData> {
|
|
34
|
-
return {
|
|
35
|
-
amount: 0,
|
|
36
|
-
value: 0
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async getVAT(time: Date, transfer: AssetTransfer, currency: Currency): Promise<null | number> {
|
|
41
|
-
return null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async getRate(time: Date, type: AssetType, asset: Asset, currency: Currency, exchange: AssetExchange|null = null): Promise<number> {
|
|
45
|
-
return 1.0
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async getTranslation(text: string, language: string): Promise<string> {
|
|
49
|
-
// Borrow a plugins to get some translations.
|
|
50
|
-
const plugin = new ImportPlugin(new CoinbaseHandler() as unknown as TransactionImportHandler)
|
|
51
|
-
const ret = plugin.t(text, language as Language)
|
|
52
|
-
if (ret !== text) {
|
|
53
|
-
return ret
|
|
54
|
-
}
|
|
55
|
-
const IEplugin = new IncomeAndExpenses()
|
|
56
|
-
return IEplugin.t(text, language as Language)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async applyResult(): Promise<Record<string, unknown>> {
|
|
60
|
-
return {}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async resultExists(processId: ID, args: unknown): Promise<boolean> {
|
|
64
|
-
return false
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async success(res: ImportState) {
|
|
68
|
-
//
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async fail(res: ImportState) {
|
|
72
|
-
//
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async waiting(res: ImportState, directions: Directions) {
|
|
76
|
-
//
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async run(): Promise<void> {
|
|
80
|
-
//
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async rollback(processId: ID): Promise<boolean> {
|
|
84
|
-
return true
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { AccountAddress, AccountNumber, AdditionalTransferInfo, AssetTransfer, StockValueData, Transaction, TransactionDescription, TransactionLine, ImportStateText, ImportConfig } from '@tasenor/common'
|
|
2
|
-
import { TransactionImportHandler, AskUI, Process } from '..'
|
|
3
|
-
import { getTestHandler } from './test-handlers'
|
|
4
|
-
import { SystemMock } from './ProcessingSystemMock'
|
|
5
|
-
import { sprintf } from 'sprintf-js'
|
|
6
|
-
import { UnitTestImportConnector } from './UnitTestImportConnector'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Helper types to reduce type forcing in simple test data.
|
|
10
|
-
*/
|
|
11
|
-
export type AssetTransferTestData = Omit<Omit<AssetTransfer, 'asset'>, 'amount'> & { asset: string, amount?: number | undefined | null }
|
|
12
|
-
|
|
13
|
-
export type AdditionalTransferInfoTestData = Omit<AdditionalTransferInfo, 'stock'> & {
|
|
14
|
-
stock?: {
|
|
15
|
-
set?: Partial<Record<string, StockValueData>>
|
|
16
|
-
change?: Partial<Record<string, StockValueData>>
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type TransactionLineTestData = Omit<Omit<TransactionLine, 'data'>, 'account'> & {
|
|
21
|
-
account: string
|
|
22
|
-
data?: AdditionalTransferInfoTestData
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* A container for setting up testing system.
|
|
27
|
-
*/
|
|
28
|
-
export class UnitTester {
|
|
29
|
-
|
|
30
|
-
static accounts: string[] = [
|
|
31
|
-
'deposit.external.EUR',
|
|
32
|
-
'deposit.currency.EUR',
|
|
33
|
-
'expense.statement.TRADE_LOSS_STOCK',
|
|
34
|
-
'fee.currency.EUR',
|
|
35
|
-
'fee.currency.USD',
|
|
36
|
-
'income.statement.TRADE_PROFIT_CRYPTO',
|
|
37
|
-
'income.statement.TRADE_PROFIT_SHORT',
|
|
38
|
-
'trade.crypto.ETH',
|
|
39
|
-
'trade.currency.EUR',
|
|
40
|
-
'trade.currency.USD',
|
|
41
|
-
'trade.short.NAKD',
|
|
42
|
-
'trade.stock.NAKD',
|
|
43
|
-
]
|
|
44
|
-
|
|
45
|
-
accountNumber: Record<string, string> = {}
|
|
46
|
-
accountName: Record<string, string> = {}
|
|
47
|
-
config: ImportConfig
|
|
48
|
-
handler: TransactionImportHandler
|
|
49
|
-
process: Process
|
|
50
|
-
|
|
51
|
-
constructor(conf: Record<string, unknown> = {}) {
|
|
52
|
-
this.config = {
|
|
53
|
-
currency: 'EUR',
|
|
54
|
-
language: 'en',
|
|
55
|
-
isTradeFeePartOfTotal: false,
|
|
56
|
-
allowShortSelling: false,
|
|
57
|
-
recordDeposits: true,
|
|
58
|
-
rules: []
|
|
59
|
-
}
|
|
60
|
-
Object.assign(this.config, conf)
|
|
61
|
-
let number = 1000
|
|
62
|
-
this.accountName = {}
|
|
63
|
-
this.accountNumber = {}
|
|
64
|
-
for (const name of UnitTester.accounts) {
|
|
65
|
-
this.config[`account.${name}`] = `${number}`
|
|
66
|
-
this.accountName[`${number}`] = name
|
|
67
|
-
this.accountNumber[name] = `${number}`
|
|
68
|
-
number += 10
|
|
69
|
-
}
|
|
70
|
-
this.handler = getTestHandler('Coinbase')
|
|
71
|
-
this.handler.system = new SystemMock()
|
|
72
|
-
this.process = new Process(this.handler.system, 'Unit test', this.config)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Set the configuration value. Also add mapping if account.
|
|
77
|
-
* @param name
|
|
78
|
-
* @param value
|
|
79
|
-
*/
|
|
80
|
-
set(name, value) {
|
|
81
|
-
this.config[name] = value
|
|
82
|
-
if (name.startsWith('account.')) {
|
|
83
|
-
if (this.accountName[value]) {
|
|
84
|
-
throw new Error(`Account number ${value} already exists. Please choose some other for ${name}.`)
|
|
85
|
-
}
|
|
86
|
-
this.accountName[value] = name.substr(8)
|
|
87
|
-
this.accountNumber[name.substr(8)] = value
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Add an answer to the answer collection.
|
|
93
|
-
*/
|
|
94
|
-
answer(segment, name, value) {
|
|
95
|
-
this.config.answers = this.config.answers || {}
|
|
96
|
-
if (this.config.answers) {
|
|
97
|
-
this.config.answers[segment] = this.config.answers[segment] || {};
|
|
98
|
-
(this.config.answers[segment] as Record<string, unknown>)[name] = value
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Get the account balance.
|
|
104
|
-
* @param name
|
|
105
|
-
*/
|
|
106
|
-
get(addr): number {
|
|
107
|
-
const number = this.accountNumber[addr]
|
|
108
|
-
if (!number) {
|
|
109
|
-
throw new Error(`Invalid account name ${addr}.`)
|
|
110
|
-
}
|
|
111
|
-
return this.handler.getBalance(addr)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Add to the initial balance.
|
|
116
|
-
* @param accunt
|
|
117
|
-
* @param amount
|
|
118
|
-
*/
|
|
119
|
-
addMoney(account: string, amount: number) {
|
|
120
|
-
if (!this.accountNumber[`${account}`]) {
|
|
121
|
-
throw new Error(`Account ${account} not defined in UnitTester class.`)
|
|
122
|
-
}
|
|
123
|
-
(this.handler.system.connector as UnitTestImportConnector).addMoney(account as AccountAddress, this.accountNumber[`${account}`] as AccountNumber, amount)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Construct a state for transfers.
|
|
128
|
-
* @param transfers
|
|
129
|
-
*/
|
|
130
|
-
makeState(transferSets: AssetTransfer[][]): ImportStateText<'classified'> {
|
|
131
|
-
const state: ImportStateText<'classified'> = {
|
|
132
|
-
stage: 'classified',
|
|
133
|
-
files: {
|
|
134
|
-
'test.csv': {
|
|
135
|
-
lines: []
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
state.segments = {}
|
|
141
|
-
state.result = {}
|
|
142
|
-
|
|
143
|
-
for (let i = 0; i < transferSets.length; i++) {
|
|
144
|
-
const id = `segment${i}`
|
|
145
|
-
state.segments[id] = {
|
|
146
|
-
id,
|
|
147
|
-
time: new Date(`2022-01-01T00:00:${sprintf('%02d', i)}.000Z`),
|
|
148
|
-
lines: []
|
|
149
|
-
}
|
|
150
|
-
state.result[id] = [{
|
|
151
|
-
type: 'transfers',
|
|
152
|
-
transfers: transferSets[i]
|
|
153
|
-
}]
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return state
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Execute one or more transfer analysis.
|
|
161
|
-
* @param transfers
|
|
162
|
-
* @param entries
|
|
163
|
-
* @returns
|
|
164
|
-
*/
|
|
165
|
-
async test(transfers: AssetTransferTestData[], entries: TransactionLineTestData[][],
|
|
166
|
-
transfers2: AssetTransferTestData[] | undefined = undefined, entries2: TransactionLineTestData[][] | undefined = undefined,
|
|
167
|
-
transfers3: AssetTransferTestData[] | undefined = undefined, entries3: TransactionLineTestData[][] | undefined = undefined,
|
|
168
|
-
transfers4: AssetTransferTestData[] | undefined = undefined, entries4: TransactionLineTestData[][] | undefined = undefined,
|
|
169
|
-
transfers5: AssetTransferTestData[] | undefined = undefined, entries5: TransactionLineTestData[][] | undefined = undefined
|
|
170
|
-
) {
|
|
171
|
-
const transfersSets: AssetTransfer[][] = [transfers as AssetTransfer[]]
|
|
172
|
-
const entriesSet: TransactionLine[][][] = [entries as TransactionLine[][]]
|
|
173
|
-
if (transfers2) transfersSets.push(transfers2 as AssetTransfer[])
|
|
174
|
-
if (transfers3) transfersSets.push(transfers3 as AssetTransfer[])
|
|
175
|
-
if (transfers4) transfersSets.push(transfers4 as AssetTransfer[])
|
|
176
|
-
if (transfers5) transfersSets.push(transfers5 as AssetTransfer[])
|
|
177
|
-
if (entries2) entriesSet.push(entries2 as TransactionLine[][])
|
|
178
|
-
if (entries3) entriesSet.push(entries3 as TransactionLine[][])
|
|
179
|
-
if (entries4) entriesSet.push(entries4 as TransactionLine[][])
|
|
180
|
-
if (entries5) entriesSet.push(entries5 as TransactionLine[][])
|
|
181
|
-
|
|
182
|
-
const state = this.makeState(transfersSets)
|
|
183
|
-
// Check just for compiler.
|
|
184
|
-
if (!state.result || !state.segments) {
|
|
185
|
-
return
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
let out
|
|
189
|
-
try {
|
|
190
|
-
out = await this.handler.analysis(this.process, state, [], this.config)
|
|
191
|
-
} catch (err) {
|
|
192
|
-
if (err instanceof AskUI) {
|
|
193
|
-
console.log('\u001b[31mTest incomplete. Need more configuration.\u001b[0m')
|
|
194
|
-
console.dir(err.element, { depth: null })
|
|
195
|
-
}
|
|
196
|
-
throw err
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (!out.result) {
|
|
200
|
-
throw new Error(`Analyse failed to get results from ${JSON.stringify(state)}.`)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
for (let i = 0; i < entriesSet.length; i++) {
|
|
204
|
-
const id = `segment${i}`
|
|
205
|
-
if (!out.result[id]) {
|
|
206
|
-
throw new Error(`Analyse failed to get results from ${JSON.stringify(state)}.`)
|
|
207
|
-
}
|
|
208
|
-
// Convert accounts to symbolic to make test writing easier.
|
|
209
|
-
for (const data of out.result[id] as TransactionDescription[]) {
|
|
210
|
-
if (data.transactions) {
|
|
211
|
-
for (const tx of data.transactions) {
|
|
212
|
-
for (const entry of tx.entries) {
|
|
213
|
-
if (this.accountName[entry.account]) {
|
|
214
|
-
entry.account = this.accountName[entry.account] as AccountNumber
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
// Verify results.
|
|
221
|
-
const result: Transaction[] = entriesSet[i].map(entries => ({
|
|
222
|
-
date: new Date(`2022-01-01T00:00:${sprintf('%02d', i)}.000Z`),
|
|
223
|
-
segmentId: id,
|
|
224
|
-
entries
|
|
225
|
-
}))
|
|
226
|
-
|
|
227
|
-
const { expect } = await import('@jest/globals')
|
|
228
|
-
expect(result).toStrictEqual(out.result[id][0].transactions)
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
package/src/testing/index.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
import { CoinbaseHandler } from '../../../tasenor-common-plugins/src/CoinbaseImport/backend/CoinbaseHandler'
|
|
4
|
-
import { KrakenHandler } from '../../../tasenor-common-plugins/src/KrakenImport/backend/KrakenHandler'
|
|
5
|
-
import { LynxHandler } from '../../../tasenor-common-plugins/src/LynxImport/backend/LynxHandler'
|
|
6
|
-
import { NordeaHandler } from '../../../tasenor-common-plugins/src/NordeaImport/backend/NordeaHandler'
|
|
7
|
-
import { NordnetHandler } from '../../../tasenor-common-plugins/src/NordnetImport/backend/NordnetHandler'
|
|
8
|
-
import { TITOHandler } from '../../../tasenor-common-plugins/src/TITOImport/backend/TITOHandler'
|
|
9
|
-
import { TransactionImportHandler } from '..'
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Mapping from handler class names to instances.
|
|
13
|
-
*/
|
|
14
|
-
const handlers: Record<string, TransactionImportHandler> = {
|
|
15
|
-
// Typescript commonly screws up, if there are slight version differencies. Force type.
|
|
16
|
-
Coinbase: new CoinbaseHandler() as unknown as TransactionImportHandler,
|
|
17
|
-
Nordea: new NordeaHandler() as unknown as TransactionImportHandler,
|
|
18
|
-
Nordnet: new NordnetHandler() as unknown as TransactionImportHandler,
|
|
19
|
-
Lynx: new LynxHandler() as unknown as TransactionImportHandler,
|
|
20
|
-
Kraken: new KrakenHandler() as unknown as TransactionImportHandler,
|
|
21
|
-
TITO: new TITOHandler() as unknown as TransactionImportHandler,
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Get a handler instance for testing.
|
|
26
|
-
* @param name
|
|
27
|
-
* @returns
|
|
28
|
-
*/
|
|
29
|
-
export function getTestHandler(className: string): TransactionImportHandler {
|
|
30
|
-
if (!handlers[className]) {
|
|
31
|
-
throw new Error(`No such import handler as '${className}'.`)
|
|
32
|
-
}
|
|
33
|
-
return handlers[className]
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get handler path.
|
|
38
|
-
* @param className
|
|
39
|
-
* @returns
|
|
40
|
-
*/
|
|
41
|
-
export function getTestHandlerPath(className: string): string {
|
|
42
|
-
const dirName = path.join(__dirname, '..', '..', '..', 'tasenor-common-plugins', 'src', `${className}Import`, 'backend')
|
|
43
|
-
if (!fs.existsSync(dirName)) {
|
|
44
|
-
throw new Error(`No such import handler path as '${className}'.`)
|
|
45
|
-
}
|
|
46
|
-
return dirName
|
|
47
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { UnitTester } from '../src/testing'
|
|
2
|
-
|
|
3
|
-
test('Direct expense account address', async () => {
|
|
4
|
-
|
|
5
|
-
const tester = new UnitTester()
|
|
6
|
-
|
|
7
|
-
await tester.test(
|
|
8
|
-
// Use direct reference to the account.
|
|
9
|
-
[
|
|
10
|
-
{
|
|
11
|
-
reason: 'expense',
|
|
12
|
-
type: 'account',
|
|
13
|
-
asset: '112233',
|
|
14
|
-
amount: -200,
|
|
15
|
-
data: {
|
|
16
|
-
text: 'My expense paid'
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
reason: 'expense',
|
|
21
|
-
type: 'account',
|
|
22
|
-
asset: '445566',
|
|
23
|
-
amount: 200
|
|
24
|
-
}
|
|
25
|
-
], [[
|
|
26
|
-
{
|
|
27
|
-
account: '112233',
|
|
28
|
-
amount: -20000,
|
|
29
|
-
description: 'My expense paid',
|
|
30
|
-
data: {
|
|
31
|
-
text: 'My expense paid'
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
account: '445566',
|
|
36
|
-
amount: 20000,
|
|
37
|
-
description: 'My expense paid',
|
|
38
|
-
}
|
|
39
|
-
]]
|
|
40
|
-
)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
test('Direct income account address', async () => {
|
|
44
|
-
|
|
45
|
-
const tester = new UnitTester()
|
|
46
|
-
|
|
47
|
-
await tester.test(
|
|
48
|
-
// Use direct reference to the account.
|
|
49
|
-
[
|
|
50
|
-
{
|
|
51
|
-
reason: 'income',
|
|
52
|
-
type: 'account',
|
|
53
|
-
asset: '112233',
|
|
54
|
-
amount: -200,
|
|
55
|
-
data: {
|
|
56
|
-
text: 'My income paid.'
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
reason: 'income',
|
|
61
|
-
type: 'account',
|
|
62
|
-
asset: '445566',
|
|
63
|
-
amount: 200,
|
|
64
|
-
data: {
|
|
65
|
-
text: 'Yes it is.'
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
], [[
|
|
69
|
-
{
|
|
70
|
-
account: '112233',
|
|
71
|
-
amount: -20000,
|
|
72
|
-
description: 'My income paid. Yes it is.',
|
|
73
|
-
data: {
|
|
74
|
-
text: 'My income paid.'
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
account: '445566',
|
|
79
|
-
amount: 20000,
|
|
80
|
-
description: 'My income paid. Yes it is.',
|
|
81
|
-
data: {
|
|
82
|
-
text: 'Yes it is.'
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
]]
|
|
86
|
-
)
|
|
87
|
-
})
|
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
import { UnitTester } from '../src/testing'
|
|
2
|
-
|
|
3
|
-
test('Buying and selling', async () => {
|
|
4
|
-
|
|
5
|
-
const tester = new UnitTester()
|
|
6
|
-
|
|
7
|
-
tester.addMoney('trade.currency.EUR', 1000000)
|
|
8
|
-
|
|
9
|
-
await tester.test(
|
|
10
|
-
// Buy 1.5 ETH for 200€ and 1€ fee.
|
|
11
|
-
[
|
|
12
|
-
{
|
|
13
|
-
reason: 'trade',
|
|
14
|
-
type: 'currency',
|
|
15
|
-
asset: 'EUR',
|
|
16
|
-
amount: -200
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
reason: 'trade',
|
|
20
|
-
type: 'crypto',
|
|
21
|
-
asset: 'ETH',
|
|
22
|
-
amount: 1.5
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
reason: 'fee',
|
|
26
|
-
type: 'currency',
|
|
27
|
-
asset: 'EUR',
|
|
28
|
-
amount: 1
|
|
29
|
-
}
|
|
30
|
-
], [[
|
|
31
|
-
{
|
|
32
|
-
account: 'trade.currency.EUR',
|
|
33
|
-
amount: -20100,
|
|
34
|
-
description: 'Buy +1.5 ETH',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
account: 'trade.crypto.ETH',
|
|
38
|
-
amount: 20000,
|
|
39
|
-
data: {
|
|
40
|
-
stock: {
|
|
41
|
-
change: {
|
|
42
|
-
ETH: {
|
|
43
|
-
amount: 1.5,
|
|
44
|
-
value: 20000,
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
description: 'Buy +1.5 ETH',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
account: 'fee.currency.EUR',
|
|
53
|
-
amount: 100,
|
|
54
|
-
description: 'Buy +1.5 ETH',
|
|
55
|
-
}
|
|
56
|
-
]],
|
|
57
|
-
// Sell 1.5 ETH for 250€ and 1€ fee.
|
|
58
|
-
[
|
|
59
|
-
{
|
|
60
|
-
reason: 'trade',
|
|
61
|
-
type: 'currency',
|
|
62
|
-
asset: 'EUR',
|
|
63
|
-
amount: 251
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
reason: 'trade',
|
|
67
|
-
type: 'crypto',
|
|
68
|
-
asset: 'ETH',
|
|
69
|
-
amount: -1.5
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
reason: 'fee',
|
|
73
|
-
type: 'currency',
|
|
74
|
-
asset: 'EUR',
|
|
75
|
-
amount: 1
|
|
76
|
-
}
|
|
77
|
-
], [[
|
|
78
|
-
{
|
|
79
|
-
account: 'trade.currency.EUR',
|
|
80
|
-
amount: 25000,
|
|
81
|
-
description: 'Sell -1.5 ETH',
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
account: 'trade.crypto.ETH',
|
|
85
|
-
amount: -20000,
|
|
86
|
-
data: {
|
|
87
|
-
stock: {
|
|
88
|
-
change: {
|
|
89
|
-
ETH: {
|
|
90
|
-
amount: -1.5,
|
|
91
|
-
value: -20000,
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
description: 'Sell -1.5 ETH',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
account: 'fee.currency.EUR',
|
|
100
|
-
amount: 100,
|
|
101
|
-
description: 'Sell -1.5 ETH',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
account: 'income.statement.TRADE_PROFIT_CRYPTO',
|
|
105
|
-
amount: -5100,
|
|
106
|
-
description: 'Sell -1.5 ETH',
|
|
107
|
-
}
|
|
108
|
-
]],
|
|
109
|
-
// Buy 5 NAKD for 300€, no fee.
|
|
110
|
-
[
|
|
111
|
-
{
|
|
112
|
-
reason: 'trade',
|
|
113
|
-
type: 'currency',
|
|
114
|
-
asset: 'EUR',
|
|
115
|
-
amount: -300
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
reason: 'trade',
|
|
119
|
-
type: 'stock',
|
|
120
|
-
asset: 'NAKD',
|
|
121
|
-
amount: 5
|
|
122
|
-
}
|
|
123
|
-
], [[
|
|
124
|
-
{
|
|
125
|
-
account: 'trade.currency.EUR',
|
|
126
|
-
amount: -30000,
|
|
127
|
-
description: 'Buy +5 NAKD',
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
account: 'trade.stock.NAKD',
|
|
131
|
-
amount: 30000,
|
|
132
|
-
data: {
|
|
133
|
-
stock: {
|
|
134
|
-
change: {
|
|
135
|
-
NAKD: {
|
|
136
|
-
amount: 5,
|
|
137
|
-
value: 30000,
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
description: 'Buy +5 NAKD',
|
|
143
|
-
},
|
|
144
|
-
]],
|
|
145
|
-
// Sell 5 NAKD for 200€, no fee.
|
|
146
|
-
[
|
|
147
|
-
{
|
|
148
|
-
reason: 'trade',
|
|
149
|
-
type: 'currency',
|
|
150
|
-
asset: 'EUR',
|
|
151
|
-
amount: 200
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
reason: 'trade',
|
|
155
|
-
type: 'stock',
|
|
156
|
-
asset: 'NAKD',
|
|
157
|
-
amount: -5
|
|
158
|
-
}
|
|
159
|
-
], [[
|
|
160
|
-
{
|
|
161
|
-
account: 'trade.currency.EUR',
|
|
162
|
-
amount: 20000,
|
|
163
|
-
description: 'Sell -5 NAKD',
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
account: 'trade.stock.NAKD',
|
|
167
|
-
amount: -30000,
|
|
168
|
-
data: {
|
|
169
|
-
stock: {
|
|
170
|
-
change: {
|
|
171
|
-
NAKD: {
|
|
172
|
-
amount: -5,
|
|
173
|
-
value: -30000,
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
description: 'Sell -5 NAKD',
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
account: 'expense.statement.TRADE_LOSS_STOCK',
|
|
182
|
-
amount: 10000,
|
|
183
|
-
description: 'Sell -5 NAKD',
|
|
184
|
-
},
|
|
185
|
-
]]
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
expect(tester.get('trade.currency.EUR')).toBe(994900)
|
|
189
|
-
expect(tester.get('trade.stock.NAKD')).toBe(0)
|
|
190
|
-
expect(tester.get('expense.statement.TRADE_LOSS_STOCK')).toBe(10000)
|
|
191
|
-
expect(tester.get('trade.crypto.ETH')).toBe(0)
|
|
192
|
-
expect(tester.get('fee.currency.EUR')).toBe(200)
|
|
193
|
-
expect(tester.get('income.statement.TRADE_PROFIT_CRYPTO')).toBe(-5100)
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
test('Short selling', async () => {
|
|
197
|
-
|
|
198
|
-
const tester = new UnitTester()
|
|
199
|
-
|
|
200
|
-
tester.set('allowShortSelling', true)
|
|
201
|
-
tester.answer('segment0', 'hasBeenRenamed.stock.NAKD', false)
|
|
202
|
-
|
|
203
|
-
await tester.test(
|
|
204
|
-
// Short sell 100 NAKD for $1000 with $5 fee.
|
|
205
|
-
[
|
|
206
|
-
{
|
|
207
|
-
reason: 'trade',
|
|
208
|
-
type: 'currency',
|
|
209
|
-
asset: 'USD',
|
|
210
|
-
amount: 1000
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
reason: 'trade',
|
|
214
|
-
type: 'stock',
|
|
215
|
-
asset: 'NAKD',
|
|
216
|
-
amount: -100
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
reason: 'fee',
|
|
220
|
-
type: 'currency',
|
|
221
|
-
asset: 'USD',
|
|
222
|
-
amount: 5
|
|
223
|
-
}
|
|
224
|
-
],
|
|
225
|
-
[[
|
|
226
|
-
{
|
|
227
|
-
account: 'trade.currency.USD',
|
|
228
|
-
amount: 99500,
|
|
229
|
-
data: {
|
|
230
|
-
currency: 'USD',
|
|
231
|
-
currencyValue: 99500,
|
|
232
|
-
rates: {
|
|
233
|
-
USD: 1,
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
description: 'Short selling -100 NAKD',
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
account: 'trade.short.NAKD',
|
|
240
|
-
amount: -100000,
|
|
241
|
-
data: {
|
|
242
|
-
currency: 'USD',
|
|
243
|
-
currencyValue: -100000,
|
|
244
|
-
rates: {
|
|
245
|
-
USD: 1,
|
|
246
|
-
},
|
|
247
|
-
stock: {
|
|
248
|
-
change: {
|
|
249
|
-
NAKD: {
|
|
250
|
-
amount: -100,
|
|
251
|
-
value: -100000,
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
},
|
|
256
|
-
description: 'Short selling -100 NAKD',
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
account: 'fee.currency.USD',
|
|
260
|
-
amount: 500,
|
|
261
|
-
data: {
|
|
262
|
-
currency: 'USD',
|
|
263
|
-
currencyValue: 500,
|
|
264
|
-
rates: {
|
|
265
|
-
USD: 1,
|
|
266
|
-
},
|
|
267
|
-
},
|
|
268
|
-
description: 'Short selling -100 NAKD',
|
|
269
|
-
},
|
|
270
|
-
]], [
|
|
271
|
-
// Buy 100 NAKD back for $900 and $5 fee.
|
|
272
|
-
{
|
|
273
|
-
reason: 'trade',
|
|
274
|
-
type: 'currency',
|
|
275
|
-
asset: 'USD',
|
|
276
|
-
amount: -900
|
|
277
|
-
},
|
|
278
|
-
{
|
|
279
|
-
reason: 'trade',
|
|
280
|
-
type: 'stock',
|
|
281
|
-
asset: 'NAKD',
|
|
282
|
-
amount: 100
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
reason: 'fee',
|
|
286
|
-
type: 'currency',
|
|
287
|
-
asset: 'USD',
|
|
288
|
-
amount: 5
|
|
289
|
-
}
|
|
290
|
-
], [[
|
|
291
|
-
{
|
|
292
|
-
account: 'trade.currency.USD',
|
|
293
|
-
amount: -90500,
|
|
294
|
-
data: {
|
|
295
|
-
currency: 'USD',
|
|
296
|
-
currencyValue: -90500,
|
|
297
|
-
rates: {
|
|
298
|
-
USD: 1,
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
description: 'Closing short position +100 NAKD',
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
account: 'trade.short.NAKD',
|
|
305
|
-
amount: 100000,
|
|
306
|
-
data: {
|
|
307
|
-
currency: 'USD',
|
|
308
|
-
currencyValue: 100000,
|
|
309
|
-
rates: {
|
|
310
|
-
USD: 1,
|
|
311
|
-
},
|
|
312
|
-
stock: {
|
|
313
|
-
change: {
|
|
314
|
-
NAKD: {
|
|
315
|
-
amount: 100,
|
|
316
|
-
value: 100000,
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
},
|
|
321
|
-
description: 'Closing short position +100 NAKD',
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
account: 'fee.currency.USD',
|
|
325
|
-
amount: 500,
|
|
326
|
-
data: {
|
|
327
|
-
currency: 'USD',
|
|
328
|
-
currencyValue: 500,
|
|
329
|
-
rates: {
|
|
330
|
-
USD: 1,
|
|
331
|
-
},
|
|
332
|
-
},
|
|
333
|
-
description: 'Closing short position +100 NAKD',
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
account: 'income.statement.TRADE_PROFIT_SHORT',
|
|
337
|
-
amount: -10000,
|
|
338
|
-
data: {
|
|
339
|
-
currency: 'USD',
|
|
340
|
-
currencyValue: -10000,
|
|
341
|
-
rates: {
|
|
342
|
-
USD: 1,
|
|
343
|
-
},
|
|
344
|
-
},
|
|
345
|
-
description: 'Closing short position +100 NAKD',
|
|
346
|
-
},
|
|
347
|
-
]],
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
expect(tester.get('trade.currency.USD')).toBe(9000)
|
|
351
|
-
expect(tester.get('fee.currency.USD')).toBe(1000)
|
|
352
|
-
expect(tester.get('income.statement.TRADE_PROFIT_SHORT')).toBe(-10000)
|
|
353
|
-
expect(tester.get('trade.short.NAKD')).toBe(0)
|
|
354
|
-
})
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { UnitTester } from '../src/testing'
|
|
2
|
-
|
|
3
|
-
test('Use partly debt to buy assets', async () => {
|
|
4
|
-
|
|
5
|
-
const tester = new UnitTester()
|
|
6
|
-
|
|
7
|
-
tester.addMoney('trade.currency.EUR', 10000)
|
|
8
|
-
// Set up debt account.
|
|
9
|
-
tester.set('account.debt.currency.EUR', '8001')
|
|
10
|
-
|
|
11
|
-
await tester.test(
|
|
12
|
-
// Buy 2.0 ETH for 200€ when we have only 100€ on the account.
|
|
13
|
-
[
|
|
14
|
-
{
|
|
15
|
-
reason: 'trade',
|
|
16
|
-
type: 'currency',
|
|
17
|
-
asset: 'EUR',
|
|
18
|
-
amount: -200
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
reason: 'trade',
|
|
22
|
-
type: 'crypto',
|
|
23
|
-
asset: 'ETH',
|
|
24
|
-
amount: 2.0
|
|
25
|
-
},
|
|
26
|
-
], [[
|
|
27
|
-
{
|
|
28
|
-
account: 'trade.currency.EUR',
|
|
29
|
-
amount: -10000,
|
|
30
|
-
description: 'Buy +2 ETH',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
account: 'trade.crypto.ETH',
|
|
34
|
-
amount: 20000,
|
|
35
|
-
data: {
|
|
36
|
-
stock: {
|
|
37
|
-
change: {
|
|
38
|
-
ETH: {
|
|
39
|
-
amount: 2,
|
|
40
|
-
value: 20000,
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
description: 'Buy +2 ETH',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
account: 'debt.currency.EUR',
|
|
49
|
-
amount: -10000,
|
|
50
|
-
description: 'Buy +2 ETH',
|
|
51
|
-
},
|
|
52
|
-
]]
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
expect(tester.get('trade.currency.EUR')).toBe(0)
|
|
56
|
-
expect(tester.get('debt.currency.EUR')).toBe(-10000)
|
|
57
|
-
expect(tester.get('trade.crypto.ETH')).toBe(20000)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
test('Use all debt to buy assets', async () => {
|
|
61
|
-
|
|
62
|
-
const tester = new UnitTester()
|
|
63
|
-
|
|
64
|
-
tester.addMoney('trade.currency.EUR', 0)
|
|
65
|
-
// Set up debt account.
|
|
66
|
-
tester.set('account.debt.currency.EUR', '8001')
|
|
67
|
-
|
|
68
|
-
await tester.test(
|
|
69
|
-
// Buy 2.0 ETH for 200€ when we have only 0€ on the account.
|
|
70
|
-
[
|
|
71
|
-
{
|
|
72
|
-
reason: 'trade',
|
|
73
|
-
type: 'currency',
|
|
74
|
-
asset: 'EUR',
|
|
75
|
-
amount: -200
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
reason: 'trade',
|
|
79
|
-
type: 'crypto',
|
|
80
|
-
asset: 'ETH',
|
|
81
|
-
amount: 2.0
|
|
82
|
-
},
|
|
83
|
-
], [[
|
|
84
|
-
{
|
|
85
|
-
account: 'debt.currency.EUR',
|
|
86
|
-
amount: -20000,
|
|
87
|
-
description: 'Buy +2 ETH',
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
account: 'trade.crypto.ETH',
|
|
91
|
-
amount: 20000,
|
|
92
|
-
data: {
|
|
93
|
-
stock: {
|
|
94
|
-
change: {
|
|
95
|
-
ETH: {
|
|
96
|
-
amount: 2,
|
|
97
|
-
value: 20000,
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
description: 'Buy +2 ETH',
|
|
103
|
-
},
|
|
104
|
-
]]
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
expect(tester.get('trade.currency.EUR')).toBe(0)
|
|
108
|
-
expect(tester.get('debt.currency.EUR')).toBe(-20000)
|
|
109
|
-
expect(tester.get('trade.crypto.ETH')).toBe(20000)
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
test('Pay back debt more than fully', async () => {
|
|
113
|
-
|
|
114
|
-
const tester = new UnitTester()
|
|
115
|
-
|
|
116
|
-
tester.set('account.debt.currency.EUR', '8001')
|
|
117
|
-
tester.addMoney('debt.currency.EUR', -10000)
|
|
118
|
-
|
|
119
|
-
await tester.test(
|
|
120
|
-
// Deposit 200€ to account having -100€ debt.
|
|
121
|
-
[
|
|
122
|
-
{
|
|
123
|
-
reason: 'deposit',
|
|
124
|
-
type: 'external',
|
|
125
|
-
asset: 'EUR',
|
|
126
|
-
amount: -200
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
reason: 'deposit',
|
|
130
|
-
type: 'currency',
|
|
131
|
-
asset: 'EUR',
|
|
132
|
-
amount: 200
|
|
133
|
-
}
|
|
134
|
-
], [[
|
|
135
|
-
{
|
|
136
|
-
account: 'deposit.external.EUR',
|
|
137
|
-
amount: -20000,
|
|
138
|
-
description: 'Deposit to Coinbase service',
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
account: 'deposit.currency.EUR',
|
|
142
|
-
amount: 10000,
|
|
143
|
-
description: 'Deposit to Coinbase service',
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
account: 'debt.currency.EUR',
|
|
147
|
-
amount: 10000,
|
|
148
|
-
description: 'Deposit to Coinbase service',
|
|
149
|
-
}
|
|
150
|
-
]]
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
expect(tester.get('deposit.external.EUR')).toBe(-20000)
|
|
154
|
-
expect(tester.get('deposit.currency.EUR')).toBe(10000)
|
|
155
|
-
expect(tester.get('debt.currency.EUR')).toBe(0)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
test('Pay back partial debt', async () => {
|
|
159
|
-
|
|
160
|
-
const tester = new UnitTester()
|
|
161
|
-
|
|
162
|
-
tester.set('account.debt.currency.EUR', '8001')
|
|
163
|
-
tester.addMoney('debt.currency.EUR', -30000)
|
|
164
|
-
|
|
165
|
-
await tester.test(
|
|
166
|
-
// Deposit 200€ to account having -300€ debt.
|
|
167
|
-
[
|
|
168
|
-
{
|
|
169
|
-
reason: 'deposit',
|
|
170
|
-
type: 'external',
|
|
171
|
-
asset: 'EUR',
|
|
172
|
-
amount: -200
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
reason: 'deposit',
|
|
176
|
-
type: 'currency',
|
|
177
|
-
asset: 'EUR',
|
|
178
|
-
amount: 200
|
|
179
|
-
}
|
|
180
|
-
], [[
|
|
181
|
-
{
|
|
182
|
-
account: 'deposit.external.EUR',
|
|
183
|
-
amount: -20000,
|
|
184
|
-
description: 'Deposit to Coinbase service',
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
account: 'debt.currency.EUR',
|
|
188
|
-
amount: 20000,
|
|
189
|
-
description: 'Deposit to Coinbase service',
|
|
190
|
-
}
|
|
191
|
-
]]
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
expect(tester.get('deposit.external.EUR')).toBe(-20000)
|
|
195
|
-
expect(tester.get('deposit.currency.EUR')).toBe(0)
|
|
196
|
-
expect(tester.get('debt.currency.EUR')).toBe(-10000)
|
|
197
|
-
})
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { Asset } from '@tasenor/common'
|
|
2
|
-
import { UnitTester } from '../src/testing'
|
|
3
|
-
|
|
4
|
-
test('Handle complex multipart dividend', async () => {
|
|
5
|
-
|
|
6
|
-
const tester = new UnitTester()
|
|
7
|
-
|
|
8
|
-
tester.set('account.dividend.currency.USD', '5000')
|
|
9
|
-
tester.set('account.income.statement.LISTED_CASH_DIVIDEND', '5001')
|
|
10
|
-
tester.set('account.tax.statement.WITHHOLDING_TAX', '6000')
|
|
11
|
-
|
|
12
|
-
await tester.test([
|
|
13
|
-
{
|
|
14
|
-
reason: 'dividend',
|
|
15
|
-
type: 'currency',
|
|
16
|
-
asset: 'USD',
|
|
17
|
-
amount: null,
|
|
18
|
-
data: {
|
|
19
|
-
text: 'CORR PRA(US21870U3041) Cash Dividend USD 0.460938 per Share (Ordinary Dividend)'
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
reason: 'income',
|
|
24
|
-
type: 'statement',
|
|
25
|
-
asset: 'LISTED_CASH_DIVIDEND',
|
|
26
|
-
data: {
|
|
27
|
-
text: 'CORR PRA(US21870U3041) Cash Dividend USD 0.460938 per Share (Ordinary Dividend)',
|
|
28
|
-
currency: 'USD',
|
|
29
|
-
currencyValue: -8712,
|
|
30
|
-
count: 189,
|
|
31
|
-
asset: 'CORR PRA' as Asset as Asset,
|
|
32
|
-
perAsset: 0.460938,
|
|
33
|
-
rates: {
|
|
34
|
-
USD: 0.8929
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
value: -7779
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
reason: 'dividend',
|
|
41
|
-
type: 'currency',
|
|
42
|
-
asset: 'USD',
|
|
43
|
-
amount: null,
|
|
44
|
-
data: {
|
|
45
|
-
text: 'CORR PRA(US21870U3041) Payment in Lieu of Dividend (Ordinary Dividend)'
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
reason: 'income',
|
|
50
|
-
type: 'statement',
|
|
51
|
-
asset: 'LISTED_CASH_DIVIDEND',
|
|
52
|
-
data: {
|
|
53
|
-
text: 'CORR PRA(US21870U3041) Payment in Lieu of Dividend (Ordinary Dividend)',
|
|
54
|
-
currency: 'USD',
|
|
55
|
-
currencyValue: -507,
|
|
56
|
-
count: 1,
|
|
57
|
-
asset: 'CORR PRA' as Asset as Asset,
|
|
58
|
-
perAsset: 5.07,
|
|
59
|
-
rates: {
|
|
60
|
-
USD: 0.8929
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
value: -453
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
reason: 'tax',
|
|
67
|
-
type: 'statement',
|
|
68
|
-
asset: 'WITHHOLDING_TAX',
|
|
69
|
-
data: {
|
|
70
|
-
text: 'CORR PRA(US21870U3041) Cash Dividend USD 0.460938 per Share - US Tax',
|
|
71
|
-
currency: 'USD',
|
|
72
|
-
currencyValue: 1307,
|
|
73
|
-
rates: {
|
|
74
|
-
USD: 0.8929
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
value: 1167
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
reason: 'tax',
|
|
81
|
-
type: 'statement',
|
|
82
|
-
asset: 'WITHHOLDING_TAX',
|
|
83
|
-
data: {
|
|
84
|
-
text: 'CORR PRA(US21870U3041) Payment in Lieu of Dividend - US Tax',
|
|
85
|
-
currency: 'USD',
|
|
86
|
-
currencyValue: 76,
|
|
87
|
-
rates: {
|
|
88
|
-
USD: 0.8929
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
value: 68
|
|
92
|
-
}
|
|
93
|
-
],
|
|
94
|
-
[[
|
|
95
|
-
{
|
|
96
|
-
account: 'dividend.currency.USD',
|
|
97
|
-
amount: 6612,
|
|
98
|
-
data: {
|
|
99
|
-
currency: 'USD',
|
|
100
|
-
currencyValue: 7405,
|
|
101
|
-
rates: {
|
|
102
|
-
USD: 0.8929,
|
|
103
|
-
},
|
|
104
|
-
text: 'CORR PRA(US21870U3041) Cash Dividend USD 0.460938 per Share (Ordinary Dividend)',
|
|
105
|
-
},
|
|
106
|
-
description: 'Dividend CORR PRA',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
account: 'income.statement.LISTED_CASH_DIVIDEND',
|
|
110
|
-
amount: -7779,
|
|
111
|
-
data: {
|
|
112
|
-
asset: 'CORR PRA' as Asset,
|
|
113
|
-
count: 189,
|
|
114
|
-
currency: 'USD',
|
|
115
|
-
currencyValue: -8712,
|
|
116
|
-
perAsset: 0.460938,
|
|
117
|
-
rates: {
|
|
118
|
-
USD: 0.8929,
|
|
119
|
-
},
|
|
120
|
-
text: 'CORR PRA(US21870U3041) Cash Dividend USD 0.460938 per Share (Ordinary Dividend)',
|
|
121
|
-
},
|
|
122
|
-
description: 'Dividend CORR PRA',
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
account: 'dividend.currency.USD',
|
|
126
|
-
amount: 385,
|
|
127
|
-
data: {
|
|
128
|
-
currency: 'USD',
|
|
129
|
-
currencyValue: 431,
|
|
130
|
-
rates: {
|
|
131
|
-
USD: 0.8929,
|
|
132
|
-
},
|
|
133
|
-
text: 'CORR PRA(US21870U3041) Payment in Lieu of Dividend (Ordinary Dividend)',
|
|
134
|
-
},
|
|
135
|
-
description: 'Dividend CORR PRA',
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
account: 'income.statement.LISTED_CASH_DIVIDEND',
|
|
139
|
-
amount: -453,
|
|
140
|
-
data: {
|
|
141
|
-
asset: 'CORR PRA' as Asset,
|
|
142
|
-
count: 1,
|
|
143
|
-
currency: 'USD',
|
|
144
|
-
currencyValue: -507,
|
|
145
|
-
perAsset: 5.07,
|
|
146
|
-
rates: {
|
|
147
|
-
USD: 0.8929,
|
|
148
|
-
},
|
|
149
|
-
text: 'CORR PRA(US21870U3041) Payment in Lieu of Dividend (Ordinary Dividend)',
|
|
150
|
-
},
|
|
151
|
-
description: 'Dividend CORR PRA',
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
account: 'tax.statement.WITHHOLDING_TAX',
|
|
155
|
-
amount: 1167,
|
|
156
|
-
data: {
|
|
157
|
-
currency: 'USD',
|
|
158
|
-
currencyValue: 1307,
|
|
159
|
-
rates: {
|
|
160
|
-
USD: 0.8929,
|
|
161
|
-
},
|
|
162
|
-
text: 'CORR PRA(US21870U3041) Cash Dividend USD 0.460938 per Share - US Tax',
|
|
163
|
-
},
|
|
164
|
-
description: 'Dividend CORR PRA',
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
account: 'tax.statement.WITHHOLDING_TAX',
|
|
168
|
-
amount: 68,
|
|
169
|
-
data: {
|
|
170
|
-
currency: 'USD',
|
|
171
|
-
currencyValue: 76,
|
|
172
|
-
rates: {
|
|
173
|
-
USD: 0.8929,
|
|
174
|
-
},
|
|
175
|
-
text: 'CORR PRA(US21870U3041) Payment in Lieu of Dividend - US Tax',
|
|
176
|
-
},
|
|
177
|
-
description: 'Dividend CORR PRA',
|
|
178
|
-
},
|
|
179
|
-
]]
|
|
180
|
-
)
|
|
181
|
-
})
|