Skip to content

Commit 31728f6

Browse files
waleedlatif1claude
andcommitted
fix(quickbooks): declare the bill payment and purchase order fields the contracts dropped
A contract body is a Zod object, so any key it does not declare is stripped before the provider operation runs - silently, with no validation error. The contracts were authored before currencyCode/apAccountId/documentNumber were added to Create Bill Payment and dueDate to Update Purchase Order, so those params were dead: the block forwarded them and they never reached Intuit. Adds a parity test across all twelve contract-bound operations so a param added to a tool without its contract fails instead of silently disappearing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RB5M2gk7zZgGomnQqr7WRB
1 parent da307ae commit 31728f6

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

apps/sim/lib/api/contracts/tools/quickbooks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ export const quickBooksCreateBillPaymentBodySchema = quickBooksAuthSchema.extend
310310
paymentAccountId: requiredQuickBooksId('paymentAccountId'),
311311
billAllocations: quickBooksBillAllocationsSchema.optional(),
312312
transactionDate: optionalQuickBooksDate('transactionDate'),
313+
apAccountId: optionalQuickBooksId('apAccountId'),
314+
currencyCode: optionalQuickBooksText('currencyCode', 8),
315+
documentNumber: optionalQuickBooksText('documentNumber', 256),
313316
privateNote: optionalQuickBooksText('privateNote', 4000),
314317
requestId: optionalQuickBooksText('requestId', 256),
315318
})
@@ -407,6 +410,7 @@ export const quickBooksUpdatePurchaseOrderBodySchema = quickBooksAuthSchema.exte
407410
vendorId: optionalQuickBooksId('vendorId'),
408411
apAccountId: optionalQuickBooksId('apAccountId'),
409412
transactionDate: optionalQuickBooksDate('transactionDate'),
413+
dueDate: optionalQuickBooksDate('dueDate'),
410414
documentNumber: optionalQuickBooksText('documentNumber', 256),
411415
privateNote: optionalQuickBooksText('privateNote', 4000),
412416
})
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { describe, expect, it } from 'vitest'
5+
import {
6+
quickBooksCreateBillPaymentContract,
7+
quickBooksUpdateBillContract,
8+
quickBooksUpdateBillPaymentContract,
9+
quickBooksUpdateCreditMemoContract,
10+
quickBooksUpdateCustomerPaymentContract,
11+
quickBooksUpdateEmployeeContract,
12+
quickBooksUpdateItemContract,
13+
quickBooksUpdatePurchaseContract,
14+
quickBooksUpdatePurchaseOrderContract,
15+
quickBooksUpdateRefundReceiptContract,
16+
quickBooksUpdateVendorContract,
17+
quickBooksUpdateVendorCreditContract,
18+
} from '@/lib/api/contracts/tools/quickbooks'
19+
import { quickbooksCreateBillPaymentTool } from '@/tools/quickbooks/create_bill_payment'
20+
import { quickbooksUpdateBillTool } from '@/tools/quickbooks/update_bill'
21+
import { quickbooksUpdateBillPaymentTool } from '@/tools/quickbooks/update_bill_payment'
22+
import { quickbooksUpdateCreditMemoTool } from '@/tools/quickbooks/update_credit_memo'
23+
import { quickbooksUpdateCustomerPaymentTool } from '@/tools/quickbooks/update_customer_payment'
24+
import { quickbooksUpdateEmployeeTool } from '@/tools/quickbooks/update_employee'
25+
import { quickbooksUpdateItemTool } from '@/tools/quickbooks/update_item'
26+
import { quickbooksUpdatePurchaseTool } from '@/tools/quickbooks/update_purchase'
27+
import { quickbooksUpdatePurchaseOrderTool } from '@/tools/quickbooks/update_purchase_order'
28+
import { quickbooksUpdateRefundReceiptTool } from '@/tools/quickbooks/update_refund_receipt'
29+
import { quickbooksUpdateVendorTool } from '@/tools/quickbooks/update_vendor'
30+
import { quickbooksUpdateVendorCreditTool } from '@/tools/quickbooks/update_vendor_credit'
31+
32+
/**
33+
* A contract body is a Zod object, so any key it does not declare is STRIPPED
34+
* before the provider operation runs — silently, with no validation error. A
35+
* tool param that the contract omits is therefore dead: the user fills it in,
36+
* the block forwards it, and it never reaches Intuit.
37+
*/
38+
const CONTRACT_BOUND_OPERATIONS = [
39+
['create_bill_payment', quickbooksCreateBillPaymentTool, quickBooksCreateBillPaymentContract],
40+
['update_bill', quickbooksUpdateBillTool, quickBooksUpdateBillContract],
41+
['update_bill_payment', quickbooksUpdateBillPaymentTool, quickBooksUpdateBillPaymentContract],
42+
['update_credit_memo', quickbooksUpdateCreditMemoTool, quickBooksUpdateCreditMemoContract],
43+
[
44+
'update_customer_payment',
45+
quickbooksUpdateCustomerPaymentTool,
46+
quickBooksUpdateCustomerPaymentContract,
47+
],
48+
['update_employee', quickbooksUpdateEmployeeTool, quickBooksUpdateEmployeeContract],
49+
['update_item', quickbooksUpdateItemTool, quickBooksUpdateItemContract],
50+
['update_purchase', quickbooksUpdatePurchaseTool, quickBooksUpdatePurchaseContract],
51+
[
52+
'update_purchase_order',
53+
quickbooksUpdatePurchaseOrderTool,
54+
quickBooksUpdatePurchaseOrderContract,
55+
],
56+
[
57+
'update_refund_receipt',
58+
quickbooksUpdateRefundReceiptTool,
59+
quickBooksUpdateRefundReceiptContract,
60+
],
61+
['update_vendor', quickbooksUpdateVendorTool, quickBooksUpdateVendorContract],
62+
['update_vendor_credit', quickbooksUpdateVendorCreditTool, quickBooksUpdateVendorCreditContract],
63+
] as const
64+
65+
describe('QuickBooks contract/tool param parity', () => {
66+
it.each(CONTRACT_BOUND_OPERATIONS)(
67+
'%s declares every tool param in its contract body',
68+
(_name, tool, contract) => {
69+
const bodyShape = (contract.body as unknown as { shape: Record<string, unknown> }).shape
70+
const declared = new Set(Object.keys(bodyShape))
71+
const dropped = Object.keys(tool.params).filter((param) => !declared.has(param))
72+
expect(dropped).toEqual([])
73+
}
74+
)
75+
})

0 commit comments

Comments
 (0)