From 571e89d348088f2bbd70d4326f4aa2dabf993f15 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Mon, 13 Jan 2025 15:54:55 -0500 Subject: [PATCH 1/6] clear_books init --- .../actions/create-client/create-client.mjs | 60 +++++ .../actions/create-invoice/create-invoice.mjs | 76 ++++++ .../actions/record-payment/record-payment.mjs | 53 +++++ components/clear_books/clear_books.app.mjs | 222 +++++++++++++++++- components/clear_books/package.json | 2 +- .../new-client-instant/new-client-instant.mjs | 158 +++++++++++++ .../new-invoice-instant.mjs | 85 +++++++ .../new-payment-instant.mjs | 110 +++++++++ 8 files changed, 762 insertions(+), 4 deletions(-) create mode 100644 components/clear_books/actions/create-client/create-client.mjs create mode 100644 components/clear_books/actions/create-invoice/create-invoice.mjs create mode 100644 components/clear_books/actions/record-payment/record-payment.mjs create mode 100644 components/clear_books/sources/new-client-instant/new-client-instant.mjs create mode 100644 components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs create mode 100644 components/clear_books/sources/new-payment-instant/new-payment-instant.mjs diff --git a/components/clear_books/actions/create-client/create-client.mjs b/components/clear_books/actions/create-client/create-client.mjs new file mode 100644 index 0000000000000..cacedc8cbba7f --- /dev/null +++ b/components/clear_books/actions/create-client/create-client.mjs @@ -0,0 +1,60 @@ +import clearBooks from "../../clear_books.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "clear_books-create-client", + name: "Create Client", + description: "Creates a new client in Clear Books. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + clear_books: { + type: "app", + app: "clear_books", + }, + createClientName: { + propDefinition: [ + "clear_books", + "createClientName", + ], + }, + createClientContactDetails: { + propDefinition: [ + "clear_books", + "createClientContactDetails", + ], + }, + createClientNotes: { + propDefinition: [ + "clear_books", + "createClientNotes", + ], + optional: true, + }, + createClientTags: { + propDefinition: [ + "clear_books", + "createClientTags", + ], + optional: true, + }, + }, + async run({ $ }) { + const data = { + name: this.createClientName, + contact_details: this.createClientContactDetails.map(JSON.parse), + }; + + if (this.createClientNotes) { + data.notes = this.createClientNotes; + } + + if (this.createClientTags) { + data.tags = this.createClientTags; + } + + const client = await this.clear_books.createClient(data); + $.export("$summary", `Created client ${client.name} with ID ${client.id}`); + return client; + }, +}; diff --git a/components/clear_books/actions/create-invoice/create-invoice.mjs b/components/clear_books/actions/create-invoice/create-invoice.mjs new file mode 100644 index 0000000000000..93000b98bdfbf --- /dev/null +++ b/components/clear_books/actions/create-invoice/create-invoice.mjs @@ -0,0 +1,76 @@ +import clear_books from "../../clear_books.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "clear_books-create-invoice", + name: "Create Invoice", + description: "Creates a new invoice in Clear Books. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + clear_books, + createInvoiceClientDetails: { + propDefinition: [ + clear_books, + "createInvoiceClientDetails", + ], + }, + createInvoiceLineItems: { + propDefinition: [ + clear_books, + "createInvoiceLineItems", + ], + }, + createInvoiceIssueDate: { + propDefinition: [ + clear_books, + "createInvoiceIssueDate", + ], + }, + createInvoiceNotes: { + propDefinition: [ + clear_books, + "createInvoiceNotes", + ], + }, + createInvoiceDueDate: { + propDefinition: [ + clear_books, + "createInvoiceDueDate", + ], + }, + createInvoiceTags: { + propDefinition: [ + clear_books, + "createInvoiceTags", + ], + }, + }, + async run({ $ }) { + const clientDetails = JSON.parse(this.createInvoiceClientDetails[0]); + const lineItems = this.createInvoiceLineItems.map((item) => JSON.parse(item)); + + const data = { + client_details: clientDetails, + line_items: lineItems, + issue_date: this.createInvoiceIssueDate, + }; + + if (this.createInvoiceNotes) { + data.notes = this.createInvoiceNotes; + } + + if (this.createInvoiceDueDate) { + data.due_date = this.createInvoiceDueDate; + } + + if (this.createInvoiceTags && this.createInvoiceTags.length > 0) { + data.tags = this.createInvoiceTags; + } + + const response = await this.clear_books.createInvoice(data); + + $.export("$summary", `Created invoice with ID ${response.id}`); + return response; + }, +}; diff --git a/components/clear_books/actions/record-payment/record-payment.mjs b/components/clear_books/actions/record-payment/record-payment.mjs new file mode 100644 index 0000000000000..f4716443fe4d6 --- /dev/null +++ b/components/clear_books/actions/record-payment/record-payment.mjs @@ -0,0 +1,53 @@ +import clear_books from "../../clear_books.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "clear_books-record-payment", + name: "Record Payment", + description: "Records a payment against an existing invoice. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + clear_books: { + type: "app", + app: "clear_books", + }, + recordPaymentAmount: { + propDefinition: [ + "clear_books", + "recordPaymentAmount", + ], + }, + recordInvoiceId: { + propDefinition: [ + "clear_books", + "recordInvoiceId", + ], + }, + recordPaymentDate: { + propDefinition: [ + "clear_books", + "recordPaymentDate", + ], + }, + recordPaymentMethod: { + propDefinition: [ + "clear_books", + "recordPaymentMethod", + ], + }, + }, + async run({ $ }) { + const paymentData = { + amount: this.recordPaymentAmount, + invoice_id: this.recordInvoiceId, + payment_date: this.recordPaymentDate, + }; + if (this.recordPaymentMethod) { + paymentData.payment_method = this.recordPaymentMethod; + } + const response = await this.clear_books.recordPayment(paymentData); + $.export("$summary", `Recorded payment of $${this.recordPaymentAmount} on invoice ${this.recordInvoiceId}`); + return response; + }, +}; diff --git a/components/clear_books/clear_books.app.mjs b/components/clear_books/clear_books.app.mjs index 33887c22cdf6c..bdc02faafdb37 100644 --- a/components/clear_books/clear_books.app.mjs +++ b/components/clear_books/clear_books.app.mjs @@ -1,11 +1,227 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "clear_books", - propDefinitions: {}, + propDefinitions: { + // Triggers + invoiceStatusFilter: { + type: "string", + label: "Invoice Status Filter", + description: "Filter invoices by status (e.g., draft, sent, paid)", + optional: true, + }, + paymentMethodFilter: { + type: "string", + label: "Payment Method Filter", + description: "Filter payments by payment method", + optional: true, + }, + invoiceReferenceFilter: { + type: "string", + label: "Invoice Reference Filter", + description: "Filter payments by invoice reference", + optional: true, + }, + clientTagsFilter: { + type: "string[]", + label: "Client Tags Filter", + description: "Filter clients by specific tags", + optional: true, + }, + clientTypesFilter: { + type: "string", + label: "Client Types Filter", + description: "Filter clients by specific types", + optional: true, + }, + // Actions - Create Invoice + createInvoiceClientDetails: { + type: "string[]", + label: "Client Details", + description: "Details of the client. Each detail should be a JSON string.", + }, + createInvoiceLineItems: { + type: "string[]", + label: "Line Items", + description: "Line items for the invoice. Each item should be a JSON string.", + }, + createInvoiceIssueDate: { + type: "string", + label: "Issue Date", + description: "Date when the invoice is issued.", + }, + createInvoiceNotes: { + type: "string", + label: "Notes", + description: "Optional notes for the invoice.", + optional: true, + }, + createInvoiceDueDate: { + type: "string", + label: "Due Date", + description: "Optional due date for the invoice.", + optional: true, + }, + createInvoiceTags: { + type: "string[]", + label: "Tags", + description: "Optional tags for the invoice.", + optional: true, + }, + // Actions - Record Payment + recordPaymentAmount: { + type: "number", + label: "Payment Amount", + description: "Amount of the payment.", + }, + recordInvoiceId: { + type: "string", + label: "Invoice ID", + description: "ID of the invoice to record the payment against.", + }, + recordPaymentDate: { + type: "string", + label: "Payment Date", + description: "Date when the payment was made.", + }, + recordPaymentMethod: { + type: "string", + label: "Payment Method", + description: "Optional payment method.", + optional: true, + }, + // Actions - Create Client + createClientName: { + type: "string", + label: "Client Name", + description: "Name of the client.", + }, + createClientContactDetails: { + type: "string[]", + label: "Contact Details", + description: "Contact details for the client. Each detail should be a JSON string.", + }, + createClientNotes: { + type: "string", + label: "Notes", + description: "Optional notes for the client.", + optional: true, + }, + createClientTags: { + type: "string[]", + label: "Tags", + description: "Optional tags for the client.", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data authKeys() { console.log(Object.keys(this.$auth)); }, + _baseUrl() { + return "https://api.clearbooks.com/v1"; + }, + async _makeRequest(opts = {}) { + const { + $, method = "GET", path = "/", headers, data, params, ...otherOpts + } = opts; + return axios($, { + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "Authorization": `Bearer ${this.$auth.api_token}`, + "Content-Type": "application/json", + }, + data, + params, + ...otherOpts, + }); + }, + // Auxiliary Methods + async listInvoices(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/invoices", + params: opts, + }); + }, + async getInvoice(opts = {}) { + const { invoiceId } = opts; + return this._makeRequest({ + method: "GET", + path: `/invoices/${invoiceId}`, + }); + }, + async createInvoice(data) { + return this._makeRequest({ + method: "POST", + path: "/invoices", + data, + }); + }, + async listPayments(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/payments", + params: opts, + }); + }, + async getPayment(opts = {}) { + const { paymentId } = opts; + return this._makeRequest({ + method: "GET", + path: `/payments/${paymentId}`, + }); + }, + async recordPayment(data) { + return this._makeRequest({ + method: "POST", + path: "/payments", + data, + }); + }, + async listClients(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/clients", + params: opts, + }); + }, + async getClient(opts = {}) { + const { clientId } = opts; + return this._makeRequest({ + method: "GET", + path: `/clients/${clientId}`, + }); + }, + async createClient(data) { + return this._makeRequest({ + method: "POST", + path: "/clients", + data, + }); + }, + async paginate(fn, ...opts) { + const results = []; + let hasMore = true; + let page = 1; + + while (hasMore) { + const response = await fn({ + page, + ...opts, + }); + if (response.length === 0) { + hasMore = false; + } else { + results.push(...response); + page += 1; + } + } + + return results; + }, }, -}; \ No newline at end of file +}; diff --git a/components/clear_books/package.json b/components/clear_books/package.json index d8a028a1515cb..add8c6eefe9f4 100644 --- a/components/clear_books/package.json +++ b/components/clear_books/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/clear_books/sources/new-client-instant/new-client-instant.mjs b/components/clear_books/sources/new-client-instant/new-client-instant.mjs new file mode 100644 index 0000000000000..fd2f19e79a841 --- /dev/null +++ b/components/clear_books/sources/new-client-instant/new-client-instant.mjs @@ -0,0 +1,158 @@ +import clear_books from "../../clear_books.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "clear_books-new-client-instant", + name: "New Client Instant", + description: "Emit new event when a new client is added to the system. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + clear_books: { + type: "app", + app: "clear_books", + }, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + clientTagsFilter: { + propDefinition: [ + clear_books, + "clientTagsFilter", + ], + }, + clientTypesFilter: { + propDefinition: [ + clear_books, + "clientTypesFilter", + ], + }, + }, + hooks: { + async deploy() { + const params = {}; + if (this.clientTagsFilter && this.clientTagsFilter.length > 0) { + params.tags = this.clientTagsFilter; + } + if (this.clientTypesFilter && this.clientTypesFilter.length > 0) { + params.types = this.clientTypesFilter; + } + const clients = await this.clear_books.paginate(this.clear_books.listClients, params); + const last50Clients = clients.slice(-50); + for (const client of last50Clients) { + this.$emit(client, { + id: client.id || client.ts, + summary: `New client: ${client.name}`, + ts: client.created_at + ? Date.parse(client.created_at) + : Date.now(), + }); + } + }, + async activate() { + try { + const callbackUrl = this.http.endpoint; + const data = { + event: "client.created", + callback_url: callbackUrl, + }; + const response = await this.clear_books._makeRequest({ + method: "POST", + path: "/webhooks", + data, + }); + const webhookId = response.id; + await this.db.set("webhookId", webhookId); + } catch (error) { + throw new Error(`Failed to activate webhook: ${error.message}`); + } + }, + async deactivate() { + try { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this.clear_books._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + await this.db.delete("webhookId"); + } + } catch (error) { + throw new Error(`Failed to deactivate webhook: ${error.message}`); + } + }, + }, + async run(event) { + try { + const signature = event.headers["X-Signature"]; + const rawBody = event.body_raw; + const secret = this.clear_books.$auth.webhook_secret; + + if (secret) { + const computedSignature = crypto.createHmac("sha256", secret).update(rawBody) + .digest("hex"); + if (computedSignature !== signature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + } + + const client = event.body; + + // Apply Client Tags Filter + if (this.clientTagsFilter && this.clientTagsFilter.length > 0) { + const clientTags = client.tags || []; + const hasMatchingTag = this.clientTagsFilter.some((tag) => clientTags.includes(tag)); + if (!hasMatchingTag) { + await this.http.respond({ + status: 200, + body: "No matching tags.", + }); + return; + } + } + + // Apply Client Types Filter + if (this.clientTypesFilter && this.clientTypesFilter.length > 0) { + const clientType = client.type; + const matchesType = this.clientTypesFilter.includes(clientType); + if (!matchesType) { + await this.http.respond({ + status: 200, + body: "No matching types.", + }); + return; + } + } + + // Emit the event + this.$emit(client, { + id: client.id || client.ts, + summary: `New client: ${client.name}`, + ts: client.created_at + ? Date.parse(client.created_at) + : Date.now(), + }); + + // Respond to the webhook + await this.http.respond({ + status: 200, + body: "OK", + }); + } catch (error) { + // Handle any unexpected errors + await this.http.respond({ + status: 500, + body: `Error processing webhook: ${error.message}`, + }); + throw error; + } + }, +}; diff --git a/components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs b/components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs new file mode 100644 index 0000000000000..dad1ca1b6075b --- /dev/null +++ b/components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs @@ -0,0 +1,85 @@ +import clear_books from "../../clear_books.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "clear_books-new-invoice-instant", + name: "New Invoice Created", + description: "Emit a new event when a new invoice is created. [See the documentation](${docsLink})", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + clear_books: { + type: "app", + app: "clear_books", + }, + invoiceStatusFilter: { + propDefinition: [ + "clear_books", + "invoiceStatusFilter", + ], + }, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: { + type: "$.service.db", + }, + }, + hooks: { + async deploy() { + const params = {}; + if (this.invoiceStatusFilter) { + params.status = this.invoiceStatusFilter; + } + const invoices = await this.clear_books.paginate(this.clear_books.listInvoices, params); + const last50 = invoices.slice(-50); + for (const invoice of last50) { + this.$emit(invoice, { + id: invoice.id || String(invoice.created_at), + summary: `Invoice ${invoice.id} created with status ${invoice.status}`, + ts: Date.parse(invoice.issue_date) || Date.now(), + }); + } + }, + async activate() { + const data = { + event: "invoice.created", + url: this.http.endpoint, + }; + if (this.invoiceStatusFilter) { + data.filter = { + status: this.invoiceStatusFilter, + }; + } + const webhook = await this.clear_books._makeRequest({ + method: "POST", + path: "/webhooks", + data, + }); + await this.db.set("webhookId", webhook.id); + }, + async deactivate() { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this.clear_books._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + await this.db.set("webhookId", null); + } + }, + }, + async run(event) { + const invoice = event.body; + if (this.invoiceStatusFilter && invoice.status !== this.invoiceStatusFilter) { + return; + } + this.$emit(invoice, { + id: invoice.id, + summary: `Invoice ${invoice.id} created with status ${invoice.status}`, + ts: new Date(invoice.issue_date).getTime(), + }); + }, +}; diff --git a/components/clear_books/sources/new-payment-instant/new-payment-instant.mjs b/components/clear_books/sources/new-payment-instant/new-payment-instant.mjs new file mode 100644 index 0000000000000..9073665802f5a --- /dev/null +++ b/components/clear_books/sources/new-payment-instant/new-payment-instant.mjs @@ -0,0 +1,110 @@ +import clear_books from "../../clear_books.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "clear_books-new-payment-instant", + name: "New Payment Recorded", + description: "Emit new event when a payment is recorded. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + clear_books: { + type: "app", + app: "clear_books", + }, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + paymentMethodFilter: { + propDefinition: [ + "clear_books", + "paymentMethodFilter", + ], + optional: true, + }, + invoiceReferenceFilter: { + propDefinition: [ + "clear_books", + "invoiceReferenceFilter", + ], + optional: true, + }, + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(id) { + return this.db.set("webhookId", id); + }, + }, + hooks: { + async activate() { + const webhookUrl = this.http.endpoint; + const data = { + url: webhookUrl, + event: "payment.recorded", + }; + const response = await this.clear_books._makeRequest({ + method: "POST", + path: "/webhooks", + data, + }); + const webhookId = response.id; + await this._setWebhookId(webhookId); + }, + async deactivate() { + const webhookId = await this._getWebhookId(); + if (webhookId) { + await this.clear_books._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + await this._setWebhookId(null); + } + }, + async deploy() { + const payments = await this.clear_books.paginate(this.clear_books.listPayments, { + paymentMethod: this.paymentMethodFilter, + invoiceReference: this.invoiceReferenceFilter, + }); + const recentPayments = payments.slice(-50).reverse(); + for (const payment of recentPayments) { + this.$emit(payment, { + id: payment.id || payment.ts || Date.now(), + summary: `New payment recorded: $${payment.amount}`, + ts: payment.date + ? Date.parse(payment.date) + : Date.now(), + }); + } + }, + }, + async run(event) { + const payment = event; + if ( + (this.paymentMethodFilter && payment.paymentMethod !== this.paymentMethodFilter) || + (this.invoiceReferenceFilter && payment.invoiceReference !== this.invoiceReferenceFilter) + ) { + this.http.respond({ + status: 200, + body: "OK", + }); + return; + } + this.$emit(payment, { + id: payment.id || payment.ts || Date.now(), + summary: `New payment recorded: $${payment.amount}`, + ts: payment.date + ? Date.parse(payment.date) + : Date.now(), + }); + this.http.respond({ + status: 200, + body: "OK", + }); + }, +}; From ed2c989307bbfe50eff004ece2e34109c1ade5e2 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 14 Jan 2025 12:15:53 -0500 Subject: [PATCH 2/6] new-components --- .../actions/create-client/create-client.mjs | 60 ---- .../create-customer/create-customer.mjs | 84 ++++++ .../actions/create-invoice/create-invoice.mjs | 76 ----- .../create-purchase-document.mjs | 85 ++++++ .../create-supplier/create-supplier.mjs | 84 ++++++ .../actions/record-payment/record-payment.mjs | 53 ---- components/clear_books/clear_books.app.mjs | 267 ++++++------------ components/clear_books/package.json | 5 +- .../clear_books/sources/common/base.mjs | 79 ++++++ .../new-client-instant/new-client-instant.mjs | 158 ----------- .../new-customer-created.mjs | 24 ++ .../new-invoice-instant.mjs | 85 ------ .../new-payment-instant.mjs | 110 -------- .../new-purchase-document-created.mjs | 38 +++ .../new-transaction-created.mjs | 24 ++ 15 files changed, 508 insertions(+), 724 deletions(-) delete mode 100644 components/clear_books/actions/create-client/create-client.mjs create mode 100644 components/clear_books/actions/create-customer/create-customer.mjs delete mode 100644 components/clear_books/actions/create-invoice/create-invoice.mjs create mode 100644 components/clear_books/actions/create-purchase-document/create-purchase-document.mjs create mode 100644 components/clear_books/actions/create-supplier/create-supplier.mjs delete mode 100644 components/clear_books/actions/record-payment/record-payment.mjs create mode 100644 components/clear_books/sources/common/base.mjs delete mode 100644 components/clear_books/sources/new-client-instant/new-client-instant.mjs create mode 100644 components/clear_books/sources/new-customer-created/new-customer-created.mjs delete mode 100644 components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs delete mode 100644 components/clear_books/sources/new-payment-instant/new-payment-instant.mjs create mode 100644 components/clear_books/sources/new-purchase-document-created/new-purchase-document-created.mjs create mode 100644 components/clear_books/sources/new-transaction-created/new-transaction-created.mjs diff --git a/components/clear_books/actions/create-client/create-client.mjs b/components/clear_books/actions/create-client/create-client.mjs deleted file mode 100644 index cacedc8cbba7f..0000000000000 --- a/components/clear_books/actions/create-client/create-client.mjs +++ /dev/null @@ -1,60 +0,0 @@ -import clearBooks from "../../clear_books.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "clear_books-create-client", - name: "Create Client", - description: "Creates a new client in Clear Books. [See the documentation]()", - version: "0.0.{{ts}}", - type: "action", - props: { - clear_books: { - type: "app", - app: "clear_books", - }, - createClientName: { - propDefinition: [ - "clear_books", - "createClientName", - ], - }, - createClientContactDetails: { - propDefinition: [ - "clear_books", - "createClientContactDetails", - ], - }, - createClientNotes: { - propDefinition: [ - "clear_books", - "createClientNotes", - ], - optional: true, - }, - createClientTags: { - propDefinition: [ - "clear_books", - "createClientTags", - ], - optional: true, - }, - }, - async run({ $ }) { - const data = { - name: this.createClientName, - contact_details: this.createClientContactDetails.map(JSON.parse), - }; - - if (this.createClientNotes) { - data.notes = this.createClientNotes; - } - - if (this.createClientTags) { - data.tags = this.createClientTags; - } - - const client = await this.clear_books.createClient(data); - $.export("$summary", `Created client ${client.name} with ID ${client.id}`); - return client; - }, -}; diff --git a/components/clear_books/actions/create-customer/create-customer.mjs b/components/clear_books/actions/create-customer/create-customer.mjs new file mode 100644 index 0000000000000..f5770b8771a03 --- /dev/null +++ b/components/clear_books/actions/create-customer/create-customer.mjs @@ -0,0 +1,84 @@ +import clearBooks from "../../clear_books.app.mjs"; + +export default { + key: "clear_books-create-customer", + name: "Create Customer", + description: "Creates a new customer in Clear Books. [See the documentation](https://u.pcloud.link/publink/show?code=XZkThJ5Z4zKewgCL6VBpfxlPeHPDdXXj0Cc7)", + version: "0.0.1", + type: "action", + props: { + clearBooks, + name: { + type: "string", + label: "Name", + description: "Name of the customer", + }, + email: { + type: "string", + label: "Email", + description: "Email address of the customer", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "Phone number of the customer", + optional: true, + }, + streetAddress: { + type: "string", + label: "Street Address", + description: "Street address of the customer", + optional: true, + }, + town: { + type: "string", + label: "Town", + description: "Town of the customer", + optional: true, + }, + county: { + type: "string", + label: "County", + description: "County of the customer", + optional: true, + }, + postcode: { + type: "string", + label: "Postcode", + description: "Postcode of the customer", + optional: true, + }, + countryCode: { + type: "string", + label: "Country Code", + description: "Country code of the customer", + optional: true, + }, + }, + async run({ $ }) { + const hasAddress = this.streetAddress + || this.town + || this.county + || this.postcode + || this.countryCode; + + const response = await this.clearBooks.createCustomer({ + $, + data: { + name: this.name, + email: this.email, + phone: this.phone, + address: hasAddress && { + line1: this.streetAddress, + town: this.town, + county: this.county, + postcode: this.postcode, + countryCode: this.countryCode, + }, + }, + }); + $.export("$summary", `Successfully created customer with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/clear_books/actions/create-invoice/create-invoice.mjs b/components/clear_books/actions/create-invoice/create-invoice.mjs deleted file mode 100644 index 93000b98bdfbf..0000000000000 --- a/components/clear_books/actions/create-invoice/create-invoice.mjs +++ /dev/null @@ -1,76 +0,0 @@ -import clear_books from "../../clear_books.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "clear_books-create-invoice", - name: "Create Invoice", - description: "Creates a new invoice in Clear Books. [See the documentation]()", - version: "0.0.{{ts}}", - type: "action", - props: { - clear_books, - createInvoiceClientDetails: { - propDefinition: [ - clear_books, - "createInvoiceClientDetails", - ], - }, - createInvoiceLineItems: { - propDefinition: [ - clear_books, - "createInvoiceLineItems", - ], - }, - createInvoiceIssueDate: { - propDefinition: [ - clear_books, - "createInvoiceIssueDate", - ], - }, - createInvoiceNotes: { - propDefinition: [ - clear_books, - "createInvoiceNotes", - ], - }, - createInvoiceDueDate: { - propDefinition: [ - clear_books, - "createInvoiceDueDate", - ], - }, - createInvoiceTags: { - propDefinition: [ - clear_books, - "createInvoiceTags", - ], - }, - }, - async run({ $ }) { - const clientDetails = JSON.parse(this.createInvoiceClientDetails[0]); - const lineItems = this.createInvoiceLineItems.map((item) => JSON.parse(item)); - - const data = { - client_details: clientDetails, - line_items: lineItems, - issue_date: this.createInvoiceIssueDate, - }; - - if (this.createInvoiceNotes) { - data.notes = this.createInvoiceNotes; - } - - if (this.createInvoiceDueDate) { - data.due_date = this.createInvoiceDueDate; - } - - if (this.createInvoiceTags && this.createInvoiceTags.length > 0) { - data.tags = this.createInvoiceTags; - } - - const response = await this.clear_books.createInvoice(data); - - $.export("$summary", `Created invoice with ID ${response.id}`); - return response; - }, -}; diff --git a/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs new file mode 100644 index 0000000000000..e706a9a60fad7 --- /dev/null +++ b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs @@ -0,0 +1,85 @@ +import clearBooks from "../../clear_books.app.mjs"; + +export default { + key: "clear_books-create-purchase-document", + name: "Create Purchase Document", + description: "Creates a new Purchase Document in Clear Books. [See the documentation](https://u.pcloud.link/publink/show?code=XZkThJ5Z4zKewgCL6VBpfxlPeHPDdXXj0Cc7)", + version: "0.0.1", + type: "action", + props: { + clearBooks, + purchaseType: { + propDefinition: [ + clearBooks, + "purchaseType", + ], + }, + date: { + type: "string", + label: "Date", + description: "The date of the purchase. Format: YYYY-MM-DD", + }, + supplierId: { + propDefinition: [ + clearBooks, + "supplierId", + ], + }, + description: { + type: "string", + label: "Description", + description: "Description of the purchase document", + optional: true, + }, + numLineItems: { + type: "integer", + label: "Number of Line Items", + description: "The number of line items to create", + reloadProps: true, + }, + }, + additionalProps() { + const props = {}; + if (!this.numLineItems) { + return props; + } + for (let i = 1; i <= this.numLineItems; i++) { + props[`line_item_${i}_unit_price`] = { + type: "string", + label: `Line Item ${i} - Unit Price`, + }; + props[`line_item_${i}_quantity`] = { + type: "integer", + label: `Line Item ${i} - Quantity`, + }; + props[`line_item_${i}_description`] = { + type: "string", + label: `Line Item ${i} - Description`, + }; + } + return props; + }, + async run({ $ }) { + const lineItems = []; + for (let i = 1; i <= this.numLineItems; i++) { + lineItems.push({ + unitPrice: this[`line_items_${i}_unit_price`], + quantity: this[`line_items_${i}_quantity`], + description: this[`line_items_${i}_description`], + }); + } + + const response = await this.clearBooks.createPurchaseDocument({ + $, + type: this.purchaseType, + data: { + date: this.date, + supplierId: this.supplierId, + description: this.description, + lineItems, + }, + }); + $.export("$summary", `Successfully created purchase document with ID ${response.id}`); + return response; + }, +}; diff --git a/components/clear_books/actions/create-supplier/create-supplier.mjs b/components/clear_books/actions/create-supplier/create-supplier.mjs new file mode 100644 index 0000000000000..f728e96d9384b --- /dev/null +++ b/components/clear_books/actions/create-supplier/create-supplier.mjs @@ -0,0 +1,84 @@ +import clearBooks from "../../clear_books.app.mjs"; + +export default { + key: "clear_books-create-supplier", + name: "Create Supplier", + description: "Creates a new supplier in Clear Books. [See the documentation](https://u.pcloud.link/publink/show?code=XZkThJ5Z4zKewgCL6VBpfxlPeHPDdXXj0Cc7)", + version: "0.0.1", + type: "action", + props: { + clearBooks, + name: { + type: "string", + label: "Name", + description: "Name of the supplier", + }, + email: { + type: "string", + label: "Email", + description: "Email address of the supplier", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "Phone number of the supplier", + optional: true, + }, + streetAddress: { + type: "string", + label: "Street Address", + description: "Street address of the supplier", + optional: true, + }, + town: { + type: "string", + label: "Town", + description: "Town of the supplier", + optional: true, + }, + county: { + type: "string", + label: "County", + description: "County of the supplier", + optional: true, + }, + postcode: { + type: "string", + label: "Postcode", + description: "Postcode of the supplier", + optional: true, + }, + countryCode: { + type: "string", + label: "Country Code", + description: "Country code of the supplier", + optional: true, + }, + }, + async run({ $ }) { + const hasAddress = this.streetAddress + || this.town + || this.county + || this.postcode + || this.countryCode; + + const response = await this.clearBooks.createSupplier({ + $, + data: { + name: this.name, + email: this.email, + phone: this.phone, + address: hasAddress && { + line1: this.streetAddress, + town: this.town, + county: this.county, + postcode: this.postcode, + countryCode: this.countryCode, + }, + }, + }); + $.export("$summary", `Successfully created supplier with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/clear_books/actions/record-payment/record-payment.mjs b/components/clear_books/actions/record-payment/record-payment.mjs deleted file mode 100644 index f4716443fe4d6..0000000000000 --- a/components/clear_books/actions/record-payment/record-payment.mjs +++ /dev/null @@ -1,53 +0,0 @@ -import clear_books from "../../clear_books.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "clear_books-record-payment", - name: "Record Payment", - description: "Records a payment against an existing invoice. [See the documentation]()", - version: "0.0.{{ts}}", - type: "action", - props: { - clear_books: { - type: "app", - app: "clear_books", - }, - recordPaymentAmount: { - propDefinition: [ - "clear_books", - "recordPaymentAmount", - ], - }, - recordInvoiceId: { - propDefinition: [ - "clear_books", - "recordInvoiceId", - ], - }, - recordPaymentDate: { - propDefinition: [ - "clear_books", - "recordPaymentDate", - ], - }, - recordPaymentMethod: { - propDefinition: [ - "clear_books", - "recordPaymentMethod", - ], - }, - }, - async run({ $ }) { - const paymentData = { - amount: this.recordPaymentAmount, - invoice_id: this.recordInvoiceId, - payment_date: this.recordPaymentDate, - }; - if (this.recordPaymentMethod) { - paymentData.payment_method = this.recordPaymentMethod; - } - const response = await this.clear_books.recordPayment(paymentData); - $.export("$summary", `Recorded payment of $${this.recordPaymentAmount} on invoice ${this.recordInvoiceId}`); - return response; - }, -}; diff --git a/components/clear_books/clear_books.app.mjs b/components/clear_books/clear_books.app.mjs index bdc02faafdb37..e183d7ad9e886 100644 --- a/components/clear_books/clear_books.app.mjs +++ b/components/clear_books/clear_books.app.mjs @@ -4,224 +4,129 @@ export default { type: "app", app: "clear_books", propDefinitions: { - // Triggers - invoiceStatusFilter: { + supplierId: { type: "string", - label: "Invoice Status Filter", - description: "Filter invoices by status (e.g., draft, sent, paid)", - optional: true, - }, - paymentMethodFilter: { - type: "string", - label: "Payment Method Filter", - description: "Filter payments by payment method", - optional: true, - }, - invoiceReferenceFilter: { - type: "string", - label: "Invoice Reference Filter", - description: "Filter payments by invoice reference", - optional: true, - }, - clientTagsFilter: { - type: "string[]", - label: "Client Tags Filter", - description: "Filter clients by specific tags", - optional: true, - }, - clientTypesFilter: { - type: "string", - label: "Client Types Filter", - description: "Filter clients by specific types", - optional: true, - }, - // Actions - Create Invoice - createInvoiceClientDetails: { - type: "string[]", - label: "Client Details", - description: "Details of the client. Each detail should be a JSON string.", - }, - createInvoiceLineItems: { - type: "string[]", - label: "Line Items", - description: "Line items for the invoice. Each item should be a JSON string.", - }, - createInvoiceIssueDate: { - type: "string", - label: "Issue Date", - description: "Date when the invoice is issued.", - }, - createInvoiceNotes: { - type: "string", - label: "Notes", - description: "Optional notes for the invoice.", - optional: true, - }, - createInvoiceDueDate: { - type: "string", - label: "Due Date", - description: "Optional due date for the invoice.", - optional: true, - }, - createInvoiceTags: { - type: "string[]", - label: "Tags", - description: "Optional tags for the invoice.", - optional: true, - }, - // Actions - Record Payment - recordPaymentAmount: { - type: "number", - label: "Payment Amount", - description: "Amount of the payment.", - }, - recordInvoiceId: { - type: "string", - label: "Invoice ID", - description: "ID of the invoice to record the payment against.", - }, - recordPaymentDate: { - type: "string", - label: "Payment Date", - description: "Date when the payment was made.", - }, - recordPaymentMethod: { - type: "string", - label: "Payment Method", - description: "Optional payment method.", - optional: true, - }, - // Actions - Create Client - createClientName: { - type: "string", - label: "Client Name", - description: "Name of the client.", - }, - createClientContactDetails: { - type: "string[]", - label: "Contact Details", - description: "Contact details for the client. Each detail should be a JSON string.", - }, - createClientNotes: { + label: "Supplier ID", + description: "The identifier of a supplier", + async options({ page }) { + const suppliers = await this.listSuppliers({ + params: { + page: page + 1, + }, + }); + return suppliers?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + purchaseType: { type: "string", - label: "Notes", - description: "Optional notes for the client.", - optional: true, - }, - createClientTags: { - type: "string[]", - label: "Tags", - description: "Optional tags for the client.", - optional: true, + label: "Purchase Type", + description: "Type of purchase document", + options: [ + "bills", + "creditNotes", + ], }, }, methods: { - authKeys() { - console.log(Object.keys(this.$auth)); - }, _baseUrl() { - return "https://api.clearbooks.com/v1"; + return "https://api.clearbooks.co.uk/v1"; }, - async _makeRequest(opts = {}) { - const { - $, method = "GET", path = "/", headers, data, params, ...otherOpts - } = opts; + _makeRequest({ + $ = this, + path, + ...otherOpts + }) { return axios($, { - method, - url: this._baseUrl() + path, + url: `${this._baseUrl()}${path}`, headers: { - ...headers, - "Authorization": `Bearer ${this.$auth.api_token}`, - "Content-Type": "application/json", + Authorization: `Bearer ${this.$auth.oauth_access_token}`, }, - data, - params, ...otherOpts, }); }, - // Auxiliary Methods - async listInvoices(opts = {}) { + listCustomers(opts = {}) { return this._makeRequest({ - method: "GET", - path: "/invoices", - params: opts, + path: "/accounting/customers", + ...opts, }); }, - async getInvoice(opts = {}) { - const { invoiceId } = opts; + listPurchaseDocuments({ + type, ...opts + }) { return this._makeRequest({ - method: "GET", - path: `/invoices/${invoiceId}`, + path: `/accounting/purchases/${type}`, + ...opts, }); }, - async createInvoice(data) { + listTransactions(opts = {}) { return this._makeRequest({ - method: "POST", - path: "/invoices", - data, + path: "/accounting/transactions", + ...opts, }); }, - async listPayments(opts = {}) { + listSuppliers(opts = {}) { return this._makeRequest({ - method: "GET", - path: "/payments", - params: opts, + path: "/accounting/suppliers", + ...opts, }); }, - async getPayment(opts = {}) { - const { paymentId } = opts; - return this._makeRequest({ - method: "GET", - path: `/payments/${paymentId}`, - }); - }, - async recordPayment(data) { + createCustomer(opts = {}) { return this._makeRequest({ method: "POST", - path: "/payments", - data, - }); - }, - async listClients(opts = {}) { - return this._makeRequest({ - method: "GET", - path: "/clients", - params: opts, + path: "/accounting/customers", + ...opts, }); }, - async getClient(opts = {}) { - const { clientId } = opts; + createPurchaseDocument({ + type, ...opts + }) { return this._makeRequest({ - method: "GET", - path: `/clients/${clientId}`, + method: "POST", + path: `/accounting/purchases/${type}`, + ...opts, }); }, - async createClient(data) { + createSupplier(opts = {}) { return this._makeRequest({ method: "POST", - path: "/clients", - data, + path: "/accounting/suppliers", + ...opts, }); }, - async paginate(fn, ...opts) { - const results = []; - let hasMore = true; - let page = 1; - - while (hasMore) { - const response = await fn({ - page, - ...opts, - }); - if (response.length === 0) { - hasMore = false; - } else { - results.push(...response); - page += 1; - } + async *paginate({ + fn, + args, + max, + }) { + args = { + ...args, + params: { + ...args?.params, + limit: 100, + page: 1, + }, + }; + let total, count = 0; + try { + do { + const results = await fn(args); + for (const item of results) { + yield item; + if (max && ++count >= max) { + return; + } + } + total = results?.length; + args.params.page++; + } while (total === args.params.limit); + } catch (e) { + console.log(`Error: ${e}`); } - - return results; }, }, }; diff --git a/components/clear_books/package.json b/components/clear_books/package.json index add8c6eefe9f4..55fb4e395f0d0 100644 --- a/components/clear_books/package.json +++ b/components/clear_books/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/clear_books", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Clear Books Components", "main": "clear_books.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/clear_books/sources/common/base.mjs b/components/clear_books/sources/common/base.mjs new file mode 100644 index 0000000000000..52baefa2e2714 --- /dev/null +++ b/components/clear_books/sources/common/base.mjs @@ -0,0 +1,79 @@ +import clearBooks from "../../clear_books.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + clearBooks, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || 0; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + getBaseArgs() { + return { + params: { + sortBy: "id", + sortDirection: "descending", + }, + }; + }, + getArgs() { + return {}; + }, + getFn() { + throw new Error("getFn is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + async processEvent(max) { + const lastId = this._getLastId(); + const results = []; + + const items = this.clearBooks.paginate({ + fn: this.getFn(), + args: { + ...this.getBaseArgs(), + ...this.getArgs(), + }, + max, + }); + + for await (const item of items) { + if (item.id > lastId) { + results.push(item); + } else { + break; + } + } + + if (!results.length) { + return; + } + + this._setLastId(results[0].id); + results.reverse().forEach((item) => { + const meta = this.generateMeta(item); + this.$emit(item, meta); + }); + }, + }, + hooks: { + async deploy() { + await this.processEvent(25); + }, + }, + async run() { + await this.processEvent(); + }, +}; diff --git a/components/clear_books/sources/new-client-instant/new-client-instant.mjs b/components/clear_books/sources/new-client-instant/new-client-instant.mjs deleted file mode 100644 index fd2f19e79a841..0000000000000 --- a/components/clear_books/sources/new-client-instant/new-client-instant.mjs +++ /dev/null @@ -1,158 +0,0 @@ -import clear_books from "../../clear_books.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; - -export default { - key: "clear_books-new-client-instant", - name: "New Client Instant", - description: "Emit new event when a new client is added to the system. [See the documentation]()", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - clear_books: { - type: "app", - app: "clear_books", - }, - http: { - type: "$.interface.http", - customResponse: true, - }, - db: "$.service.db", - clientTagsFilter: { - propDefinition: [ - clear_books, - "clientTagsFilter", - ], - }, - clientTypesFilter: { - propDefinition: [ - clear_books, - "clientTypesFilter", - ], - }, - }, - hooks: { - async deploy() { - const params = {}; - if (this.clientTagsFilter && this.clientTagsFilter.length > 0) { - params.tags = this.clientTagsFilter; - } - if (this.clientTypesFilter && this.clientTypesFilter.length > 0) { - params.types = this.clientTypesFilter; - } - const clients = await this.clear_books.paginate(this.clear_books.listClients, params); - const last50Clients = clients.slice(-50); - for (const client of last50Clients) { - this.$emit(client, { - id: client.id || client.ts, - summary: `New client: ${client.name}`, - ts: client.created_at - ? Date.parse(client.created_at) - : Date.now(), - }); - } - }, - async activate() { - try { - const callbackUrl = this.http.endpoint; - const data = { - event: "client.created", - callback_url: callbackUrl, - }; - const response = await this.clear_books._makeRequest({ - method: "POST", - path: "/webhooks", - data, - }); - const webhookId = response.id; - await this.db.set("webhookId", webhookId); - } catch (error) { - throw new Error(`Failed to activate webhook: ${error.message}`); - } - }, - async deactivate() { - try { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this.clear_books._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - await this.db.delete("webhookId"); - } - } catch (error) { - throw new Error(`Failed to deactivate webhook: ${error.message}`); - } - }, - }, - async run(event) { - try { - const signature = event.headers["X-Signature"]; - const rawBody = event.body_raw; - const secret = this.clear_books.$auth.webhook_secret; - - if (secret) { - const computedSignature = crypto.createHmac("sha256", secret).update(rawBody) - .digest("hex"); - if (computedSignature !== signature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - } - - const client = event.body; - - // Apply Client Tags Filter - if (this.clientTagsFilter && this.clientTagsFilter.length > 0) { - const clientTags = client.tags || []; - const hasMatchingTag = this.clientTagsFilter.some((tag) => clientTags.includes(tag)); - if (!hasMatchingTag) { - await this.http.respond({ - status: 200, - body: "No matching tags.", - }); - return; - } - } - - // Apply Client Types Filter - if (this.clientTypesFilter && this.clientTypesFilter.length > 0) { - const clientType = client.type; - const matchesType = this.clientTypesFilter.includes(clientType); - if (!matchesType) { - await this.http.respond({ - status: 200, - body: "No matching types.", - }); - return; - } - } - - // Emit the event - this.$emit(client, { - id: client.id || client.ts, - summary: `New client: ${client.name}`, - ts: client.created_at - ? Date.parse(client.created_at) - : Date.now(), - }); - - // Respond to the webhook - await this.http.respond({ - status: 200, - body: "OK", - }); - } catch (error) { - // Handle any unexpected errors - await this.http.respond({ - status: 500, - body: `Error processing webhook: ${error.message}`, - }); - throw error; - } - }, -}; diff --git a/components/clear_books/sources/new-customer-created/new-customer-created.mjs b/components/clear_books/sources/new-customer-created/new-customer-created.mjs new file mode 100644 index 0000000000000..21a0a05497f4f --- /dev/null +++ b/components/clear_books/sources/new-customer-created/new-customer-created.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "clear_books-new-customer-created", + name: "New Customer Created", + description: "Emit new event when a new customer is added to the system.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFn() { + return this.clearBooks.listCustomers; + }, + generateMeta(customer) { + return { + id: customer.id, + summary: `New Customer with ID: ${customer.id}`, + ts: Date.now(), + }; + }, + }, +}; diff --git a/components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs b/components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs deleted file mode 100644 index dad1ca1b6075b..0000000000000 --- a/components/clear_books/sources/new-invoice-instant/new-invoice-instant.mjs +++ /dev/null @@ -1,85 +0,0 @@ -import clear_books from "../../clear_books.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "clear_books-new-invoice-instant", - name: "New Invoice Created", - description: "Emit a new event when a new invoice is created. [See the documentation](${docsLink})", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - clear_books: { - type: "app", - app: "clear_books", - }, - invoiceStatusFilter: { - propDefinition: [ - "clear_books", - "invoiceStatusFilter", - ], - }, - http: { - type: "$.interface.http", - customResponse: false, - }, - db: { - type: "$.service.db", - }, - }, - hooks: { - async deploy() { - const params = {}; - if (this.invoiceStatusFilter) { - params.status = this.invoiceStatusFilter; - } - const invoices = await this.clear_books.paginate(this.clear_books.listInvoices, params); - const last50 = invoices.slice(-50); - for (const invoice of last50) { - this.$emit(invoice, { - id: invoice.id || String(invoice.created_at), - summary: `Invoice ${invoice.id} created with status ${invoice.status}`, - ts: Date.parse(invoice.issue_date) || Date.now(), - }); - } - }, - async activate() { - const data = { - event: "invoice.created", - url: this.http.endpoint, - }; - if (this.invoiceStatusFilter) { - data.filter = { - status: this.invoiceStatusFilter, - }; - } - const webhook = await this.clear_books._makeRequest({ - method: "POST", - path: "/webhooks", - data, - }); - await this.db.set("webhookId", webhook.id); - }, - async deactivate() { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this.clear_books._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - await this.db.set("webhookId", null); - } - }, - }, - async run(event) { - const invoice = event.body; - if (this.invoiceStatusFilter && invoice.status !== this.invoiceStatusFilter) { - return; - } - this.$emit(invoice, { - id: invoice.id, - summary: `Invoice ${invoice.id} created with status ${invoice.status}`, - ts: new Date(invoice.issue_date).getTime(), - }); - }, -}; diff --git a/components/clear_books/sources/new-payment-instant/new-payment-instant.mjs b/components/clear_books/sources/new-payment-instant/new-payment-instant.mjs deleted file mode 100644 index 9073665802f5a..0000000000000 --- a/components/clear_books/sources/new-payment-instant/new-payment-instant.mjs +++ /dev/null @@ -1,110 +0,0 @@ -import clear_books from "../../clear_books.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "clear_books-new-payment-instant", - name: "New Payment Recorded", - description: "Emit new event when a payment is recorded. [See the documentation]()", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - clear_books: { - type: "app", - app: "clear_books", - }, - db: "$.service.db", - http: { - type: "$.interface.http", - customResponse: true, - }, - paymentMethodFilter: { - propDefinition: [ - "clear_books", - "paymentMethodFilter", - ], - optional: true, - }, - invoiceReferenceFilter: { - propDefinition: [ - "clear_books", - "invoiceReferenceFilter", - ], - optional: true, - }, - }, - methods: { - _getWebhookId() { - return this.db.get("webhookId"); - }, - _setWebhookId(id) { - return this.db.set("webhookId", id); - }, - }, - hooks: { - async activate() { - const webhookUrl = this.http.endpoint; - const data = { - url: webhookUrl, - event: "payment.recorded", - }; - const response = await this.clear_books._makeRequest({ - method: "POST", - path: "/webhooks", - data, - }); - const webhookId = response.id; - await this._setWebhookId(webhookId); - }, - async deactivate() { - const webhookId = await this._getWebhookId(); - if (webhookId) { - await this.clear_books._makeRequest({ - method: "DELETE", - path: `/webhooks/${webhookId}`, - }); - await this._setWebhookId(null); - } - }, - async deploy() { - const payments = await this.clear_books.paginate(this.clear_books.listPayments, { - paymentMethod: this.paymentMethodFilter, - invoiceReference: this.invoiceReferenceFilter, - }); - const recentPayments = payments.slice(-50).reverse(); - for (const payment of recentPayments) { - this.$emit(payment, { - id: payment.id || payment.ts || Date.now(), - summary: `New payment recorded: $${payment.amount}`, - ts: payment.date - ? Date.parse(payment.date) - : Date.now(), - }); - } - }, - }, - async run(event) { - const payment = event; - if ( - (this.paymentMethodFilter && payment.paymentMethod !== this.paymentMethodFilter) || - (this.invoiceReferenceFilter && payment.invoiceReference !== this.invoiceReferenceFilter) - ) { - this.http.respond({ - status: 200, - body: "OK", - }); - return; - } - this.$emit(payment, { - id: payment.id || payment.ts || Date.now(), - summary: `New payment recorded: $${payment.amount}`, - ts: payment.date - ? Date.parse(payment.date) - : Date.now(), - }); - this.http.respond({ - status: 200, - body: "OK", - }); - }, -}; diff --git a/components/clear_books/sources/new-purchase-document-created/new-purchase-document-created.mjs b/components/clear_books/sources/new-purchase-document-created/new-purchase-document-created.mjs new file mode 100644 index 0000000000000..330f1c8b6b3a7 --- /dev/null +++ b/components/clear_books/sources/new-purchase-document-created/new-purchase-document-created.mjs @@ -0,0 +1,38 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "clear_books-new-purchase-document-created", + name: "New Purchase Document Created", + description: "Emit new event when a new purchase document is created.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + purchaseType: { + propDefinition: [ + common.props.clearBooks, + "purchaseType", + ], + }, + }, + methods: { + ...common.methods, + getFn() { + return this.clearBooks.listPurchaseDocuments; + }, + getArgs() { + return { + type: this.purchaseType, + }; + }, + generateMeta(purchaseDocument) { + return { + id: purchaseDocument.id, + summary: `New Purchase Document with ID: ${purchaseDocument.id}`, + ts: Date.now(), + }; + }, + }, +}; diff --git a/components/clear_books/sources/new-transaction-created/new-transaction-created.mjs b/components/clear_books/sources/new-transaction-created/new-transaction-created.mjs new file mode 100644 index 0000000000000..3cdaaf0f73dd6 --- /dev/null +++ b/components/clear_books/sources/new-transaction-created/new-transaction-created.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "clear_books-new-transaction-created", + name: "New Transaction Created", + description: "Emit new event when a new transaction is created.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFn() { + return this.clearBooks.listTransactions; + }, + generateMeta(transaction) { + return { + id: transaction.id, + summary: `New Transaction with ID: ${transaction.id}`, + ts: Date.now(), + }; + }, + }, +}; From a72e5b3797bf5826389e56181eb28335dae86a69 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Wed, 15 Jan 2025 13:17:06 -0500 Subject: [PATCH 3/6] updates --- .../create-purchase-document/create-purchase-document.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs index e706a9a60fad7..33382c3358dc7 100644 --- a/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs +++ b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs @@ -63,9 +63,9 @@ export default { const lineItems = []; for (let i = 1; i <= this.numLineItems; i++) { lineItems.push({ - unitPrice: this[`line_items_${i}_unit_price`], - quantity: this[`line_items_${i}_quantity`], - description: this[`line_items_${i}_description`], + unitPrice: this[`line_item_${i}_unit_price`], + quantity: this[`line_item_${i}_quantity`], + description: this[`line_item_${i}_description`], }); } From 8647dec191543b40dc085c4273bfb6e090e53949 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Wed, 15 Jan 2025 13:18:48 -0500 Subject: [PATCH 4/6] pnpm-lock.yaml --- pnpm-lock.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad0d4660bbcf1..b8af9c01cd575 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1849,7 +1849,11 @@ importers: components/clarify: {} - components/clear_books: {} + components/clear_books: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/clearbit: dependencies: From 6366449ead79fa9b6ad68db92a26d5e6781808ed Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Wed, 15 Jan 2025 13:22:38 -0500 Subject: [PATCH 5/6] pnpm-lock.yaml --- pnpm-lock.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1dc61a7b51d4a..65e3c3dfe9279 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9596,8 +9596,7 @@ importers: components/showpad: {} - components/shutterstock: - specifiers: {} + components/shutterstock: {} components/sidetracker: {} @@ -31017,6 +31016,8 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) + transitivePeerDependencies: + - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: From 597a6d58325343d97633ada6da861e4ec8b3e633 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Thu, 16 Jan 2025 11:19:27 -0500 Subject: [PATCH 6/6] add lineItemsJson prop --- .../create-purchase-document.mjs | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs index 33382c3358dc7..8c742b374415c 100644 --- a/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs +++ b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs @@ -1,4 +1,5 @@ import clearBooks from "../../clear_books.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; export default { key: "clear_books-create-purchase-document", @@ -34,9 +35,16 @@ export default { numLineItems: { type: "integer", label: "Number of Line Items", - description: "The number of line items to create", + description: "The number of line items. Use this to manually enter Unit Price, Quantity, and Description of each line item.", + optional: true, reloadProps: true, }, + lineItemsJson: { + type: "string", + label: "Line Items JSON", + description: "JSON value containing an array of Line Items. For example: `[{\"description\":\"Line Item 1 Description\",\"unitPrice\":1022,\"quantity\":1,\"accountCode\":\"2001001\"},{\"description\":\"Line Item 2 Description\",\"unitPrice\":1023,\"quantity\":2,\"accountCode\":\"2001001\"}]`. [See documentation](https://u.pcloud.link/publink/show?code=XZkThJ5Z4zKewgCL6VBpfxlPeHPDdXXj0Cc7)", + optional: true, + }, }, additionalProps() { const props = {}; @@ -59,14 +67,45 @@ export default { } return props; }, + methods: { + buildLineItems() { + const lineItems = []; + for (let i = 1; i <= this.numLineItems; i++) { + lineItems.push({ + unitPrice: this[`line_item_${i}_unit_price`], + quantity: this[`line_item_${i}_quantity`], + description: this[`line_item_${i}_description`], + }); + } + return lineItems; + }, + parseLineItemsJson() { + try { + return Array.isArray(this.lineItemsJson) + ? this.lineItemsJson.map((item) => typeof item === "string" + ? JSON.parse(item) + : item) + : typeof this.lineItemsJson === "string" + ? JSON.parse(this.lineItemsJson) + : this.lineItemsJson; + } catch { + throw new ConfigurationError("Could not parse Line Items JSON"); + } + }, + }, async run({ $ }) { + if (!this.numLineItems && !this.lineItemsJson) { + throw new ConfigurationError("Please enter at least one line item"); + } + const lineItems = []; - for (let i = 1; i <= this.numLineItems; i++) { - lineItems.push({ - unitPrice: this[`line_item_${i}_unit_price`], - quantity: this[`line_item_${i}_quantity`], - description: this[`line_item_${i}_description`], - }); + if (this.numLineItems) { + const lineItemsManual = this.buildLineItems(); + lineItems.push(...lineItemsManual); + } + if (this.lineItemsJson) { + const lineItemsJson = this.parseLineItemsJson(); + lineItems.push(...lineItemsJson); } const response = await this.clearBooks.createPurchaseDocument({