228 lines
5.9 KiB
TypeScript
228 lines
5.9 KiB
TypeScript
import { type RouteLocation } from 'vue-router'
|
|
import { ApiClient } from './base'
|
|
import type { Currency } from './data'
|
|
import type { Page, PageRequest } from './pagination'
|
|
import { getSelectedProfile } from './profile'
|
|
import type { Attachment } from './attachment'
|
|
|
|
export interface AccountType {
|
|
id: string
|
|
name: string
|
|
debitsPositive: boolean
|
|
emoji: string
|
|
}
|
|
|
|
export abstract class AccountTypes {
|
|
public static readonly CHECKING: AccountType = {
|
|
id: 'CHECKING',
|
|
name: 'Checking',
|
|
debitsPositive: true,
|
|
emoji: '💵',
|
|
}
|
|
public static readonly SAVINGS: AccountType = {
|
|
id: 'SAVINGS',
|
|
name: 'Savings',
|
|
debitsPositive: true,
|
|
emoji: '💰',
|
|
}
|
|
public static readonly CREDIT_CARD: AccountType = {
|
|
id: 'CREDIT_CARD',
|
|
name: 'Credit Card',
|
|
debitsPositive: false,
|
|
emoji: '💳',
|
|
}
|
|
public static readonly BROKERAGE: AccountType = {
|
|
id: 'BROKERAGE',
|
|
name: 'Brokerage',
|
|
debitsPositive: true,
|
|
emoji: '📈',
|
|
}
|
|
|
|
public static of(id: string): AccountType {
|
|
if (id === 'CHECKING') return AccountTypes.CHECKING
|
|
if (id === 'SAVINGS') return AccountTypes.SAVINGS
|
|
if (id === 'CREDIT_CARD') return AccountTypes.CREDIT_CARD
|
|
if (id === 'BROKERAGE') return AccountTypes.BROKERAGE
|
|
throw new Error('Unknown account type: ' + id)
|
|
}
|
|
}
|
|
|
|
export interface Account {
|
|
id: number
|
|
createdAt: string
|
|
archived: boolean
|
|
type: string
|
|
numberSuffix: string
|
|
name: string
|
|
currency: Currency
|
|
description: string
|
|
currentBalance: number | null
|
|
}
|
|
|
|
export interface SimpleAccountResponse {
|
|
id: number
|
|
name: string
|
|
type: string
|
|
numberSuffix: string
|
|
}
|
|
|
|
export interface AccountCreationPayload {
|
|
type: string
|
|
numberSuffix: string
|
|
name: string
|
|
currency: string
|
|
description: string
|
|
}
|
|
|
|
export enum AccountJournalEntryType {
|
|
CREDIT = 'CREDIT',
|
|
DEBIT = 'DEBIT',
|
|
}
|
|
|
|
export interface AccountJournalEntry {
|
|
id: number
|
|
timestamp: string
|
|
accountId: number
|
|
transactionId: number
|
|
amount: number
|
|
type: AccountJournalEntryType
|
|
currency: Currency
|
|
}
|
|
|
|
export enum AccountValueRecordType {
|
|
BALANCE = 'BALANCE',
|
|
}
|
|
|
|
export interface AccountValueRecord {
|
|
id: number
|
|
timestamp: string
|
|
accountId: number
|
|
type: AccountValueRecordType
|
|
value: number
|
|
currency: Currency
|
|
attachments: Attachment[]
|
|
}
|
|
|
|
export interface AccountValueRecordCreationPayload {
|
|
timestamp: string
|
|
type: AccountValueRecordType
|
|
value: number
|
|
}
|
|
|
|
// History:
|
|
export enum AccountHistoryItemType {
|
|
TEXT = 'TEXT',
|
|
PROPERTY_CHANGE = 'PROPERTY_CHANGE',
|
|
VALUE_RECORD = 'VALUE_RECORD',
|
|
JOURNAL_ENTRY = 'JOURNAL_ENTRY',
|
|
}
|
|
|
|
export function accountHistoryItemTypeToDisplayName(type: AccountHistoryItemType) {
|
|
if (type === AccountHistoryItemType.TEXT) return 'Text'
|
|
if (type === AccountHistoryItemType.PROPERTY_CHANGE) return 'Property Change'
|
|
if (type === AccountHistoryItemType.VALUE_RECORD) return 'Value Record'
|
|
if (type === AccountHistoryItemType.JOURNAL_ENTRY) return 'Journal Entry'
|
|
return 'Unknown'
|
|
}
|
|
|
|
export interface AccountHistoryItem {
|
|
timestamp: string
|
|
type: AccountHistoryItemType
|
|
}
|
|
|
|
export interface AccountHistoryValueRecordItem extends AccountHistoryItem {
|
|
valueRecordId: number
|
|
valueRecordType: AccountValueRecordType
|
|
value: number
|
|
currency: Currency
|
|
}
|
|
|
|
export interface AccountHistoryJournalEntryItem extends AccountHistoryItem {
|
|
journalEntryType: AccountJournalEntryType
|
|
amount: number
|
|
currency: Currency
|
|
transactionId: number
|
|
transactionDescription: string
|
|
}
|
|
|
|
export interface CurrencyBalance {
|
|
currency: Currency
|
|
balance: number
|
|
}
|
|
|
|
export class AccountApiClient extends ApiClient {
|
|
readonly path: string
|
|
readonly profileName: string
|
|
|
|
constructor(route: RouteLocation) {
|
|
super()
|
|
this.profileName = getSelectedProfile(route)
|
|
this.path = `/profiles/${getSelectedProfile(route)}/accounts`
|
|
}
|
|
|
|
getAccounts(): Promise<Account[]> {
|
|
return super.getJson(this.path)
|
|
}
|
|
|
|
getAccount(id: number): Promise<Account> {
|
|
return super.getJson(this.path + '/' + id)
|
|
}
|
|
|
|
createAccount(data: AccountCreationPayload): Promise<Account> {
|
|
return super.postJson(this.path, data)
|
|
}
|
|
|
|
updateAccount(id: number, data: AccountCreationPayload): Promise<Account> {
|
|
return super.putJson(this.path + '/' + id, data)
|
|
}
|
|
|
|
deleteAccount(id: number): Promise<void> {
|
|
return super.delete(this.path + '/' + id)
|
|
}
|
|
|
|
async getBalance(id: number, timestamp: Date): Promise<number | undefined> {
|
|
const result: { balance: number | null } = await super.getJson(
|
|
`${this.path}/${id}/balance?timestamp=${timestamp.toISOString()}`,
|
|
)
|
|
if (result.balance === null) return undefined
|
|
return result.balance
|
|
}
|
|
|
|
getHistory(id: number, pageRequest: PageRequest): Promise<Page<AccountHistoryItem>> {
|
|
return super.getJsonPage(`${this.path}/${id}/history`, pageRequest)
|
|
}
|
|
|
|
getTotalBalances(): Promise<CurrencyBalance[]> {
|
|
return super.getJson(`/profiles/${this.profileName}/account-balances`)
|
|
}
|
|
|
|
getValueRecords(accountId: number, pageRequest: PageRequest): Promise<Page<AccountValueRecord>> {
|
|
return super.getJsonPage(this.path + '/' + accountId + '/value-records', pageRequest)
|
|
}
|
|
|
|
getValueRecord(accountId: number, valueRecordId: number): Promise<AccountValueRecord> {
|
|
return super.getJson(this.path + '/' + accountId + '/value-records/' + valueRecordId)
|
|
}
|
|
|
|
createValueRecord(
|
|
accountId: number,
|
|
payload: AccountValueRecordCreationPayload,
|
|
attachments: File[],
|
|
): Promise<AccountValueRecord> {
|
|
const formData = new FormData()
|
|
formData.append('payload', JSON.stringify(payload))
|
|
for (const file of attachments) {
|
|
formData.append('file', file)
|
|
}
|
|
return super.postFormData(`${this.path}/${accountId}/value-records`, formData)
|
|
}
|
|
|
|
deleteValueRecord(accountId: number, valueRecordId: number): Promise<void> {
|
|
return super.delete(this.path + '/' + accountId + '/value-records/' + valueRecordId)
|
|
}
|
|
|
|
downloadAccountsExport(): Promise<void> {
|
|
return super.getFile(this.path + '/export')
|
|
}
|
|
}
|