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/actions/export-report/export-report.ts b/components/expensify/actions/export-report/export-report.ts new file mode 100644 index 0000000000000..d59e02f92233b --- /dev/null +++ b/components/expensify/actions/export-report/export-report.ts @@ -0,0 +1,247 @@ +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 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: { + expensify, + reportIds: { + type: "string[]", + label: "Report IDs", + description: "The IDs of the reports to be exported. Required if `startDate` or `approvedAfter` 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", + ], + }, + markedAsExported: { + type: "string", + label: "Marked as Exported Label (Filter)", + description: "Filters out reports that have already been exported with that label", + 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: "Export reports for the specified employee email", + optional: true, + }, + policyIds: { + propDefinition: [ + expensify, + "policyExportIds", + ], + }, + 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. Required if `fileExtension` is `csv`, `txt`, `json`, or `xml`.", + 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"); + } + + 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({ + 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, + markedAsExported: this.markedAsExported, + policyIDList: this.policyIds + ? this.policyIds.join(",") + : undefined, + }, + reportState: 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 4593136a9f5ad..5cb282c0e7dc5 100644 --- a/components/expensify/app/expensify.app.ts +++ b/components/expensify/app/expensify.app.ts @@ -6,15 +6,30 @@ export default defineApp({ type: "app", app: "expensify", propDefinitions: { + policyExportIds: { + 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, + })) || []; + }, + }, employeeEmail: { type: "string", label: "Employee Email", - description: "The expenses will be created in this account.", + description: "The expenses will be created in this account", }, policyId: { type: "string", label: "Policy ID", - description: "Select the policy where the report will be created.", + description: "Select the policy where the report will be created", async options({ userEmail }) { const { policyList } = await this.getPolicyList({ userEmail, @@ -152,7 +167,17 @@ export default defineApp({ }, }, $); }, - + async listPolicies({ $ = this } = {}) { + return this._makeRequest({ + method: "post", + data: { + type: "get", + inputSettings: { + type: "policyList", + }, + }, + }, $); + }, async downloadFile({ $, fileName, }) { 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": [