-
Notifications
You must be signed in to change notification settings - Fork 5.5k
QuickBooks Webhook Sources #1564
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
6b21e25
831711f
a425096
e5d06a2
d0b29bf
ea99e93
8fb858b
91228bd
11b6af2
5f89600
004c390
64de7a9
c6ed1cd
70cad06
21dbb22
653ec09
1029b6f
3e8506d
101fc1b
9939441
7e67c78
6f0d455
d49208e
d0e8509
c776699
36d30db
215cfaa
af5eb91
15bde79
74ab122
70469e5
6693df1
4c5abf3
ae711d9
fd3cc0d
8baf73c
c5aa467
f60b3e9
f969c4d
5303b4d
2e7c48c
b36d12e
6f3d9a9
aeb31be
9e17d3f
04cdc8c
1f7a854
50ac040
aa4b865
c730c06
de37b8a
551986b
a0a854f
4c80a38
519de09
4bfe358
ef67ada
20264e5
b87879d
fa90d15
ce53add
b600480
9afe2b5
337a9af
b4df4fb
042f2ef
b446a51
2caf1eb
cc71cbe
f05e631
c958d5f
42dd4b5
084b77f
3a525c5
f649138
2c0ed0d
79b0f8d
d76f8d4
91e0f0d
4c71d74
7607d0d
7023986
7f3a2ab
6ce4036
9d84607
dc15a1e
5cc57d4
d752bda
60856e1
6e70342
0caf494
2225f9c
245ea12
27b61b8
a59b99b
7c3c689
4a2994c
7931dd8
9539593
238ea28
e63bb37
5861368
5c54656
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| const quickbooks = require("../../quickbooks.app"); | ||
| const fs = require("fs"); | ||
| const { promisify } = require("util"); | ||
| const stream = require("stream"); | ||
|
|
||
| module.exports = { | ||
| name: "Download PDF", | ||
| description: "Download an invoice, bill, purchase order or other QuickBooks entity as a PDF and save it in [Pipedream's temporary file system](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory) for use in a later step.", | ||
| key: "quickbooks-download-pdf", | ||
| version: "0.0.1", | ||
| type: "action", | ||
| props: { | ||
| quickbooks, | ||
| entity: { | ||
| type: "string", | ||
| label: "Document Type", | ||
| description: null, | ||
| options: [ | ||
| "CreditMemo", | ||
| "Estimate", | ||
| "Invoice", | ||
| "Payment", | ||
| "PurchaseOrder", | ||
| "RefundReceipt", | ||
| "SalesReceipt", | ||
| ], | ||
| }, | ||
| id: { | ||
| type: "string", | ||
| label: "Record ID", | ||
| description: "Use the 'Id' property of the record's JSON object or open the record for editing in Quickbooks Online to find its ID in the URL after 'txnId=', e.g. 'https://app.qbo.intuit.com/app/invoice?txnId=22743'", | ||
| }, | ||
| fileName: { | ||
| type: "string", | ||
| label: "File Name (Optional)", | ||
| description: "The name to give the PDF when it is stored in the /tmp directory, e.g. `myFile.pdf`. If no file name is provided, the PDF will be named using the record ID, e.g. '22743.pdf'", | ||
| optional: true, | ||
| }, | ||
| }, | ||
| methods: { | ||
| async downloadPDF($, entity, id, fileName) { | ||
| const file = await this.quickbooks.getPDF($, entity, id); | ||
|
|
||
| const filePath = "/tmp/" + fileName; | ||
| const pipeline = promisify(stream.pipeline); | ||
| await pipeline( | ||
| file, | ||
| fs.createWriteStream(filePath), | ||
| ); | ||
| return filePath; | ||
| }, | ||
| }, | ||
| async run({ $ }) { | ||
| const fileName = this.fileName || this.id; | ||
| const fileNameWithExtension = fileName.endsWith(".pdf") | ||
| ? fileName | ||
| : fileName + ".pdf"; | ||
|
|
||
| const filePath = await this.downloadPDF($, this.entity, this.id, fileNameWithExtension); | ||
| $.export("file_path", filePath); | ||
| $.export("file_name", fileNameWithExtension); | ||
| $.export("$summary", `Successfully downloaded file: ${fileNameWithExtension}`); | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| const WEBHOOK_ENTITIES = [ | ||
| "Account", | ||
| "Bill", | ||
| "BillPayment", | ||
| "Budget", | ||
| "Class", | ||
| "CreditMemo", | ||
| "Currency", | ||
| "Customer", | ||
| "Department", | ||
| "Deposit", | ||
| "Employee", | ||
| "Estimate", | ||
| "Invoice", | ||
| "Item", | ||
| "JournalCode", | ||
| "JournalEntry", | ||
| "Payment", | ||
| "PaymentMethod", | ||
| "Preferences", | ||
| "Purchase", | ||
| "PurchaseOrder", | ||
| "RefundReceipt", | ||
| "SalesReceipt", | ||
| "TaxAgency", | ||
| "Term", | ||
| "TimeActivity", | ||
| "Transfer", | ||
| "Vendor", | ||
| "VendorCredit", | ||
| ]; | ||
|
|
||
| const WEBHOOK_OPERATIONS = [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| "Delete", | ||
| "Void", | ||
| "Emailed", | ||
| ]; | ||
|
|
||
| // The below list is based on the table shown in Step 3 of the Intuit webhooks documentation | ||
| // https://developer.intuit.com/app/developer/qbo/docs/develop/webhooks#set-up-oauth | ||
| const SUPPORTED_WEBHOOK_OPERATIONS = { | ||
| Account: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| "Delete", | ||
| ], | ||
| Bill: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| ], | ||
| BillPayment: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| ], | ||
| Budget: [ | ||
| "Create", | ||
| "Update", | ||
| ], | ||
| Class: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| "Delete", | ||
| ], | ||
| CreditMemo: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| "Emailed", | ||
| ], | ||
| Currency: [ | ||
| "Create", | ||
| "Update", | ||
| ], | ||
| Customer: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| "Delete", | ||
| ], | ||
| Department: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| ], | ||
| Deposit: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| ], | ||
| Employee: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| "Delete", | ||
| ], | ||
| Estimate: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Emailed", | ||
| ], | ||
| Invoice: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| "Emailed", | ||
| ], | ||
| Item: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| "Delete", | ||
| ], | ||
| JournalCode: [ | ||
| "Create", | ||
| "Update", | ||
| ], | ||
| JournalEntry: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| ], | ||
| Payment: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| "Emailed", | ||
| ], | ||
| PaymentMethod: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| ], | ||
| Preferences: [ | ||
| "Update", | ||
| ], | ||
| Purchase: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| ], | ||
| PurchaseOrder: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Emailed", | ||
| ], | ||
| RefundReceipt: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| "Emailed", | ||
| ], | ||
| SalesReceipt: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| "Emailed", | ||
| ], | ||
| TaxAgency: [ | ||
| "Create", | ||
| "Update", | ||
| ], | ||
| Term: [ | ||
| "Create", | ||
| "Update", | ||
| ], | ||
| TimeActivity: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| ], | ||
| Transfer: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| "Void", | ||
| ], | ||
| Vendor: [ | ||
| "Create", | ||
| "Update", | ||
| "Merge", | ||
| "Delete", | ||
| ], | ||
| VendorCredit: [ | ||
| "Create", | ||
| "Update", | ||
| "Delete", | ||
| ], | ||
| }; | ||
|
|
||
| module.exports = { | ||
| WEBHOOK_ENTITIES, | ||
| WEBHOOK_OPERATIONS, | ||
| SUPPORTED_WEBHOOK_OPERATIONS, | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,97 @@ | ||||||||||||||||||||||||||||||
| const { axios } = require("@pipedream/platform"); | ||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||
| WEBHOOK_ENTITIES, | ||||||||||||||||||||||||||||||
| WEBHOOK_OPERATIONS, | ||||||||||||||||||||||||||||||
| } = require("./constants"); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| module.exports = { | ||||||||||||||||||||||||||||||
| type: "app", | ||||||||||||||||||||||||||||||
| app: "quickbooks", | ||||||||||||||||||||||||||||||
| propDefinitions: { | ||||||||||||||||||||||||||||||
| webhookNames: { | ||||||||||||||||||||||||||||||
| type: "string[]", | ||||||||||||||||||||||||||||||
| label: "Entities", | ||||||||||||||||||||||||||||||
| description: "Select which QuickBooks entities to emit or just leave it blank to emit them all.", | ||||||||||||||||||||||||||||||
| options: WEBHOOK_ENTITIES, | ||||||||||||||||||||||||||||||
| optional: true, | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| webhookOperations: { | ||||||||||||||||||||||||||||||
| type: "string[]", | ||||||||||||||||||||||||||||||
| label: "Operations", | ||||||||||||||||||||||||||||||
| description: "Select which operations to emit or just leave it blank to emit them all.", | ||||||||||||||||||||||||||||||
| options: WEBHOOK_OPERATIONS, | ||||||||||||||||||||||||||||||
| default: WEBHOOK_OPERATIONS, | ||||||||||||||||||||||||||||||
| optional: true, | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| webhookVerifierToken: { | ||||||||||||||||||||||||||||||
| type: "string", | ||||||||||||||||||||||||||||||
| label: "Verifier Token", | ||||||||||||||||||||||||||||||
| description: "[Create an app](https://developer.intuit.com/app/developer/qbo/docs/build-your-first-app) " + | ||||||||||||||||||||||||||||||
| "on the Intuit Developer Dashboard and [set up a webhook](https://developer.intuit.com/app/developer/qbo/docs/develop/webhooks). " + | ||||||||||||||||||||||||||||||
| "Once you have a [verifier token](https://developer.intuit.com/app/developer/qbo/docs/develop/webhooks/managing-webhooks-notifications), " + | ||||||||||||||||||||||||||||||
| "fill it in below. Note that if you want to send webhooks to more than one Pipedream source, you will have to create a new Dashboard app for each different source.", | ||||||||||||||||||||||||||||||
| secret: true, | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| methods: { | ||||||||||||||||||||||||||||||
| _apiUrl() { | ||||||||||||||||||||||||||||||
| return "https://quickbooks.api.intuit.com/v3"; | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| _authToken() { | ||||||||||||||||||||||||||||||
| return this.$auth.oauth_access_token; | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| companyId() { | ||||||||||||||||||||||||||||||
| return this.$auth.company_id; | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
|
Comment on lines
+43
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Maintain consistent method naming conventions Private methods are typically prefixed with an underscore (e.g., Apply this diff to rename the method and update its references: },
- companyId() {
+ _companyId() {
return this.$auth.company_id;
},Update references to the method: const companyId = this.companyId();
+ const companyId = this._companyId();This change applies to all instances where
Also applies to: 74-93 |
||||||||||||||||||||||||||||||
| _makeRequestConfig(config = {}) { | ||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||
| headers, | ||||||||||||||||||||||||||||||
| path = "", | ||||||||||||||||||||||||||||||
| ...extraConfig | ||||||||||||||||||||||||||||||
| } = config; | ||||||||||||||||||||||||||||||
| const authToken = this._authToken(); | ||||||||||||||||||||||||||||||
| const baseUrl = this._apiUrl(); | ||||||||||||||||||||||||||||||
| const url = `${baseUrl}${path[0] === "/" | ||||||||||||||||||||||||||||||
| ? "" | ||||||||||||||||||||||||||||||
| : "/"}${path}`; | ||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||
| "Authorization": `Bearer ${authToken}`, | ||||||||||||||||||||||||||||||
| "Accept": "application/json", | ||||||||||||||||||||||||||||||
| "Content-Type": "application/json", | ||||||||||||||||||||||||||||||
| ...headers, | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| url, | ||||||||||||||||||||||||||||||
| ...extraConfig, | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| async _makeRequest($ = this, config) { | ||||||||||||||||||||||||||||||
| const requestConfig = this._makeRequestConfig(config); | ||||||||||||||||||||||||||||||
| return await axios($, requestConfig); | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| async getPDF($, entity, id) { | ||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||
| const companyId = this.companyId(); | ||||||||||||||||||||||||||||||
| return await this._makeRequest($, { | ||||||||||||||||||||||||||||||
| path: `company/${companyId}/${entity.toLowerCase()}/${id}/pdf`, | ||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||
| "Accept": "application/pdf", | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| responseType: "stream", | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| } catch (ex) { | ||||||||||||||||||||||||||||||
| if (ex.response.data.statusCode === 400) { | ||||||||||||||||||||||||||||||
| throw new Error(`Request failed with status code 400. Double-check that '${id}' is a valid ${entity} record ID.`); | ||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||
| throw ex; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
+82
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enhance error handling in the 'getPDF' method In the Apply this diff to improve error handling: } catch (ex) {
- if (ex.response.data.statusCode === 400) {
+ if (ex.response && ex.response.data && ex.response.data.statusCode === 400) {
throw new Error(`Request failed with status code 400. Double-check that '${id}' is a valid ${entity} record ID.`);
} else {
throw ex;
}
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| async getRecordDetails(entityName, id) { | ||||||||||||||||||||||||||||||
| const companyId = this.companyId(); | ||||||||||||||||||||||||||||||
| return await this._makeRequest(this, { | ||||||||||||||||||||||||||||||
| path: `company/${companyId}/${entityName.toLowerCase()}/${id}`, | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
|
Comment on lines
+90
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Ensure consistent usage of the context parameter in 'getRecordDetails' In the Apply this diff to make the method consistent: },
- async getRecordDetails(entityName, id) {
+ async getRecordDetails($, entityName, id) {
const companyId = this.companyId();
- return await this._makeRequest(this, {
+ return await this._makeRequest($, {
path: `company/${companyId}/${entityName.toLowerCase()}/${id}`,
});
},📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.