Skip to content

Commit c969b5c

Browse files
committed
feat(quickbooks): Add missing invoice, estimate, and purchase order actions - Fixes #16906
1 parent 480ec38 commit c969b5c

File tree

8 files changed

+1070
-0
lines changed

8 files changed

+1070
-0
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import { ConfigurationError } from "@pipedream/platform";
2+
import quickbooks from "../../quickbooks.app.mjs";
3+
import { parseLineItems } from "../../common/utils.mjs";
4+
5+
export default {
6+
key: "quickbooks-create-estimate",
7+
name: "Create Estimate",
8+
description: "Creates an estimate. [See the documentation](https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/estimate#create-an-estimate)",
9+
version: "0.0.1",
10+
type: "action",
11+
props: {
12+
quickbooks,
13+
customerRefValue: {
14+
propDefinition: [
15+
quickbooks,
16+
"customer",
17+
],
18+
},
19+
billEmail: {
20+
type: "string",
21+
label: "Bill Email",
22+
description: "Email address where the estimate should be sent",
23+
optional: true,
24+
},
25+
expirationDate: {
26+
type: "string",
27+
label: "Expiration Date",
28+
description: "Date when the estimate expires (YYYY-MM-DD)",
29+
optional: true,
30+
},
31+
acceptedBy: {
32+
type: "string",
33+
label: "Accepted By",
34+
description: "Name of the customer who accepted the estimate",
35+
optional: true,
36+
},
37+
acceptedDate: {
38+
type: "string",
39+
label: "Accepted Date",
40+
description: "Date when the estimate was accepted (YYYY-MM-DD)",
41+
optional: true,
42+
},
43+
currencyRefValue: {
44+
propDefinition: [
45+
quickbooks,
46+
"currency",
47+
],
48+
},
49+
docNumber: {
50+
type: "string",
51+
label: "Document Number",
52+
description: "Reference number for the transaction",
53+
optional: true,
54+
},
55+
billAddr: {
56+
type: "object",
57+
label: "Billing Address",
58+
description: "Billing address details",
59+
optional: true,
60+
},
61+
shipAddr: {
62+
type: "object",
63+
label: "Shipping Address",
64+
description: "Shipping address details",
65+
optional: true,
66+
},
67+
privateNote: {
68+
type: "string",
69+
label: "Private Note",
70+
description: "Private note for internal use",
71+
optional: true,
72+
},
73+
customerMemo: {
74+
type: "string",
75+
label: "Customer Memo",
76+
description: "Memo visible to customer",
77+
optional: true,
78+
},
79+
taxCodeId: {
80+
propDefinition: [
81+
quickbooks,
82+
"taxCodeId",
83+
],
84+
},
85+
lineItemsAsObjects: {
86+
propDefinition: [
87+
quickbooks,
88+
"lineItemsAsObjects",
89+
],
90+
reloadProps: true,
91+
},
92+
},
93+
async additionalProps() {
94+
const props = {};
95+
if (this.lineItemsAsObjects) {
96+
props.lineItems = {
97+
type: "string[]",
98+
label: "Line Items",
99+
description: "Line items of an estimate. Set DetailType to `SalesItemLineDetail`, `GroupLineDetail`, or `DescriptionOnly`. Example: `{ \"DetailType\": \"SalesItemLineDetail\", \"Amount\": 100.0, \"SalesItemLineDetail\": { \"ItemRef\": { \"name\": \"Services\", \"value\": \"1\" } } }`",
100+
};
101+
return props;
102+
}
103+
props.numLineItems = {
104+
type: "integer",
105+
label: "Number of Line Items",
106+
description: "The number of line items to enter",
107+
reloadProps: true,
108+
};
109+
if (!this.numLineItems) {
110+
return props;
111+
}
112+
for (let i = 1; i <= this.numLineItems; i++) {
113+
props[`item_${i}`] = {
114+
type: "string",
115+
label: `Line ${i} - Item ID`,
116+
options: async ({ page }) => {
117+
return this.quickbooks.getPropOptions({
118+
page,
119+
resource: "Item",
120+
mapper: ({
121+
Id: value, Name: label,
122+
}) => ({
123+
value,
124+
label,
125+
}),
126+
});
127+
},
128+
};
129+
props[`amount_${i}`] = {
130+
type: "string",
131+
label: `Line ${i} - Amount`,
132+
};
133+
}
134+
return props;
135+
},
136+
methods: {
137+
buildLineItems() {
138+
const lineItems = [];
139+
for (let i = 1; i <= this.numLineItems; i++) {
140+
lineItems.push({
141+
DetailType: "SalesItemLineDetail",
142+
Amount: this[`amount_${i}`],
143+
SalesItemLineDetail: {
144+
ItemRef: {
145+
value: this[`item_${i}`],
146+
},
147+
},
148+
});
149+
}
150+
return lineItems;
151+
},
152+
},
153+
async run({ $ }) {
154+
if ((!this.numLineItems && !this.lineItemsAsObjects) || !this.customerRefValue) {
155+
throw new ConfigurationError("Must provide lineItems and customerRefValue parameters.");
156+
}
157+
158+
const lines = this.lineItemsAsObjects
159+
? parseLineItems(this.lineItems)
160+
: this.buildLineItems();
161+
162+
lines.forEach((line) => {
163+
if (line.DetailType !== "SalesItemLineDetail" && line.DetailType !== "GroupLineDetail" && line.DetailType !== "DescriptionOnly") {
164+
throw new ConfigurationError("Line Item DetailType must be `SalesItemLineDetail`, `GroupLineDetail`, or `DescriptionOnly`");
165+
}
166+
});
167+
168+
const params = {};
169+
const data = {
170+
Line: lines,
171+
CustomerRef: {
172+
value: this.customerRefValue,
173+
},
174+
ExpirationDate: this.expirationDate,
175+
AcceptedBy: this.acceptedBy,
176+
AcceptedDate: this.acceptedDate,
177+
DocNumber: this.docNumber,
178+
BillAddr: this.billAddr,
179+
ShipAddr: this.shipAddr,
180+
PrivateNote: this.privateNote,
181+
};
182+
183+
if (this.billEmail) {
184+
params.include = "estimateLink";
185+
data.BillEmail = {
186+
Address: this.billEmail,
187+
};
188+
}
189+
if (this.currencyRefValue) {
190+
data.CurrencyRef = {
191+
value: this.currencyRefValue,
192+
};
193+
}
194+
if (this.customerMemo) {
195+
data.CustomerMemo = {
196+
value: this.customerMemo,
197+
};
198+
}
199+
200+
const response = await this.quickbooks.createEstimate({
201+
$,
202+
params,
203+
data,
204+
});
205+
206+
if (response) {
207+
$.export("summary", `Successfully created estimate with ID ${response.Estimate.Id}`);
208+
}
209+
210+
return response;
211+
},
212+
};
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { ConfigurationError } from "@pipedream/platform";
2+
import quickbooks from "../../quickbooks.app.mjs";
3+
import { parseLineItems } from "../../common/utils.mjs";
4+
5+
export default {
6+
key: "quickbooks-create-purchase-order",
7+
name: "Create Purchase Order",
8+
description: "Creates a purchase order. [See the documentation](https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/purchaseorder#create-a-purchaseorder)",
9+
version: "0.0.1",
10+
type: "action",
11+
props: {
12+
quickbooks,
13+
vendorRefValue: {
14+
propDefinition: [
15+
quickbooks,
16+
"vendor",
17+
],
18+
},
19+
dueDate: {
20+
type: "string",
21+
label: "Due Date",
22+
description: "Date when the purchase order is due (YYYY-MM-DD)",
23+
optional: true,
24+
},
25+
currencyRefValue: {
26+
propDefinition: [
27+
quickbooks,
28+
"currency",
29+
],
30+
},
31+
docNumber: {
32+
type: "string",
33+
label: "Document Number",
34+
description: "Reference number for the transaction",
35+
optional: true,
36+
},
37+
shipAddr: {
38+
type: "object",
39+
label: "Shipping Address",
40+
description: "Shipping address details",
41+
optional: true,
42+
},
43+
memo: {
44+
type: "string",
45+
label: "Memo",
46+
description: "Memo or note for the purchase order",
47+
optional: true,
48+
},
49+
lineItemsAsObjects: {
50+
propDefinition: [
51+
quickbooks,
52+
"lineItemsAsObjects",
53+
],
54+
reloadProps: true,
55+
},
56+
},
57+
async additionalProps() {
58+
const props = {};
59+
if (this.lineItemsAsObjects) {
60+
props.lineItems = {
61+
type: "string[]",
62+
label: "Line Items",
63+
description: "Line items of a purchase order. Set DetailType to `ItemBasedExpenseLineDetail` or `AccountBasedExpenseLineDetail`. Example: `{ \"DetailType\": \"ItemBasedExpenseLineDetail\", \"Amount\": 100.0, \"ItemBasedExpenseLineDetail\": { \"ItemRef\": { \"name\": \"Services\", \"value\": \"1\" } } }`",
64+
};
65+
return props;
66+
}
67+
props.numLineItems = {
68+
type: "integer",
69+
label: "Number of Line Items",
70+
description: "The number of line items to enter",
71+
reloadProps: true,
72+
};
73+
if (!this.numLineItems) {
74+
return props;
75+
}
76+
for (let i = 1; i <= this.numLineItems; i++) {
77+
props[`item_${i}`] = {
78+
type: "string",
79+
label: `Line ${i} - Item ID`,
80+
options: async ({ page }) => {
81+
return this.quickbooks.getPropOptions({
82+
page,
83+
resource: "Item",
84+
mapper: ({
85+
Id: value, Name: label,
86+
}) => ({
87+
value,
88+
label,
89+
}),
90+
});
91+
},
92+
};
93+
props[`amount_${i}`] = {
94+
type: "string",
95+
label: `Line ${i} - Amount`,
96+
};
97+
}
98+
return props;
99+
},
100+
methods: {
101+
buildLineItems() {
102+
const lineItems = [];
103+
for (let i = 1; i <= this.numLineItems; i++) {
104+
lineItems.push({
105+
DetailType: "ItemBasedExpenseLineDetail",
106+
Amount: this[`amount_${i}`],
107+
ItemBasedExpenseLineDetail: {
108+
ItemRef: {
109+
value: this[`item_${i}`],
110+
},
111+
},
112+
});
113+
}
114+
return lineItems;
115+
},
116+
},
117+
async run({ $ }) {
118+
if ((!this.numLineItems && !this.lineItemsAsObjects) || !this.vendorRefValue) {
119+
throw new ConfigurationError("Must provide lineItems and vendorRefValue parameters.");
120+
}
121+
122+
const lines = this.lineItemsAsObjects
123+
? parseLineItems(this.lineItems)
124+
: this.buildLineItems();
125+
126+
lines.forEach((line) => {
127+
if (line.DetailType !== "ItemBasedExpenseLineDetail" && line.DetailType !== "AccountBasedExpenseLineDetail") {
128+
throw new ConfigurationError("Line Item DetailType must be `ItemBasedExpenseLineDetail` or `AccountBasedExpenseLineDetail`");
129+
}
130+
});
131+
132+
const data = {
133+
Line: lines,
134+
VendorRef: {
135+
value: this.vendorRefValue,
136+
},
137+
DueDate: this.dueDate,
138+
DocNumber: this.docNumber,
139+
ShipAddr: this.shipAddr,
140+
Memo: this.memo,
141+
};
142+
143+
if (this.currencyRefValue) {
144+
data.CurrencyRef = {
145+
value: this.currencyRefValue,
146+
};
147+
}
148+
149+
const response = await this.quickbooks.createPurchaseOrder({
150+
$,
151+
data,
152+
});
153+
154+
if (response) {
155+
$.export("summary", `Successfully created purchase order with ID ${response.PurchaseOrder.Id}`);
156+
}
157+
158+
return response;
159+
},
160+
};

0 commit comments

Comments
 (0)