import type { ColorValue } from '@/core/color.ts'; import { AccountType, AccountCategory } from '@/core/account.ts'; import { PARENT_ACCOUNT_CURRENCY_PLACEHOLDER } from '@/consts/currency.ts'; import { DEFAULT_ACCOUNT_ICON_ID } from '@/consts/icon.ts'; import { DEFAULT_ACCOUNT_COLOR } from '@/consts/color.ts'; export class Account implements AccountInfoResponse { public id: string; public name: string; public parentId: string; public category: number; public type: number; public icon: string; public color: ColorValue; public currency: string; public balance: number; public balanceTime?: number; public comment: string; public creditCardStatementDate?: number; public displayOrder: number; public visible: boolean; public subAccounts?: Account[]; private readonly _isAsset?: boolean; private readonly _isLiability?: boolean; protected constructor(id: string, name: string, parentId: string, category: number, type: number, icon: string, color: string, currency: string, balance: number, comment: string, displayOrder: number, visible: boolean, balanceTime?: number, creditCardStatementDate?: number, isAsset?: boolean, isLiability?: boolean, subAccounts?: Account[]) { this.id = id; this.name = name; this.parentId = parentId; this.category = category; this.type = type; this.icon = icon; this.color = color; this.currency = currency; this.balance = balance; this.balanceTime = balanceTime; this.comment = comment; this.displayOrder = displayOrder; this.visible = visible; this.creditCardStatementDate = creditCardStatementDate; this._isAsset = isAsset; this._isLiability = isLiability; if (typeof(subAccounts) !== 'undefined') { this.subAccounts = subAccounts; } else { this.subAccounts = undefined; } } public get isAsset(): boolean { if (typeof(this._isAsset) !== 'undefined') { return this._isAsset; } const accountCategory = AccountCategory.valueOf(this.category); if (accountCategory) { return accountCategory.isAsset; } return false; } public get isLiability(): boolean { if (typeof(this._isLiability) !== 'undefined') { return this._isLiability; } const accountCategory = AccountCategory.valueOf(this.category); if (accountCategory) { return accountCategory.isLiability; } return false; } public get hidden(): boolean { return !this.visible; } public from(other: Account): void { this.id = other.id; this.category = other.category; this.type = other.type; this.name = other.name; this.icon = other.icon; this.color = other.color; this.currency = other.currency; this.balance = other.balance; this.balanceTime = other.balanceTime; this.comment = other.comment; this.creditCardStatementDate = other.creditCardStatementDate; this.visible = other.visible; } public toCreateRequest(clientSessionId: string, subAccounts?: Account[], parentAccount?: Account): AccountCreateRequest { let subAccountCreateRequests: AccountCreateRequest[] | undefined = undefined; if (this.type === AccountType.MultiSubAccounts.type) { subAccountCreateRequests = []; if (!subAccounts) { subAccounts = this.subAccounts; } if (subAccounts) { for (const subAccount of subAccounts) { subAccountCreateRequests.push(subAccount.toCreateRequest(clientSessionId, undefined, this)); } } } return { name: this.name, category: parentAccount ? parentAccount.category : this.category, type: parentAccount ? AccountType.SingleAccount.type : this.type, icon: this.icon, color: this.color, currency: parentAccount || this.type === AccountType.SingleAccount.type ? this.currency : PARENT_ACCOUNT_CURRENCY_PLACEHOLDER, balance: parentAccount || this.type === AccountType.SingleAccount.type ? this.balance : 0, balanceTime: (parentAccount || this.type === AccountType.SingleAccount.type) && this.balanceTime ? this.balanceTime : 0, comment: this.comment, creditCardStatementDate: !parentAccount && this.category === AccountCategory.CreditCard.type ? this.creditCardStatementDate : undefined, subAccounts: !parentAccount ? subAccountCreateRequests : undefined, clientSessionId: !parentAccount ? clientSessionId : undefined }; } public toModifyRequest(subAccounts?: Account[], parentAccount?: Account): AccountModifyRequest { let subAccountModifyRequests: AccountModifyRequest[] | undefined = undefined; if (this.type === AccountType.MultiSubAccounts.type) { subAccountModifyRequests = []; if (!subAccounts) { subAccounts = this.subAccounts; } if (subAccounts) { for (const subAccount of subAccounts) { subAccountModifyRequests.push(subAccount.toModifyRequest(undefined, this)); } } } return { id: this.id, name: this.name, category: parentAccount ? parentAccount.category : this.category, icon: this.icon, color: this.color, comment: this.comment, creditCardStatementDate: !parentAccount && this.category === AccountCategory.CreditCard.type ? this.creditCardStatementDate : undefined, hidden: !this.visible, subAccounts: !parentAccount ? subAccountModifyRequests : undefined, }; } public getAccountOrSubAccountId(subAccountId: string): string | null { if (this.type === AccountType.SingleAccount.type) { return this.id; } else if (this.type === AccountType.MultiSubAccounts.type && !subAccountId) { return this.id; } else if (this.type === AccountType.MultiSubAccounts.type && subAccountId) { if (!this.subAccounts || !this.subAccounts.length) { return null; } for (let i = 0; i < this.subAccounts.length; i++) { const subAccount = this.subAccounts[i]; if (subAccountId && subAccountId === subAccount.id) { return subAccount.id; } } return null; } else { return null; } } public getAccountOrSubAccountComment(subAccountId: string): string | null { if (this.type === AccountType.SingleAccount.type) { return this.comment; } else if (this.type === AccountType.MultiSubAccounts.type && !subAccountId) { return this.comment; } else if (this.type === AccountType.MultiSubAccounts.type && subAccountId) { if (!this.subAccounts || !this.subAccounts.length) { return null; } for (let i = 0; i < this.subAccounts.length; i++) { const subAccount = this.subAccounts[i]; if (subAccountId && subAccountId === subAccount.id) { return subAccount.comment; } } return null; } else { return null; } } public getSubAccountCurrencies(showHidden: boolean, subAccountId: string): string[] { if (!this.subAccounts || !this.subAccounts.length) { return []; } const subAccountCurrenciesMap: Record = {}; const subAccountCurrencies: string[] = []; for (let i = 0; i < this.subAccounts.length; i++) { const subAccount = this.subAccounts[i]; if (!showHidden && subAccount.hidden) { continue; } if (subAccountId && subAccountId === subAccount.id) { return [subAccount.currency]; } else { if (!subAccountCurrenciesMap[subAccount.currency]) { subAccountCurrenciesMap[subAccount.currency] = true; subAccountCurrencies.push(subAccount.currency); } } } return subAccountCurrencies; } public clone(): Account { return new Account( this.id, this.name, this.parentId, this.category, this.type, this.icon, this.color, this.currency, this.balance, this.comment, this.displayOrder, this.visible, this.balanceTime, this.creditCardStatementDate, this.isAsset, this.isLiability, typeof(this.subAccounts) !== 'undefined' ? Account.cloneAccounts(this.subAccounts) : undefined); } public createNewSubAccount(currency: string, balanceTime: number): Account { return new Account( '', // id '', // name '', // parentId 0, // category 0, // type this.icon, // icon this.color, // color currency, // currency 0, // balance '', // comment 0, // displayOrder true, // visible balanceTime, // balanceTime 0 // creditCardStatementDate ); } public static createNewAccount(currency: string, balanceTime: number): Account { return new Account( '', // id '', // name '', // parentId AccountCategory.Cash.type, // category AccountType.SingleAccount.type, // type DEFAULT_ACCOUNT_ICON_ID, // icon DEFAULT_ACCOUNT_COLOR, // color currency, // currency 0, // balance '', // comment 0, // displayOrder true, // visible balanceTime, // balanceTime 0 // creditCardStatementDate ); } public static of(accountResponse: AccountInfoResponse): Account { return new Account( accountResponse.id, accountResponse.name, accountResponse.parentId, accountResponse.category, accountResponse.type, accountResponse.icon, accountResponse.color, accountResponse.currency, accountResponse.balance, accountResponse.comment, accountResponse.displayOrder, !accountResponse.hidden, undefined, accountResponse.creditCardStatementDate, accountResponse.isAsset, accountResponse.isLiability, accountResponse.subAccounts ? Account.ofMany(accountResponse.subAccounts) : undefined ); } public static ofMany(accountResponses: AccountInfoResponse[]): Account[] { const accounts: Account[] = []; for (const accountResponse of accountResponses) { accounts.push(Account.of(accountResponse)); } return accounts; } public static findAccountNameById(accounts: Account[], accountId: string, defaultName?: string): string | undefined { for (const account of accounts) { if (account.id === accountId) { return account.name; } } return defaultName; } public static cloneAccounts(accounts: Account[]): Account[] { const clonedAccounts: Account[] = []; for (const account of accounts) { clonedAccounts.push(account.clone()); } return clonedAccounts; } public static sortAccounts(accounts: Account[]): Account[] { if (!accounts || !accounts.length) { return accounts; } return accounts.sort(function (account1, account2) { if (account1.category !== account2.category) { const account1Category = AccountCategory.valueOf(account1.category); const account2Category = AccountCategory.valueOf(account2.category); if (!account1Category) { return 1; } if (!account2Category) { return -1; } return account1Category.displayOrder - account2Category.displayOrder; } return account1.displayOrder - account2.displayOrder; }); } } export class AccountWithDisplayBalance extends Account { public displayBalance: string; private constructor(Account: Account, displayBalance: string) { super( Account.id, Account.name, Account.parentId, Account.category, Account.type, Account.icon, Account.color, Account.currency, Account.balance, Account.comment, Account.displayOrder, Account.visible, Account.balanceTime, Account.creditCardStatementDate, Account.isAsset, Account.isLiability, Account.subAccounts ); this.displayBalance = displayBalance; } public static fromAccount(account: Account, displayBalance: string): AccountWithDisplayBalance { return new AccountWithDisplayBalance(account, displayBalance); } } export interface AccountCreateRequest { readonly name: string; readonly category: number; readonly type: number; readonly icon: string; readonly color: string; readonly currency: string; readonly balance: number; readonly balanceTime: number; readonly comment: string; readonly creditCardStatementDate?: number; readonly subAccounts?: AccountCreateRequest[]; readonly clientSessionId?: string; } export interface AccountModifyRequest { readonly id: string; readonly name: string; readonly category: number; readonly icon: string; readonly color: string; readonly comment: string; readonly creditCardStatementDate?: number; readonly hidden: boolean; readonly subAccounts?: AccountModifyRequest[]; } export interface AccountInfoResponse { readonly id: string; readonly name: string; readonly parentId: string; readonly category: number; readonly type: number; readonly icon: string; readonly color: string; readonly currency: string; readonly balance: number; readonly comment: string; readonly creditCardStatementDate?: number; readonly displayOrder: number; readonly isAsset?: boolean; readonly isLiability?: boolean; readonly hidden: boolean; readonly subAccounts?: AccountInfoResponse[]; } export interface AccountHideRequest { readonly id: string; readonly hidden: boolean; } export interface AccountMoveRequest { readonly newDisplayOrders: AccountNewDisplayOrderRequest[]; } export interface AccountNewDisplayOrderRequest { readonly id: string; readonly displayOrder: number; } export interface AccountDeleteRequest { readonly id: string; } export interface AccountBalance { readonly balance: number; readonly isAsset: boolean; readonly isLiability: boolean; readonly currency: string; } export interface AccountDisplayBalance { readonly balance: string; readonly currency: string; } export interface CategorizedAccount { readonly category: number; readonly name: string; readonly icon: string; readonly accounts: Account[]; } export class CategorizedAccountWithDisplayBalance { public category: number; public name: string; public icon: string; public accounts: AccountWithDisplayBalance[]; public displayBalance: string; private constructor(category: number, name: string, icon: string, accounts: AccountWithDisplayBalance[], displayBalance: string) { this.category = category; this.name = name; this.icon = icon; this.accounts = accounts; this.displayBalance = displayBalance; } public static of(categorizedAccount: CategorizedAccount, accounts: AccountWithDisplayBalance[], displayBalance: string): CategorizedAccountWithDisplayBalance { return new CategorizedAccountWithDisplayBalance(categorizedAccount.category, categorizedAccount.name, categorizedAccount.icon, accounts, displayBalance); } } export interface AccountCategoriesWithVisibleCount { readonly category: number; readonly name: string; readonly icon: string; readonly allAccounts: Account[]; readonly allVisibleAccountCount: number; readonly firstVisibleAccountIndex: number; readonly allSubAccounts: Record; readonly allVisibleSubAccountCounts: Record; readonly allFirstVisibleSubAccountIndexes: Record; } export interface AccountShowingIds { readonly accounts: Record; readonly subAccounts: Record; }