import * as plugins from './bunq.plugins.js'; import { BunqAccount } from './bunq.classes.account.js'; import { BunqTransaction } from './bunq.classes.transaction.js'; import { BunqPayment } from './bunq.classes.payment.js'; import { ExportBuilder } from './bunq.classes.export.js'; import type { IBunqPaginationOptions, IBunqMonetaryAccountBank } from './bunq.interfaces.js'; export type TAccountType = 'bank' | 'joint' | 'savings' | 'external' | 'light' | 'card' | 'external_savings' | 'savings_external'; /** * a monetary account */ export class BunqMonetaryAccount { public static fromAPIObject(bunqAccountRef: BunqAccount, apiObject: any) { const newMonetaryAccount = new this(bunqAccountRef); let type: TAccountType; let accessor: string; switch (true) { case !!apiObject.MonetaryAccountBank: type = 'bank'; accessor = 'MonetaryAccountBank'; break; case !!apiObject.MonetaryAccountJoint: type = 'joint'; accessor = 'MonetaryAccountJoint'; break; case !!apiObject.MonetaryAccountSavings: type = 'savings'; accessor = 'MonetaryAccountSavings'; break; case !!apiObject.MonetaryAccountExternal: type = 'external'; accessor = 'MonetaryAccountExternal'; break; case !!apiObject.MonetaryAccountLight: type = 'light'; accessor = 'MonetaryAccountLight'; break; case !!apiObject.MonetaryAccountCard: type = 'card'; accessor = 'MonetaryAccountCard'; break; case !!apiObject.MonetaryAccountExternalSavings: type = 'external_savings'; accessor = 'MonetaryAccountExternalSavings'; break; case !!apiObject.MonetaryAccountSavingsExternal: type = 'savings_external'; accessor = 'MonetaryAccountSavingsExternal'; break; default: console.log('Unknown account type:', apiObject); throw new Error('Unknown account type'); } Object.assign(newMonetaryAccount, apiObject[accessor], { type }); return newMonetaryAccount; } // computed public type: TAccountType; // from API public id: number; public created: string; public updated: string; public alias: any[]; public avatar: { uuid: string; image: any[]; anchor_uuid: string; }; public balance: { currency: string; value: string; }; public country: string; public currency: string; public daily_limit: { currency: string; value: string; }; public daily_spent: { currency: string; value: string; }; public description: string; public public_uuid: string; public status: string; public sub_status: string; public timezone: string; public user_id: number; public monetary_account_profile: null; public notification_filters: any[]; public setting: any[]; public connected_cards: any[]; public overdraft_limit: { currency: string; value: string; }; public reason: string; public reason_description: string; public auto_save_id: null; public all_auto_save_id: any[]; public bunqAccountRef: BunqAccount; constructor(bunqAccountRefArg: BunqAccount) { this.bunqAccountRef = bunqAccountRefArg; } /** * gets all transactions on this account * @param options - Pagination options or a number for backward compatibility (treated as newer_id) */ public async getTransactions(options?: IBunqPaginationOptions | number | false): Promise { let paginationOptions: IBunqPaginationOptions = {}; // Backward compatibility: if a number or false is passed, treat it as newer_id if (typeof options === 'number' || options === false) { paginationOptions.newer_id = options; } else if (options) { paginationOptions = { ...options }; } // Set default count if not specified if (!paginationOptions.count) { paginationOptions.count = 200; } // Build clean pagination object - only include properties that are not false/undefined const cleanPaginationOptions: IBunqPaginationOptions = { count: paginationOptions.count, }; if (paginationOptions.newer_id !== undefined && paginationOptions.newer_id !== false) { cleanPaginationOptions.newer_id = paginationOptions.newer_id; } if (paginationOptions.older_id !== undefined && paginationOptions.older_id !== false) { cleanPaginationOptions.older_id = paginationOptions.older_id; } await this.bunqAccountRef.apiContext.ensureValidSession(); const response = await this.bunqAccountRef.getHttpClient().list( `/v1/user/${this.bunqAccountRef.userId}/monetary-account/${this.id}/payment`, cleanPaginationOptions ); const transactionsArray: BunqTransaction[] = []; if (response.Response) { for (const apiTransaction of response.Response) { transactionsArray.push(BunqTransaction.fromApiObject(this, apiTransaction)); } } return transactionsArray; } /** * Create a payment from this account */ public async createPayment(payment: BunqPayment): Promise { return payment.create(); } /** * Update account settings */ public async update(updates: any): Promise { // Check if this is a dangerous operation if (updates.status === 'CANCELLED' && !this.bunqAccountRef.options.dangerousOperations) { throw new Error('Dangerous operations are not enabled. Initialize the BunqAccount with dangerousOperations: true to allow cancelling accounts.'); } await this.bunqAccountRef.apiContext.ensureValidSession(); const endpoint = `/v1/user/${this.bunqAccountRef.userId}/monetary-account/${this.id}`; // Determine the correct update key based on account type let updateKey: string; switch (this.type) { case 'bank': updateKey = 'MonetaryAccountBank'; break; case 'joint': updateKey = 'MonetaryAccountJoint'; break; case 'savings': updateKey = 'MonetaryAccountSavings'; break; case 'external': updateKey = 'MonetaryAccountExternal'; break; case 'light': updateKey = 'MonetaryAccountLight'; break; case 'card': updateKey = 'MonetaryAccountCard'; break; case 'external_savings': updateKey = 'MonetaryAccountExternalSavings'; break; case 'savings_external': updateKey = 'MonetaryAccountSavingsExternal'; break; default: throw new Error(`Unknown account type: ${this.type}`); } await this.bunqAccountRef.getHttpClient().put(endpoint, { [updateKey]: updates }); } /** * Get account details */ public async refresh(): Promise { await this.bunqAccountRef.apiContext.ensureValidSession(); const response = await this.bunqAccountRef.getHttpClient().get( `/v1/user/${this.bunqAccountRef.userId}/monetary-account/${this.id}` ); if (response.Response && response.Response[0]) { const refreshedAccount = BunqMonetaryAccount.fromAPIObject( this.bunqAccountRef, response.Response[0] ); // Update this instance with refreshed data Object.assign(this, refreshedAccount); } } /** * Close this monetary account */ public async close(reason: string): Promise { if (!this.bunqAccountRef.options.dangerousOperations) { throw new Error('Dangerous operations are not enabled. Initialize the BunqAccount with dangerousOperations: true to allow closing accounts.'); } await this.update({ status: 'CANCELLED', sub_status: 'REDEMPTION_VOLUNTARY', reason: 'OTHER', reason_description: reason }); } /** * Get account statement with flexible date options * @param optionsArg - Options for statement generation * @returns ExportBuilder instance for creating the statement */ public getAccountStatement(optionsArg: { from?: Date; to?: Date; monthlyIndexedFrom0?: number; monthlyIndexedFrom1?: number; includeTransactionAttachments: boolean; }): ExportBuilder { const exportBuilder = new ExportBuilder(this.bunqAccountRef, this); // Determine date range based on provided options let startDate: Date; let endDate: Date; if (optionsArg.from && optionsArg.to) { // Use provided date range startDate = optionsArg.from; endDate = optionsArg.to; } else if (optionsArg.monthlyIndexedFrom0 !== undefined) { // Calculate date range for 0-indexed month const now = new Date(); const targetDate = new Date(now.getFullYear(), now.getMonth() - optionsArg.monthlyIndexedFrom0, 1); startDate = new Date(targetDate.getFullYear(), targetDate.getMonth(), 1); endDate = new Date(targetDate.getFullYear(), targetDate.getMonth() + 1, 0); } else if (optionsArg.monthlyIndexedFrom1 !== undefined) { // Calculate date range for 1-indexed month (1 = last month, 2 = two months ago, etc.) const now = new Date(); const targetDate = new Date(now.getFullYear(), now.getMonth() - optionsArg.monthlyIndexedFrom1, 1); startDate = new Date(targetDate.getFullYear(), targetDate.getMonth(), 1); endDate = new Date(targetDate.getFullYear(), targetDate.getMonth() + 1, 0); } else { // Default to last month if no date options provided const now = new Date(); startDate = new Date(now.getFullYear(), now.getMonth() - 1, 1); endDate = new Date(now.getFullYear(), now.getMonth(), 0); } // Format dates as DD-MM-YYYY (bunq API format) const formatDate = (date: Date): string => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${day}-${month}-${year}`; }; // Configure the export builder exportBuilder.dateRange(formatDate(startDate), formatDate(endDate)); exportBuilder.includeAttachments(optionsArg.includeTransactionAttachments); return exportBuilder; } }