From 17c0b6f52d2f82e7974ec92dec22af9ce68bf690 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Tue, 9 Sep 2025 11:38:20 -0400 Subject: [PATCH 1/6] new component --- .../actions/create-expense/create-expense.ts | 2 +- .../export-report-to-pdf.ts | 2 +- .../actions/export-report/export-report.ts | 238 ++++++++++++++++++ components/expensify/app/expensify.app.ts | 30 ++- 4 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 components/expensify/actions/export-report/export-report.ts diff --git a/components/expensify/actions/create-expense/create-expense.ts b/components/expensify/actions/create-expense/create-expense.ts index bec7e094b7ff1..0b7de2ae764a4 100644 --- a/components/expensify/actions/create-expense/create-expense.ts +++ b/components/expensify/actions/create-expense/create-expense.ts @@ -3,7 +3,7 @@ import expensify from "../../app/expensify.app"; export default defineAction({ key: "expensify-create-expense", - version: "0.0.2", + version: "0.0.3", name: "Create Expense", description: "Creates a new expense. [See docs here](https://integrations.expensify.com/Integration-Server/doc/#expense-creator)", type: "action", diff --git a/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts b/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts index 74359ba4b7cde..a218a4839cb30 100644 --- a/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts +++ b/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts @@ -4,7 +4,7 @@ import fs from "fs"; export default defineAction({ key: "expensify-export-report-to-pdf", - version: "0.0.2", + version: "0.0.3", name: "Export Report To PDF", description: "Export a report to PDF. [See docs here](https://integrations.expensify.com/Integration-Server/doc/#report-exporter)", type: "action", diff --git a/components/expensify/actions/export-report/export-report.ts b/components/expensify/actions/export-report/export-report.ts new file mode 100644 index 0000000000000..9e54816d588bf --- /dev/null +++ b/components/expensify/actions/export-report/export-report.ts @@ -0,0 +1,238 @@ +import { defineAction } from "@pipedream/types"; +import expensify from "../../app/expensify.app"; +import fs from "fs"; +import { + axios, ConfigurationError, +} from "@pipedream/platform"; +import qs from "qs"; + +export default defineAction({ + key: "expensify-export-report", + name: "Export Report", + description: "Export a report to a PDF file. [See the documentation](https://integrations.expensify.com/Integration-Server/doc/#report-exporter)", + version: "0.0.1", + type: "action", + props: { + expensify, + reportIds: { + type: "string[]", + label: "Report IDs", + description: "The IDs of the reports to be exported. Required if `startDate` or a`pprovedAfter` are not specified.", + optional: true, + }, + startDate: { + type: "string", + label: "Start Date", + description: "The start date of the report. Format: YYYY-MM-DD. Required if `reportIds` or `approvedAfter ` are not specified.", + optional: true, + }, + endDate: { + type: "string", + label: "End Date", + description: "The end date of the report. Format: YYYY-MM-DD. Conditionally required, if either `startDate` or `approvedAfter` is more than one year ago.", + optional: true, + }, + approvedAfter: { + type: "string", + label: "Approved After", + description: "Filters out all reports approved before the given date, whichever occurred last (inclusive). Required if `reportIds` or `startDate` are not specified", + optional: true, + }, + fileExtension: { + type: "string", + label: "File Extension", + description: "Specifies the format of the generated report", + options: [ + "csv", + "xls", + "xlsx", + "txt", + "pdf", + "json", + "xml", + ], + }, + markAsExportedFilter: { + type: "boolean", + label: "Mark as Exported (Filter)", + description: "Filters out reports that have already been exported with that label out", + optional: true, + }, + reportStates: { + type: "string[]", + label: "Report States", + description: "Only the reports matching the specified status(es) will be exported", + options: [ + "OPEN", + "SUBMITTED", + "APPROVED", + "REIMBURSED", + "ARCHIVED", + ], + optional: true, + }, + employeeEmail: { + type: "string", + label: "Employee Email", + description: "The reports will be exported from that the specified employee email", + optional: true, + }, + policyIds: { + propDefinition: [ + expensify, + "policyIds", + ], + }, + fileBaseName: { + type: "string", + label: "File Base Name", + description: "The base name of the file to be exported", + optional: true, + }, + includeFullPageReceiptsPdf: { + type: "boolean", + label: "Include Full Page Receipts PDF", + description: "Specifies whether generated PDFs should include full page receipts. This parameter is used only if fileExtension contains pdf.", + optional: true, + }, + emailRecipients: { + type: "string[]", + label: "Email Recipients", + description: "People to email at the end of the export", + optional: true, + }, + markAsExported: { + type: "string", + label: "Mark as Exported", + description: "Mark the reports as exported with the given label", + optional: true, + }, + templatePath: { + type: "string", + label: "Template Path", + description: "The path in the /tmp directory to the template to use for the export", + optional: true, + }, + limit: { + type: "string", + label: "Limit", + description: "Maximum number of reports to export", + optional: true, + }, + test: { + type: "boolean", + label: "Test Mode", + description: "If set to true, actions defined in `onFinish` (i.e. email, markAsExported) will not be executed", + optional: true, + }, + syncDir: { + type: "dir", + accessMode: "write", + sync: true, + }, + }, + async run({ $ }) { + if (!this.reportIds && !this.startDate && !this.approvedAfter) { + throw new ConfigurationError("At least one of `reportIds`, `startDate`, or `approvedAfter` must be specified"); + } + + const onFinish = []; + if (this.emailRecipients) { + onFinish.push({ + actionName: "email", + recipients: this.emailRecipients.join(","), + }); + } + + if (this.markAsExported) { + onFinish.push({ + actionName: "markAsExported", + label: this.markAsExported, + }); + } + + const data = { + type: "file", + credentials: { + partnerUserID: this.expensify._partnerUserId(), + partnerUserSecret: this.expensify._partnerUserSecret(), + }, + onReceive: { + immediateResponse: [ + "returnRandomFileName", + ], + }, + inputSettings: { + type: "combinedReportData", + filters: { + reportIDList: this.reportIds + ? this.reportIds.join(",") + : undefined, + startDate: this.startDate, + endDate: this.endDate, + approvedAfter: this.approvedAfter, + markAsExported: this.markAsExportedFilter, + policyIDList: this.policyIds + ? this.policyIds.join(",") + : undefined, + }, + reportStates: this.reportStates + ? this.reportStates.join(",") + : undefined, + employeeEmail: this.employeeEmail, + limit: this.limit, + }, + outputSettings: { + fileExtension: this.fileExtension, + fileBaseName: this.fileBaseName, + includeFullPageReceiptsPdf: this.includeFullPageReceiptsPdf, + }, + onFinish, + test: this.test, + }; + + let fileName; + const args = { + method: "post", + url: `${this.expensify._apiUrl()}`, + }; + + if (!this.templatePath) { + fileName = await axios($, { + ...args, + data: qs.stringify({ + requestJobDescription: JSON.stringify(data), + template: "default", + }), + }); + } else { + fileName = await axios($, { + ...args, + data: qs.stringify({ + requestJobDescription: JSON.stringify(data), + template: fs.readFileSync(this.templatePath.includes("tmp/") + ? this.templatePath + : `/tmp/${this.templatePath}`, "utf8"), + }), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); + } + + const fileBuffer = await this.expensify.downloadFile({ + $, + fileName, + }); + + const path = `/tmp/${fileName}`; + + await fs.writeFileSync(path, fileBuffer); + + if (fileBuffer) { + $.export("$summary", `Successfully exported report in ${path}`); + } + + return path; + }, +}); diff --git a/components/expensify/app/expensify.app.ts b/components/expensify/app/expensify.app.ts index a59e3818d2eab..a520fa649064f 100644 --- a/components/expensify/app/expensify.app.ts +++ b/components/expensify/app/expensify.app.ts @@ -5,7 +5,23 @@ import qs from "qs"; export default defineApp({ type: "app", app: "expensify", - propDefinitions: {}, + propDefinitions: { + policyIds: { + type: "string[]", + label: "Policy IDs", + description: "The IDs of the policies to export", + optional: true, + async options() { + const { policyList } = await this.listPolicies(); + return policyList?.map(({ + id, name, + }) => ({ + label: name, + value: id, + })) || []; + }, + }, + }, methods: { _partnerUserId() { return this.$auth.partnerUserId; @@ -87,7 +103,17 @@ export default defineApp({ }, }, $); }, - + async listPolicies({ $ = this } = {}) { + return this._makeRequest({ + method: "post", + data: { + type: "get", + inputSettings: { + type: "policyList", + }, + }, + }, $); + }, async downloadFile({ $, fileName, }) { From f0e9a8b22182da42aef9c6165019cd39f383eaf0 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Tue, 9 Sep 2025 11:50:08 -0400 Subject: [PATCH 2/6] versions --- components/expensify/actions/create-expense/create-expense.ts | 2 +- components/expensify/actions/create-report/create-report.ts | 2 +- .../actions/export-report-to-pdf/export-report-to-pdf.ts | 2 +- components/expensify/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/expensify/actions/create-expense/create-expense.ts b/components/expensify/actions/create-expense/create-expense.ts index d3f8cdf1f0ee4..4855fdc99c863 100644 --- a/components/expensify/actions/create-expense/create-expense.ts +++ b/components/expensify/actions/create-expense/create-expense.ts @@ -3,7 +3,7 @@ import expensify from "../../app/expensify.app"; export default defineAction({ key: "expensify-create-expense", - version: "0.0.3", + version: "0.0.4", name: "Create Expense", description: "Creates a new expense. [See docs here](https://integrations.expensify.com/Integration-Server/doc/#expense-creator)", type: "action", diff --git a/components/expensify/actions/create-report/create-report.ts b/components/expensify/actions/create-report/create-report.ts index 8408b1816b0af..df42bfbb7a167 100644 --- a/components/expensify/actions/create-report/create-report.ts +++ b/components/expensify/actions/create-report/create-report.ts @@ -4,7 +4,7 @@ import utils from "../../common/utils"; export default defineAction({ key: "expensify-create-report", - version: "0.0.1", + version: "0.0.2", name: "Create Report", description: "Creates a new report with transactions in a user's account. [See docs here](https://integrations.expensify.com/Integration-Server/doc/#report-creator)", type: "action", diff --git a/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts b/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts index a218a4839cb30..b854857cc5686 100644 --- a/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts +++ b/components/expensify/actions/export-report-to-pdf/export-report-to-pdf.ts @@ -4,7 +4,7 @@ import fs from "fs"; export default defineAction({ key: "expensify-export-report-to-pdf", - version: "0.0.3", + version: "0.0.4", name: "Export Report To PDF", description: "Export a report to PDF. [See docs here](https://integrations.expensify.com/Integration-Server/doc/#report-exporter)", type: "action", diff --git a/components/expensify/package.json b/components/expensify/package.json index 11fce76551631..bea532e7eb6eb 100644 --- a/components/expensify/package.json +++ b/components/expensify/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/expensify", - "version": "0.1.0", + "version": "0.2.0", "description": "Pipedream Expensify Components", "main": "dist/app/expensify.app.mjs", "keywords": [ From 2d0c40413a5862525f2d9a7f3c38cf9c89f3cad7 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Tue, 9 Sep 2025 12:05:48 -0400 Subject: [PATCH 3/6] updates --- .../expensify/actions/export-report/export-report.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/expensify/actions/export-report/export-report.ts b/components/expensify/actions/export-report/export-report.ts index 7be316e32e76a..09cfa4304eb18 100644 --- a/components/expensify/actions/export-report/export-report.ts +++ b/components/expensify/actions/export-report/export-report.ts @@ -52,9 +52,9 @@ export default defineAction({ "xml", ], }, - markAsExportedFilter: { + markedAsExported: { type: "boolean", - label: "Mark as Exported (Filter)", + label: "Marked as Exported", description: "Filters out reports that have already been exported with that label out", optional: true, }, @@ -74,7 +74,7 @@ export default defineAction({ employeeEmail: { type: "string", label: "Employee Email", - description: "The reports will be exported from that the specified employee email", + description: "Export reports for the specified employee email", optional: true, }, policyIds: { @@ -171,12 +171,12 @@ export default defineAction({ startDate: this.startDate, endDate: this.endDate, approvedAfter: this.approvedAfter, - markAsExported: this.markAsExportedFilter, + markedAsExported: this.markedAsExported, policyIDList: this.policyIds ? this.policyIds.join(",") : undefined, }, - reportStates: this.reportStates + reportState: this.reportStates ? this.reportStates.join(",") : undefined, employeeEmail: this.employeeEmail, @@ -184,7 +184,7 @@ export default defineAction({ }, outputSettings: { fileExtension: this.fileExtension, - fileBaseName: this.fileBaseName, + fileBasename: this.fileBaseName, includeFullPageReceiptsPdf: this.includeFullPageReceiptsPdf, }, onFinish, From 655b46dde4144d42e5f26e0521a379ffb7826718 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Wed, 10 Sep 2025 12:01:44 -0400 Subject: [PATCH 4/6] updates --- .../expensify/actions/export-report/export-report.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/components/expensify/actions/export-report/export-report.ts b/components/expensify/actions/export-report/export-report.ts index 09cfa4304eb18..a047b8a6869a8 100644 --- a/components/expensify/actions/export-report/export-report.ts +++ b/components/expensify/actions/export-report/export-report.ts @@ -110,7 +110,7 @@ export default defineAction({ templatePath: { type: "string", label: "Template Path", - description: "The path in the /tmp directory to the template to use for the export", + description: "The path in the /tmp directory to the template to use for the export. Required if `fileExtension` is `csv`, `txt`, `json`, or `xml`.", optional: true, }, limit: { @@ -136,6 +136,15 @@ export default defineAction({ throw new ConfigurationError("At least one of `reportIds`, `startDate`, or `approvedAfter` must be specified"); } + if ([ + "csv", + "txt", + "json", + "xml", + ].includes(this.fileExtension) && !this.templatePath) { + throw new ConfigurationError(`Template path is required for file extension: ${this.fileExtension}`); + } + const onFinish = []; if (this.emailRecipients) { onFinish.push({ From f1b6fad434ea3d6d2cfce12db472435b341399e6 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Thu, 11 Sep 2025 12:13:37 -0400 Subject: [PATCH 5/6] updates --- components/expensify/actions/export-report/export-report.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/expensify/actions/export-report/export-report.ts b/components/expensify/actions/export-report/export-report.ts index a047b8a6869a8..04c1fa790359d 100644 --- a/components/expensify/actions/export-report/export-report.ts +++ b/components/expensify/actions/export-report/export-report.ts @@ -53,9 +53,9 @@ export default defineAction({ ], }, markedAsExported: { - type: "boolean", - label: "Marked as Exported", - description: "Filters out reports that have already been exported with that label out", + type: "string", + label: "Marked as Exported Label (Filter)", + description: "Filters out reports that have already been exported with that label", optional: true, }, reportStates: { From 1f7dc95adcdbd26396dfda4d7a04bb0cdfc77c6d Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Thu, 11 Sep 2025 12:33:41 -0400 Subject: [PATCH 6/6] updates --- components/expensify/actions/export-report/export-report.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/expensify/actions/export-report/export-report.ts b/components/expensify/actions/export-report/export-report.ts index 04c1fa790359d..d59e02f92233b 100644 --- a/components/expensify/actions/export-report/export-report.ts +++ b/components/expensify/actions/export-report/export-report.ts @@ -9,7 +9,7 @@ import qs from "qs"; export default defineAction({ key: "expensify-export-report", name: "Export Report", - description: "Export a report to a PDF file. [See the documentation](https://integrations.expensify.com/Integration-Server/doc/#report-exporter)", + description: "Export Expensify reports to a file (csv, xls, xlsx, txt, pdf, json, xml). [See the documentation](https://integrations.expensify.com/Integration-Server/doc/#report-exporter)", version: "0.0.1", type: "action", props: { @@ -17,7 +17,7 @@ export default defineAction({ reportIds: { type: "string[]", label: "Report IDs", - description: "The IDs of the reports to be exported. Required if `startDate` or a`pprovedAfter` are not specified.", + description: "The IDs of the reports to be exported. Required if `startDate` or `approvedAfter` are not specified.", optional: true, }, startDate: {