Skip to content

Commit 006afcb

Browse files
committed
fix(integrations): harden QuickBooks contract accuracy
1 parent 89c33f2 commit 006afcb

22 files changed

Lines changed: 730 additions & 136 deletions

apps/docs/content/docs/en/integrations/quickbooks.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ Create a customer in the connected QuickBooks Online company
215215

216216
| Parameter | Type | Required | Description |
217217
| --------- | ---- | -------- | ----------- |
218-
| `displayName` | string | Yes | Unique customer display name |
218+
| `displayName` | string | No | Unique customer display name. Required unless givenName or familyName is supplied |
219219
| `companyName` | string | No | Customer company name |
220220
| `givenName` | string | No | Customer given name |
221221
| `familyName` | string | No | Customer family name |
@@ -399,7 +399,7 @@ Create a vendor in the connected QuickBooks Online company
399399

400400
| Parameter | Type | Required | Description |
401401
| --------- | ---- | -------- | ----------- |
402-
| `displayName` | string | Yes | Unique vendor display name |
402+
| `displayName` | string | No | Unique vendor display name. Required unless givenName or familyName is supplied |
403403
| `companyName` | string | No | Vendor company name |
404404
| `givenName` | string | No | Vendor given name |
405405
| `familyName` | string | No | Vendor family name |
@@ -1160,7 +1160,7 @@ Record a customer payment with optional bounded invoice allocations
11601160

11611161
### QuickBooks Update Customer Payment
11621162

1163-
Sparse-update a customer payment using its current sync token
1163+
Read, merge, and full-update a customer payment using its current sync token
11641164

11651165
#### Input
11661166

@@ -1869,7 +1869,7 @@ Read, merge, and full-update bill header fields using its current sync token
18691869
| --------- | ---- | -------- | ----------- |
18701870
| `billId` | string | Yes | Bill ID to update |
18711871
| `syncToken` | string | Yes | Current bill sync token |
1872-
| `vendorId` | string | Yes | Current or replacement vendor ID required by QuickBooks |
1872+
| `vendorId` | string | No | Replacement vendor ID; omit to preserve the current vendor |
18731873
| `apAccountId` | string | No | Replacement accounts-payable account ID |
18741874
| `transactionDate` | string | No | Replacement bill date in YYYY-MM-DD format |
18751875
| `dueDate` | string | No | Replacement due date in YYYY-MM-DD format |
@@ -2017,7 +2017,7 @@ Read, merge, and full-update a BillPayment without changing allocations
20172017
| --------- | ---- | -------- | ----------- |
20182018
| `billPaymentId` | string | Yes | BillPayment ID to update |
20192019
| `syncToken` | string | Yes | Current BillPayment sync token |
2020-
| `vendorId` | string | Yes | Current vendor ID required by QuickBooks |
2020+
| `vendorId` | string | No | Replacement vendor ID; omit to preserve the current vendor |
20212021
| `transactionDate` | string | No | Replacement payment date in YYYY-MM-DD format |
20222022
| `privateNote` | string | No | Replacement internal note |
20232023

@@ -2161,7 +2161,7 @@ Read, merge, and full-update vendor-credit header fields
21612161
| --------- | ---- | -------- | ----------- |
21622162
| `vendorCreditId` | string | Yes | VendorCredit ID to update |
21632163
| `syncToken` | string | Yes | Current vendor-credit sync token |
2164-
| `vendorId` | string | Yes | Current or replacement vendor ID required by QuickBooks |
2164+
| `vendorId` | string | No | Replacement vendor ID; omit to preserve the current vendor |
21652165
| `apAccountId` | string | No | Replacement accounts-payable account ID |
21662166
| `transactionDate` | string | No | Replacement date in YYYY-MM-DD format |
21672167
| `documentNumber` | string | No | Replacement vendor-credit number |

apps/sim/blocks/blocks/quickbooks.ts

Lines changed: 193 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,21 @@ function optionalValue(value: unknown): unknown {
289289
return typeof value === 'string' && value.trim() === '' ? undefined : value
290290
}
291291

292+
function requiredWhenNameAlternativesAreEmpty(
293+
values: Record<string, unknown> | undefined,
294+
operations: readonly string[],
295+
alternativeFields: readonly string[]
296+
) {
297+
const operation = typeof values?.operation === 'string' ? values.operation : ''
298+
const alternativesAreEmpty = alternativeFields.every(
299+
(field) => optionalValue(values?.[field]) === undefined
300+
)
301+
return {
302+
field: 'operation',
303+
value: operations.includes(operation) && alternativesAreEmpty ? operation : [],
304+
}
305+
}
306+
292307
function paginationCondition(values?: Record<string, unknown>) {
293308
if (!values) {
294309
return { field: 'operation', value: [...PAGINATED_OPERATIONS] }
@@ -386,6 +401,157 @@ export const QuickBooksBlock: BlockConfig<QuickBooksResponse> = {
386401
integrationType: IntegrationType.Commerce,
387402
bgColor: '#2CA01C',
388403
icon: QuickBooksIcon,
404+
canvasPresentation: {
405+
defaultTitle: 'QuickBooks',
406+
sentences: {
407+
byOperation: {
408+
quickbooks_get_company_info: ['Read the connected QuickBooks company'],
409+
quickbooks_read_master_data: [
410+
'Read',
411+
{ field: 'recordType', core: true },
412+
{ text: 'using', field: 'readMode' },
413+
],
414+
quickbooks_create_customer: [
415+
{
416+
text: 'Create customer',
417+
field: ['displayName', 'givenName', 'familyName'],
418+
core: true,
419+
},
420+
],
421+
quickbooks_update_customer: [{ text: 'Update customer', field: 'customerId', core: true }],
422+
quickbooks_create_employee: [
423+
{
424+
text: 'Create employee',
425+
field: ['displayName', 'givenName', 'familyName'],
426+
core: true,
427+
},
428+
],
429+
quickbooks_update_employee: [{ text: 'Update employee', field: 'employeeId', core: true }],
430+
quickbooks_create_vendor: [
431+
{
432+
text: 'Create vendor',
433+
field: ['displayName', 'givenName', 'familyName'],
434+
core: true,
435+
},
436+
],
437+
quickbooks_update_vendor: [{ text: 'Update vendor', field: 'vendorId', core: true }],
438+
quickbooks_create_item: [{ text: 'Create item', field: 'name', core: true }],
439+
quickbooks_update_item: [{ text: 'Update item', field: 'itemId', core: true }],
440+
quickbooks_read_sales_transactions: [
441+
'Read sales transactions',
442+
{ text: 'of type', field: 'transactionType', core: true },
443+
{ text: 'using', field: 'readMode' },
444+
],
445+
quickbooks_create_estimate: [
446+
{ text: 'Create an estimate for customer', field: 'customerId', core: true },
447+
],
448+
quickbooks_update_estimate: [
449+
{ text: 'Update estimate', field: 'transactionId', core: true },
450+
],
451+
quickbooks_create_invoice: [
452+
{ text: 'Create an invoice for customer', field: 'customerId', core: true },
453+
],
454+
quickbooks_update_invoice: [{ text: 'Update invoice', field: 'transactionId', core: true }],
455+
quickbooks_void_invoice: [{ text: 'Void invoice', field: 'transactionId', core: true }],
456+
quickbooks_create_sales_receipt: [
457+
{ text: 'Create a sales receipt for customer', field: 'customerId', core: true },
458+
],
459+
quickbooks_update_sales_receipt: [
460+
{ text: 'Update sales receipt', field: 'transactionId', core: true },
461+
],
462+
quickbooks_create_customer_payment: [
463+
{ text: 'Record payment from customer', field: 'customerId', core: true },
464+
{ text: 'for', field: 'totalAmount' },
465+
],
466+
quickbooks_update_customer_payment: [
467+
{ text: 'Update customer payment', field: 'transactionId', core: true },
468+
],
469+
quickbooks_void_customer_payment: [
470+
{ text: 'Void customer payment', field: 'transactionId', core: true },
471+
],
472+
quickbooks_create_credit_memo: [
473+
{ text: 'Create a credit memo for customer', field: 'customerId', core: true },
474+
],
475+
quickbooks_update_credit_memo: [
476+
{ text: 'Update credit memo', field: 'transactionId', core: true },
477+
],
478+
quickbooks_create_refund_receipt: [
479+
{ text: 'Create a refund receipt for customer', field: 'customerId', core: true },
480+
],
481+
quickbooks_update_refund_receipt: [
482+
{ text: 'Update refund receipt', field: 'transactionId', core: true },
483+
],
484+
quickbooks_read_purchasing_transactions: [
485+
'Read purchasing transactions',
486+
{ text: 'of type', field: 'purchasingTransactionType', core: true },
487+
{ text: 'using', field: 'readMode' },
488+
],
489+
quickbooks_create_purchase_order: [
490+
{ text: 'Create a purchase order for vendor', field: 'vendorId', core: true },
491+
],
492+
quickbooks_update_purchase_order: [
493+
{ text: 'Update purchase order', field: 'transactionId', core: true },
494+
],
495+
quickbooks_create_bill: [
496+
{ text: 'Create a bill for vendor', field: 'vendorId', core: true },
497+
],
498+
quickbooks_update_bill: [{ text: 'Update bill', field: 'transactionId', core: true }],
499+
quickbooks_create_bill_payment: [
500+
{ text: 'Record bill payment for vendor', field: 'vendorId', core: true },
501+
{ text: 'for', field: 'totalAmount' },
502+
],
503+
quickbooks_update_bill_payment: [
504+
{ text: 'Update bill payment', field: 'transactionId', core: true },
505+
],
506+
quickbooks_create_vendor_credit: [
507+
{ text: 'Create a credit for vendor', field: 'vendorId', core: true },
508+
],
509+
quickbooks_update_vendor_credit: [
510+
{ text: 'Update vendor credit', field: 'transactionId', core: true },
511+
],
512+
quickbooks_create_purchase: [
513+
{ text: 'Record a purchase for vendor', field: 'vendorId', core: true },
514+
],
515+
quickbooks_update_purchase: [
516+
{ text: 'Update purchase', field: 'transactionId', core: true },
517+
],
518+
quickbooks_read_accounting_transactions: [
519+
'Read accounting transactions',
520+
{ text: 'of type', field: 'accountingTransactionType', core: true },
521+
{ text: 'using', field: 'readMode' },
522+
],
523+
quickbooks_create_journal_entry: ['Create a journal entry'],
524+
quickbooks_update_journal_entry: [
525+
{ text: 'Update journal entry', field: 'transactionId', core: true },
526+
],
527+
quickbooks_create_deposit: [
528+
{ text: 'Create a deposit into account', field: 'depositAccountId', core: true },
529+
],
530+
quickbooks_update_deposit: [{ text: 'Update deposit', field: 'transactionId', core: true }],
531+
quickbooks_run_financial_report: [{ text: 'Run report', field: 'reportType', core: true }],
532+
quickbooks_email_transaction: [
533+
{ text: 'Email', field: 'documentTransactionType', core: true },
534+
{ text: 'with ID', field: 'documentTransactionId' },
535+
],
536+
quickbooks_download_transaction_pdf: [
537+
{ text: 'Download PDF for', field: 'documentTransactionType', core: true },
538+
{ text: 'with ID', field: 'documentTransactionId' },
539+
],
540+
quickbooks_read_attachments: [
541+
'Read QuickBooks attachments',
542+
{ text: 'for', field: 'attachmentTargetType' },
543+
{ text: 'with ID', field: 'attachmentTargetId' },
544+
],
545+
quickbooks_add_attachment: [
546+
{ text: 'Add attachment to', field: 'attachmentTargetType', core: true },
547+
{ text: 'with ID', field: 'attachmentTargetId' },
548+
],
549+
quickbooks_download_attachment: [
550+
{ text: 'Download attachment', field: 'attachmentId', core: true },
551+
],
552+
},
553+
},
554+
},
389555
subBlocks: [
390556
{
391557
id: 'operation',
@@ -545,15 +711,18 @@ export const QuickBooksBlock: BlockConfig<QuickBooksResponse> = {
545711
type: 'dropdown',
546712
options: [
547713
{ label: 'Bill', id: 'bill' },
714+
{ label: 'Bill Payment', id: 'bill_payment' },
548715
{ label: 'Credit Memo', id: 'credit_memo' },
549-
{ label: 'Customer', id: 'customer' },
716+
{ label: 'Deposit', id: 'deposit' },
550717
{ label: 'Estimate', id: 'estimate' },
551718
{ label: 'Invoice', id: 'invoice' },
719+
{ label: 'Item', id: 'item' },
720+
{ label: 'Journal Entry', id: 'journal_entry' },
552721
{ label: 'Customer Payment', id: 'payment' },
553722
{ label: 'Purchase or Expense', id: 'purchase' },
723+
{ label: 'Purchase Order', id: 'purchase_order' },
554724
{ label: 'Refund Receipt', id: 'refund_receipt' },
555725
{ label: 'Sales Receipt', id: 'sales_receipt' },
556-
{ label: 'Vendor', id: 'vendor' },
557726
{ label: 'Vendor Credit', id: 'vendor_credit' },
558727
],
559728
condition: attachmentTargetCondition,
@@ -1274,11 +1443,8 @@ export const QuickBooksBlock: BlockConfig<QuickBooksResponse> = {
12741443
'quickbooks_update_vendor',
12751444
'quickbooks_create_purchase_order',
12761445
'quickbooks_create_bill',
1277-
'quickbooks_update_bill',
12781446
'quickbooks_create_bill_payment',
1279-
'quickbooks_update_bill_payment',
12801447
'quickbooks_create_vendor_credit',
1281-
'quickbooks_update_vendor_credit',
12821448
],
12831449
},
12841450
},
@@ -1315,10 +1481,12 @@ export const QuickBooksBlock: BlockConfig<QuickBooksResponse> = {
13151481
field: 'operation',
13161482
value: [...CUSTOMER_OPERATIONS, ...EMPLOYEE_OPERATIONS, ...VENDOR_OPERATIONS],
13171483
},
1318-
required: {
1319-
field: 'operation',
1320-
value: ['quickbooks_create_customer', 'quickbooks_create_vendor'],
1321-
},
1484+
required: (values) =>
1485+
requiredWhenNameAlternativesAreEmpty(
1486+
values,
1487+
['quickbooks_create_customer', 'quickbooks_create_vendor'],
1488+
['givenName', 'familyName']
1489+
),
13221490
},
13231491
{
13241492
id: 'companyName',
@@ -1339,6 +1507,14 @@ export const QuickBooksBlock: BlockConfig<QuickBooksResponse> = {
13391507
field: 'operation',
13401508
value: [...CUSTOMER_OPERATIONS, ...EMPLOYEE_OPERATIONS, ...VENDOR_OPERATIONS],
13411509
},
1510+
required: (values) =>
1511+
requiredWhenNameAlternativesAreEmpty(
1512+
values,
1513+
['quickbooks_create_customer', 'quickbooks_create_employee', 'quickbooks_create_vendor'],
1514+
values?.operation === 'quickbooks_create_employee'
1515+
? ['familyName']
1516+
: ['displayName', 'familyName']
1517+
),
13421518
},
13431519
{
13441520
id: 'familyName',
@@ -1349,6 +1525,14 @@ export const QuickBooksBlock: BlockConfig<QuickBooksResponse> = {
13491525
field: 'operation',
13501526
value: [...CUSTOMER_OPERATIONS, ...EMPLOYEE_OPERATIONS, ...VENDOR_OPERATIONS],
13511527
},
1528+
required: (values) =>
1529+
requiredWhenNameAlternativesAreEmpty(
1530+
values,
1531+
['quickbooks_create_customer', 'quickbooks_create_employee', 'quickbooks_create_vendor'],
1532+
values?.operation === 'quickbooks_create_employee'
1533+
? ['givenName']
1534+
: ['displayName', 'givenName']
1535+
),
13521536
},
13531537
{
13541538
id: 'primaryEmail',

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,35 @@ const documentTransactionTypeSchema = z.enum([
2929

3030
const attachmentTargetTypeSchema = z.enum([
3131
'bill',
32+
'bill_payment',
3233
'credit_memo',
33-
'customer',
34+
'deposit',
3435
'estimate',
3536
'invoice',
37+
'item',
38+
'journal_entry',
3639
'payment',
3740
'purchase',
41+
'purchase_order',
3842
'refund_receipt',
3943
'sales_receipt',
40-
'vendor',
4144
'vendor_credit',
4245
])
4346

44-
const optionalFileName = z.string().trim().max(180, 'Filename is too long').optional().nullable()
47+
const optionalFileName = z.string().trim().max(1000, 'Filename is too long').optional().nullable()
4548
const optionalContentType = z
4649
.string()
4750
.trim()
48-
.max(255, 'Content type is too long')
51+
.max(100, 'Content type is too long')
4952
.optional()
5053
.nullable()
5154
const optionalDescription = z
5255
.string()
5356
.trim()
54-
.max(1000, 'Description is too long')
57+
.max(2000, 'Description is too long')
5558
.optional()
5659
.nullable()
57-
const optionalNote = z.string().trim().max(4000, 'Note is too long').optional().nullable()
60+
const optionalNote = z.string().trim().max(2000, 'Note is too long').optional().nullable()
5861
const boundedId = z.string().trim().min(1, 'ID is required').max(256, 'ID is too long')
5962
const routeErrorSchema = z.object({ success: z.literal(false), error: z.string().min(1) })
6063
const attachableSchema = z
@@ -139,7 +142,7 @@ export type QuickBooksAddAttachmentBody = z.output<typeof quickBooksAddAttachmen
139142

140143
const quickBooksStoredFileShape = {
141144
file: userFileSchema,
142-
fileName: z.string().min(1).max(180),
145+
fileName: z.string().min(1).max(1000),
143146
size: z.number().int().positive(),
144147
}
145148

0 commit comments

Comments
 (0)