Skip to content

Commit 74e6e2f

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
feat(quickbooks): add master data and CRUD tools
1 parent 6df18b0 commit 74e6e2f

9 files changed

Lines changed: 1684 additions & 9 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { filterUndefined } from '@sim/utils/object'
2+
import { QUICKBOOKS_MAX_RESPONSE_BYTES } from '@/tools/quickbooks/client'
3+
import { ErrorExtractorId } from '@/tools/error-extractors'
4+
import type {
5+
QuickBooksCreateCustomerParams,
6+
QuickBooksCustomer,
7+
QuickBooksMutationResponse,
8+
} from '@/tools/quickbooks/types'
9+
import {
10+
QUICKBOOKS_CUSTOMER_PROPERTIES,
11+
QUICKBOOKS_MUTATION_OUTPUTS,
12+
} from '@/tools/quickbooks/types'
13+
import {
14+
buildQuickBooksEntityUrl,
15+
getQuickBooksToolHeaders,
16+
optionalQuickBooksString,
17+
quickBooksEmailAddress,
18+
quickBooksPhoneNumber,
19+
requiredQuickBooksString,
20+
transformQuickBooksMutationResponse,
21+
} from '@/tools/quickbooks/utils'
22+
import type { ToolConfig } from '@/tools/types'
23+
24+
export const quickbooksCreateCustomerTool: ToolConfig<
25+
QuickBooksCreateCustomerParams,
26+
QuickBooksMutationResponse<QuickBooksCustomer>
27+
> = {
28+
id: 'quickbooks_create_customer',
29+
name: 'QuickBooks Create Customer',
30+
description: 'Create a customer in the connected QuickBooks Online company',
31+
version: '1.0.0',
32+
params: {
33+
accessToken: {
34+
type: 'string',
35+
required: true,
36+
visibility: 'hidden',
37+
description: 'QuickBooks OAuth access token',
38+
},
39+
realmId: {
40+
type: 'string',
41+
required: true,
42+
visibility: 'hidden',
43+
description: 'QuickBooks company ID derived from the connected credential',
44+
},
45+
displayName: {
46+
type: 'string',
47+
required: true,
48+
visibility: 'user-or-llm',
49+
description: 'Unique customer display name',
50+
},
51+
companyName: {
52+
type: 'string',
53+
required: false,
54+
visibility: 'user-or-llm',
55+
description: 'Customer company name',
56+
},
57+
givenName: {
58+
type: 'string',
59+
required: false,
60+
visibility: 'user-or-llm',
61+
description: 'Customer given name',
62+
},
63+
familyName: {
64+
type: 'string',
65+
required: false,
66+
visibility: 'user-or-llm',
67+
description: 'Customer family name',
68+
},
69+
primaryEmail: {
70+
type: 'string',
71+
required: false,
72+
visibility: 'user-or-llm',
73+
description: 'Customer primary email address',
74+
},
75+
primaryPhone: {
76+
type: 'string',
77+
required: false,
78+
visibility: 'user-or-llm',
79+
description: 'Customer primary phone number',
80+
},
81+
billingAddress: {
82+
type: 'json',
83+
required: false,
84+
visibility: 'user-or-llm',
85+
description: 'Customer billing address',
86+
},
87+
shippingAddress: {
88+
type: 'json',
89+
required: false,
90+
visibility: 'user-or-llm',
91+
description: 'Customer shipping address',
92+
},
93+
taxable: {
94+
type: 'boolean',
95+
required: false,
96+
visibility: 'user-or-llm',
97+
description: 'Whether sales to this customer are taxable',
98+
},
99+
},
100+
oauth: {
101+
required: true,
102+
provider: 'quickbooks',
103+
requiredScopes: ['com.intuit.quickbooks.accounting'],
104+
},
105+
errorExtractor: ErrorExtractorId.QUICKBOOKS_FAULT,
106+
request: {
107+
url: (params) => buildQuickBooksEntityUrl(params.realmId, 'customer').toString(),
108+
method: 'POST',
109+
headers: (params) => getQuickBooksToolHeaders(params.accessToken, 'application/json'),
110+
body: (params) =>
111+
filterUndefined({
112+
DisplayName: requiredQuickBooksString(params.displayName, 'displayName'),
113+
CompanyName: optionalQuickBooksString(params.companyName),
114+
GivenName: optionalQuickBooksString(params.givenName),
115+
FamilyName: optionalQuickBooksString(params.familyName),
116+
PrimaryEmailAddr: quickBooksEmailAddress(params.primaryEmail),
117+
PrimaryPhone: quickBooksPhoneNumber(params.primaryPhone),
118+
BillAddr: params.billingAddress,
119+
ShipAddr: params.shippingAddress,
120+
Taxable: params.taxable,
121+
}),
122+
retry: { enabled: false },
123+
maxResponseBytes: QUICKBOOKS_MAX_RESPONSE_BYTES,
124+
},
125+
transformResponse: (response) =>
126+
transformQuickBooksMutationResponse<QuickBooksCustomer>(response, 'Customer'),
127+
outputs: {
128+
record: {
129+
type: 'json',
130+
description: 'Created QuickBooks Customer record',
131+
properties: QUICKBOOKS_CUSTOMER_PROPERTIES,
132+
},
133+
...QUICKBOOKS_MUTATION_OUTPUTS,
134+
},
135+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { filterUndefined } from '@sim/utils/object'
2+
import { QUICKBOOKS_MAX_RESPONSE_BYTES } from '@/tools/quickbooks/client'
3+
import { ErrorExtractorId } from '@/tools/error-extractors'
4+
import type {
5+
QuickBooksCreateItemParams,
6+
QuickBooksItem,
7+
QuickBooksMutationResponse,
8+
} from '@/tools/quickbooks/types'
9+
import { QUICKBOOKS_ITEM_PROPERTIES, QUICKBOOKS_MUTATION_OUTPUTS } from '@/tools/quickbooks/types'
10+
import {
11+
buildQuickBooksEntityUrl,
12+
getQuickBooksToolHeaders,
13+
optionalQuickBooksString,
14+
quickBooksReference,
15+
requiredQuickBooksString,
16+
transformQuickBooksMutationResponse,
17+
validateQuickBooksOptionalNumber,
18+
} from '@/tools/quickbooks/utils'
19+
import type { ToolConfig } from '@/tools/types'
20+
21+
const QUICKBOOKS_ITEM_TYPES = {
22+
service: 'Service',
23+
non_inventory: 'NonInventory',
24+
} as const
25+
26+
export const quickbooksCreateItemTool: ToolConfig<
27+
QuickBooksCreateItemParams,
28+
QuickBooksMutationResponse<QuickBooksItem>
29+
> = {
30+
id: 'quickbooks_create_item',
31+
name: 'QuickBooks Create Item',
32+
description: 'Create a Service or Non-inventory item in QuickBooks Online',
33+
version: '1.0.0',
34+
params: {
35+
accessToken: {
36+
type: 'string',
37+
required: true,
38+
visibility: 'hidden',
39+
description: 'QuickBooks OAuth access token',
40+
},
41+
realmId: {
42+
type: 'string',
43+
required: true,
44+
visibility: 'hidden',
45+
description: 'QuickBooks company ID derived from the connected credential',
46+
},
47+
name: {
48+
type: 'string',
49+
required: true,
50+
visibility: 'user-or-llm',
51+
description: 'Unique item name',
52+
},
53+
itemType: {
54+
type: 'string',
55+
required: true,
56+
visibility: 'user-or-llm',
57+
description: 'Writable item type: service or non_inventory',
58+
},
59+
incomeAccountId: {
60+
type: 'string',
61+
required: true,
62+
visibility: 'user-or-llm',
63+
description: 'Income account ID used when the item is sold',
64+
},
65+
description: {
66+
type: 'string',
67+
required: false,
68+
visibility: 'user-or-llm',
69+
description: 'Sales description',
70+
},
71+
unitPrice: {
72+
type: 'number',
73+
required: false,
74+
visibility: 'user-or-llm',
75+
description: 'Sales price per unit',
76+
},
77+
purchaseDescription: {
78+
type: 'string',
79+
required: false,
80+
visibility: 'user-or-llm',
81+
description: 'Purchase description',
82+
},
83+
purchaseCost: {
84+
type: 'number',
85+
required: false,
86+
visibility: 'user-or-llm',
87+
description: 'Purchase cost per unit',
88+
},
89+
expenseAccountId: {
90+
type: 'string',
91+
required: false,
92+
visibility: 'user-or-llm',
93+
description: 'Expense account ID used when the item is purchased',
94+
},
95+
taxable: {
96+
type: 'boolean',
97+
required: false,
98+
visibility: 'user-or-llm',
99+
description: 'Whether the item is taxable',
100+
},
101+
},
102+
oauth: {
103+
required: true,
104+
provider: 'quickbooks',
105+
requiredScopes: ['com.intuit.quickbooks.accounting'],
106+
},
107+
errorExtractor: ErrorExtractorId.QUICKBOOKS_FAULT,
108+
request: {
109+
url: (params) => buildQuickBooksEntityUrl(params.realmId, 'item').toString(),
110+
method: 'POST',
111+
headers: (params) => getQuickBooksToolHeaders(params.accessToken, 'application/json'),
112+
body: (params) => {
113+
const type = QUICKBOOKS_ITEM_TYPES[params.itemType]
114+
if (!type) throw new Error(`Unsupported writable QuickBooks item type: ${params.itemType}`)
115+
const purchaseDescription = optionalQuickBooksString(params.purchaseDescription)
116+
const purchaseCost = validateQuickBooksOptionalNumber(params.purchaseCost, 'purchaseCost')
117+
if (
118+
(purchaseDescription !== undefined || purchaseCost !== undefined) &&
119+
!params.expenseAccountId?.trim()
120+
) {
121+
throw new Error('expenseAccountId is required when purchase fields are supplied')
122+
}
123+
return filterUndefined({
124+
Name: requiredQuickBooksString(params.name, 'name'),
125+
Type: type,
126+
IncomeAccountRef: quickBooksReference(params.incomeAccountId, 'incomeAccountId'),
127+
Description: optionalQuickBooksString(params.description),
128+
UnitPrice: validateQuickBooksOptionalNumber(params.unitPrice, 'unitPrice'),
129+
PurchaseDesc: purchaseDescription,
130+
PurchaseCost: purchaseCost,
131+
ExpenseAccountRef: params.expenseAccountId
132+
? quickBooksReference(params.expenseAccountId, 'expenseAccountId')
133+
: undefined,
134+
Taxable: params.taxable,
135+
})
136+
},
137+
retry: { enabled: false },
138+
maxResponseBytes: QUICKBOOKS_MAX_RESPONSE_BYTES,
139+
},
140+
transformResponse: (response) =>
141+
transformQuickBooksMutationResponse<QuickBooksItem>(response, 'Item'),
142+
outputs: {
143+
record: {
144+
type: 'json',
145+
description: 'Created QuickBooks Item record',
146+
properties: QUICKBOOKS_ITEM_PROPERTIES,
147+
},
148+
...QUICKBOOKS_MUTATION_OUTPUTS,
149+
},
150+
}

0 commit comments

Comments
 (0)