diff --git a/components/bluesnap/bluesnap.app.mjs b/components/bluesnap/bluesnap.app.mjs index 2dcc3eb0bae52..67f5cf5ac3f7a 100644 --- a/components/bluesnap/bluesnap.app.mjs +++ b/components/bluesnap/bluesnap.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; 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-purchase-document/create-purchase-document.mjs b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs new file mode 100644 index 0000000000000..8c742b374415c --- /dev/null +++ b/components/clear_books/actions/create-purchase-document/create-purchase-document.mjs @@ -0,0 +1,124 @@ +import clearBooks from "../../clear_books.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +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. 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 = {}; + 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; + }, + 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 = []; + 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({ + $, + 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/clear_books.app.mjs b/components/clear_books/clear_books.app.mjs index e10a5b47a6bf8..e183d7ad9e886 100644 --- a/components/clear_books/clear_books.app.mjs +++ b/components/clear_books/clear_books.app.mjs @@ -1,11 +1,132 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "clear_books", - propDefinitions: {}, + propDefinitions: { + supplierId: { + type: "string", + 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: "Purchase Type", + description: "Type of purchase document", + options: [ + "bills", + "creditNotes", + ], + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.clearbooks.co.uk/v1"; + }, + _makeRequest({ + $ = this, + path, + ...otherOpts + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + ...otherOpts, + }); + }, + listCustomers(opts = {}) { + return this._makeRequest({ + path: "/accounting/customers", + ...opts, + }); + }, + listPurchaseDocuments({ + type, ...opts + }) { + return this._makeRequest({ + path: `/accounting/purchases/${type}`, + ...opts, + }); + }, + listTransactions(opts = {}) { + return this._makeRequest({ + path: "/accounting/transactions", + ...opts, + }); + }, + listSuppliers(opts = {}) { + return this._makeRequest({ + path: "/accounting/suppliers", + ...opts, + }); + }, + createCustomer(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/accounting/customers", + ...opts, + }); + }, + createPurchaseDocument({ + type, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/accounting/purchases/${type}`, + ...opts, + }); + }, + createSupplier(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/accounting/suppliers", + ...opts, + }); + }, + 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}`); + } }, }, }; diff --git a/components/clear_books/package.json b/components/clear_books/package.json index d8a028a1515cb..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" } -} \ No newline at end of file +} 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-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-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(), + }; + }, + }, +}; diff --git a/components/grafana/grafana.app.mjs b/components/grafana/grafana.app.mjs index 62ab563c11f68..2f53cbc58cecb 100644 --- a/components/grafana/grafana.app.mjs +++ b/components/grafana/grafana.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/polymer_co/polymer_co.app.mjs b/components/polymer_co/polymer_co.app.mjs index 7f014c7a7a7ec..619862a459949 100644 --- a/components/polymer_co/polymer_co.app.mjs +++ b/components/polymer_co/polymer_co.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/shutterstock/shutterstock.app.mjs b/components/shutterstock/shutterstock.app.mjs index cc72c74b01671..b5f44e95591fb 100644 --- a/components/shutterstock/shutterstock.app.mjs +++ b/components/shutterstock/shutterstock.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca391384a140c..65e3c3dfe9279 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1858,7 +1858,11 @@ importers: components/clarify: {} - components/clear_books: {} + components/clear_books: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/clearbit: dependencies: @@ -9592,8 +9596,7 @@ importers: components/showpad: {} - components/shutterstock: - specifiers: {} + components/shutterstock: {} components/sidetracker: {} @@ -31013,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: