11import { describe , expect , it } from 'vitest'
22import { QuickBooksBlock } from '@/blocks/blocks/quickbooks'
33import * as quickBooksTools from '@/tools/quickbooks'
4+ import { QUICKBOOKS_REPORTS } from '@/tools/quickbooks/report-metadata'
45
56function requiredCondition ( fieldId : string , values : Record < string , unknown > ) {
67 const field = QuickBooksBlock . subBlocks . find ( ( subBlock ) => subBlock . id === fieldId )
@@ -66,7 +67,7 @@ describe('QuickBooks block operation coverage', () => {
6667 const optionIds = operation . options . map ( ( option ) => option . id ) . sort ( )
6768 const accessIds = [ ...( QuickBooksBlock . tools ?. access ?? [ ] ) ] . sort ( )
6869
69- expect ( toolIds ) . toHaveLength ( 47 )
70+ expect ( toolIds ) . toHaveLength ( 49 )
7071 expect ( optionIds ) . toEqual ( toolIds )
7172 expect ( accessIds ) . toEqual ( toolIds )
7273 for ( const toolId of toolIds ) {
@@ -79,3 +80,176 @@ describe('QuickBooks block operation coverage', () => {
7980 expect ( new Set ( ids ) . size ) . toBe ( ids . length )
8081 } )
8182} )
83+
84+ function blockParams ( values : Record < string , unknown > ) : Record < string , unknown > {
85+ const params = QuickBooksBlock . tools ?. config ?. params
86+ if ( ! params ) throw new Error ( 'QuickBooks block does not define a params mapper' )
87+ return params ( values ) as Record < string , unknown >
88+ }
89+
90+ function staticRequiredValue ( fieldId : string ) : unknown {
91+ const field = QuickBooksBlock . subBlocks . find ( ( subBlock ) => subBlock . id === fieldId )
92+ if ( ! field || typeof field . required !== 'object' ) {
93+ throw new Error ( `${ fieldId } does not define a static required condition` )
94+ }
95+ return field . required . value
96+ }
97+
98+ describe ( 'QuickBooks block documented requirements' , ( ) => {
99+ it ( 'requires a customer only where Intuit lists CustomerRef on the request model' , ( ) => {
100+ const required = staticRequiredValue ( 'customerId' ) as string [ ]
101+ expect ( required ) . toContain ( 'quickbooks_create_invoice' )
102+ expect ( required ) . toContain ( 'quickbooks_create_estimate' )
103+ expect ( required ) . toContain ( 'quickbooks_create_credit_memo' )
104+ expect ( required ) . not . toContain ( 'quickbooks_create_sales_receipt' )
105+ expect ( required ) . not . toContain ( 'quickbooks_create_refund_receipt' )
106+ } )
107+
108+ it ( 'accepts the documented maximum page size of 1000' , ( ) => {
109+ expect (
110+ blockParams ( {
111+ operation : 'quickbooks_read_master_data' ,
112+ readMode : 'list' ,
113+ maxResults : '1000' ,
114+ } ) . maxResults
115+ ) . toBe ( 1000 )
116+ expect ( ( ) =>
117+ blockParams ( {
118+ operation : 'quickbooks_read_master_data' ,
119+ readMode : 'list' ,
120+ maxResults : '1001' ,
121+ } )
122+ ) . toThrow ( 'maxResults must be an integer from 1 through 1000' )
123+ } )
124+
125+ it ( 'omits pagination from every by-ID read' , ( ) => {
126+ for ( const [ operation , extra ] of [
127+ [ 'quickbooks_read_master_data' , { recordType : 'customer' } ] ,
128+ [ 'quickbooks_read_sales_transactions' , { transactionType : 'invoice' } ] ,
129+ [ 'quickbooks_read_purchasing_transactions' , { purchasingTransactionType : 'bill' } ] ,
130+ [ 'quickbooks_read_accounting_transactions' , { accountingTransactionType : 'deposit' } ] ,
131+ [ 'quickbooks_read_attachments' , { } ] ,
132+ ] as const ) {
133+ const mapped = blockParams ( {
134+ operation,
135+ readMode : 'by_id' ,
136+ startPosition : '5' ,
137+ maxResults : '10' ,
138+ ...extra ,
139+ } )
140+ expect ( mapped , operation ) . not . toHaveProperty ( 'startPosition' )
141+ expect ( mapped , operation ) . not . toHaveProperty ( 'maxResults' )
142+ }
143+ } )
144+ } )
145+
146+ describe ( 'QuickBooks block wiring for documented tool parameters' , ( ) => {
147+ it ( 'sends the multicurrency and tax-treatment fields on the creates that document them' , ( ) => {
148+ const bill = blockParams ( {
149+ operation : 'quickbooks_create_bill' ,
150+ currencyCode : 'EUR' ,
151+ globalTaxCalculation : 'TaxInclusive' ,
152+ } )
153+ expect ( bill . currencyCode ) . toBe ( 'EUR' )
154+ expect ( bill . globalTaxCalculation ) . toBe ( 'TaxInclusive' )
155+
156+ const billPayment = blockParams ( {
157+ operation : 'quickbooks_create_bill_payment' ,
158+ currencyCode : 'EUR' ,
159+ globalTaxCalculation : 'TaxInclusive' ,
160+ apAccountId : 'ap-1' ,
161+ documentNumber : 'BP-1' ,
162+ } )
163+ expect ( billPayment . currencyCode ) . toBe ( 'EUR' )
164+ expect ( billPayment . globalTaxCalculation ) . toBeUndefined ( )
165+ expect ( billPayment . apAccountId ) . toBe ( 'ap-1' )
166+ expect ( billPayment . documentNumber ) . toBe ( 'BP-1' )
167+
168+ const journalEntry = blockParams ( {
169+ operation : 'quickbooks_create_journal_entry' ,
170+ currencyCode : 'GBP' ,
171+ globalTaxCalculation : 'default' ,
172+ } )
173+ expect ( journalEntry . currencyCode ) . toBe ( 'GBP' )
174+ expect ( journalEntry . globalTaxCalculation ) . toBeUndefined ( )
175+ } )
176+
177+ it ( 'sends a purchase-order due date' , ( ) => {
178+ expect (
179+ blockParams ( { operation : 'quickbooks_create_purchase_order' , dueDate : '2026-09-30' } ) . dueDate
180+ ) . toBe ( '2026-09-30' )
181+ expect (
182+ blockParams ( { operation : 'quickbooks_update_purchase_order' , dueDate : '2026-09-30' } ) . dueDate
183+ ) . toBe ( '2026-09-30' )
184+ } )
185+
186+ it ( 'coerces the quick-zoom switch to the boolean the report tool requires' , ( ) => {
187+ expect (
188+ blockParams ( {
189+ operation : 'quickbooks_run_financial_report' ,
190+ reportType : 'balance_sheet' ,
191+ reportQuickZoomUrl : 'true' ,
192+ } ) . quickZoomUrl
193+ ) . toBe ( true )
194+ expect (
195+ blockParams ( {
196+ operation : 'quickbooks_run_financial_report' ,
197+ reportType : 'cash_flow' ,
198+ reportQuickZoomUrl : 'true' ,
199+ } ) . quickZoomUrl
200+ ) . toBeUndefined ( )
201+ } )
202+
203+ it ( 'sends the report date macro and the employee filter' , ( ) => {
204+ expect (
205+ blockParams ( {
206+ operation : 'quickbooks_run_financial_report' ,
207+ reportType : 'balance_sheet' ,
208+ reportDateMacro : 'last_fiscal_year' ,
209+ } ) . dateMacro
210+ ) . toBe ( 'last_fiscal_year' )
211+ expect (
212+ blockParams ( {
213+ operation : 'quickbooks_run_financial_report' ,
214+ reportType : 'profit_and_loss_detail' ,
215+ reportEmployeeId : 'employee-1' ,
216+ } ) . employeeId
217+ ) . toBe ( 'employee-1' )
218+ } )
219+
220+ it ( 'offers every documented report in the report dropdown' , ( ) => {
221+ const reportType = QuickBooksBlock . subBlocks . find ( ( subBlock ) => subBlock . id === 'reportType' )
222+ if ( ! reportType || ! ( 'options' in reportType ) || ! Array . isArray ( reportType . options ) ) {
223+ throw new Error ( 'QuickBooks report dropdown is missing' )
224+ }
225+ expect ( reportType . options . map ( ( option ) => option . id ) . sort ( ) ) . toEqual (
226+ Object . keys ( QUICKBOOKS_REPORTS ) . sort ( )
227+ )
228+ } )
229+
230+ it ( 'keeps the upload and download attachment file names as separate fields' , ( ) => {
231+ expect (
232+ blockParams ( {
233+ operation : 'quickbooks_download_attachment' ,
234+ attachmentId : 'attachment-1' ,
235+ downloadAttachmentFileName : 'saved.pdf' ,
236+ } ) . fileName
237+ ) . toBe ( 'saved.pdf' )
238+ expect (
239+ blockParams ( {
240+ operation : 'quickbooks_add_attachment' ,
241+ attachmentKind : 'note' ,
242+ attachmentFileName : 'ignored.pdf' ,
243+ } ) . fileName
244+ ) . toBeUndefined ( )
245+
246+ const uploadName = QuickBooksBlock . subBlocks . find (
247+ ( subBlock ) => subBlock . id === 'attachmentFileName'
248+ )
249+ expect ( uploadName ?. condition ) . toEqual ( {
250+ field : 'operation' ,
251+ value : 'quickbooks_add_attachment' ,
252+ and : { field : 'attachmentKind' , value : 'file' } ,
253+ } )
254+ } )
255+ } )
0 commit comments