import * as plugins from './bunq.plugins.js'; import { BunqAccount } from './bunq.classes.account.js'; import { BunqMonetaryAccount } from './bunq.classes.monetaryaccount.js'; export type TExportFormat = 'CSV' | 'PDF' | 'MT940'; export class BunqExport { private bunqAccount: BunqAccount; private monetaryAccount: BunqMonetaryAccount; public id?: number; public created?: string; public updated?: string; public status?: string; constructor(bunqAccount: BunqAccount, monetaryAccount: BunqMonetaryAccount) { this.bunqAccount = bunqAccount; this.monetaryAccount = monetaryAccount; } /** * Create a new export */ public async create(options: { statementFormat: TExportFormat; dateStart: string; dateEnd: string; regionalFormat?: 'EUROPEAN' | 'UK_US'; includeAttachment?: boolean; }): Promise { await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().post( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/customer-statement`, { statement_format: options.statementFormat, date_start: options.dateStart, date_end: options.dateEnd, regional_format: options.regionalFormat || 'EUROPEAN', include_attachment: options.includeAttachment || false } ); if (response.Response && response.Response[0] && response.Response[0].Id) { this.id = response.Response[0].Id.id; return this.id; } throw new Error('Failed to create export'); } /** * Get export details */ public async get(): Promise { if (!this.id) { throw new Error('Export ID not set'); } await this.bunqAccount.apiContext.ensureValidSession(); const response = await this.bunqAccount.getHttpClient().get( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/customer-statement/${this.id}` ); if (response.Response && response.Response[0]) { const data = response.Response[0].CustomerStatement; this.status = data.status; return data; } throw new Error('Export not found'); } /** * Delete export */ public async delete(): Promise { if (!this.id) { throw new Error('Export ID not set'); } await this.bunqAccount.apiContext.ensureValidSession(); await this.bunqAccount.getHttpClient().delete( `/v1/user/${this.bunqAccount.userId}/monetary-account/${this.monetaryAccount.id}/customer-statement/${this.id}` ); } /** * List exports */ public static async list( bunqAccount: BunqAccount, monetaryAccountId: number ): Promise { await bunqAccount.apiContext.ensureValidSession(); const response = await bunqAccount.getHttpClient().list( `/v1/user/${bunqAccount.userId}/monetary-account/${monetaryAccountId}/customer-statement` ); return response.Response || []; } /** * Download the export content */ public async downloadContent(): Promise { if (!this.id) { throw new Error('Export ID not set'); } // First get the export details to find the attachment const exportDetails = await this.get(); if (!exportDetails.attachment || exportDetails.attachment.length === 0) { throw new Error('Export has no attachment'); } const attachmentUuid = exportDetails.attachment[0].attachment_public_uuid; // Download the attachment content const response = await plugins.smartrequest.request( `${this.bunqAccount.apiContext.getBaseUrl()}/v1/attachment-public/${attachmentUuid}/content`, { method: 'GET', headers: { 'X-Bunq-Client-Authentication': this.bunqAccount.apiContext.getSession().getContext().sessionToken } } ); return Buffer.from(response.body); } /** * Save export to file */ public async saveToFile(filePath: string): Promise { const content = await this.downloadContent(); await plugins.smartfile.memory.toFs(content, filePath); } /** * Wait for export to complete */ public async waitForCompletion(maxWaitMs: number = 60000): Promise { const startTime = Date.now(); while (true) { const details = await this.get(); if (details.status === 'COMPLETE') { return; } if (details.status === 'FAILED') { throw new Error('Export failed'); } if (Date.now() - startTime > maxWaitMs) { throw new Error('Export timed out'); } // Wait 2 seconds before checking again await new Promise(resolve => setTimeout(resolve, 2000)); } } /** * Create and download export in one go */ public static async createAndDownload( bunqAccount: BunqAccount, monetaryAccount: BunqMonetaryAccount, options: { statementFormat: TExportFormat; dateStart: string; dateEnd: string; regionalFormat?: 'EUROPEAN' | 'UK_US'; includeAttachment?: boolean; outputPath: string; } ): Promise { const bunqExport = new BunqExport(bunqAccount, monetaryAccount); // Create export await bunqExport.create({ statementFormat: options.statementFormat, dateStart: options.dateStart, dateEnd: options.dateEnd, regionalFormat: options.regionalFormat, includeAttachment: options.includeAttachment }); // Wait for completion await bunqExport.waitForCompletion(); // Save to file await bunqExport.saveToFile(options.outputPath); } } /** * Export builder for easier export creation */ export class ExportBuilder { private bunqAccount: BunqAccount; private monetaryAccount: BunqMonetaryAccount; private options: any = {}; constructor(bunqAccount: BunqAccount, monetaryAccount: BunqMonetaryAccount) { this.bunqAccount = bunqAccount; this.monetaryAccount = monetaryAccount; } /** * Set format to CSV */ public asCsv(): this { this.options.statementFormat = 'CSV'; return this; } /** * Set format to PDF */ public asPdf(): this { this.options.statementFormat = 'PDF'; return this; } /** * Set format to MT940 */ public asMt940(): this { this.options.statementFormat = 'MT940'; return this; } /** * Set date range */ public dateRange(startDate: string, endDate: string): this { this.options.dateStart = startDate; this.options.dateEnd = endDate; return this; } /** * Set last N days */ public lastDays(days: number): this { const endDate = new Date(); const startDate = new Date(); startDate.setDate(startDate.getDate() - days); this.options.dateStart = startDate.toISOString().split('T')[0]; this.options.dateEnd = endDate.toISOString().split('T')[0]; return this; } /** * Set last month */ public lastMonth(): this { const now = new Date(); const startDate = new Date(now.getFullYear(), now.getMonth() - 1, 1); const endDate = new Date(now.getFullYear(), now.getMonth(), 0); this.options.dateStart = startDate.toISOString().split('T')[0]; this.options.dateEnd = endDate.toISOString().split('T')[0]; return this; } /** * Set regional format */ public regionalFormat(format: 'EUROPEAN' | 'UK_US'): this { this.options.regionalFormat = format; return this; } /** * Include attachments */ public includeAttachments(include: boolean = true): this { this.options.includeAttachment = include; return this; } /** * Create the export */ public async create(): Promise { if (!this.options.statementFormat) { throw new Error('Export format is required'); } if (!this.options.dateStart || !this.options.dateEnd) { throw new Error('Date range is required'); } const bunqExport = new BunqExport(this.bunqAccount, this.monetaryAccount); await bunqExport.create(this.options); return bunqExport; } /** * Create and download to file */ public async downloadTo(filePath: string): Promise { const bunqExport = await this.create(); await bunqExport.waitForCompletion(); await bunqExport.saveToFile(filePath); } }