@@ -561,6 +561,127 @@ describe('CRM Metadata Spec Compliance', () => {
561561 expect ( contacts . lookup_page_size ) . toBe ( 15 ) ;
562562 } ) ;
563563 } ) ;
564+
565+ // ------------------------------------------------------------------
566+ // Enterprise Query Parameter Injection & Filter Bar Integration
567+ // ------------------------------------------------------------------
568+
569+ describe ( 'Enterprise Query Parameter & Filter Bar Compatibility' , ( ) => {
570+ /**
571+ * Simulate RecordPickerDialog's lookupFiltersToRecord conversion.
572+ * This mirrors the internal function in RecordPickerDialog.tsx to verify
573+ * that CRM metadata produces correct $filter query parameters.
574+ */
575+ function lookupFiltersToRecord (
576+ filters : Array < { field : string ; operator : string ; value : unknown } > ,
577+ ) : Record < string , any > {
578+ const result : Record < string , any > = { } ;
579+ for ( const f of filters ) {
580+ switch ( f . operator ) {
581+ case 'eq' : result [ f . field ] = f . value ; break ;
582+ case 'ne' : result [ f . field ] = { $ne : f . value } ; break ;
583+ case 'gt' : result [ f . field ] = { $gt : f . value } ; break ;
584+ case 'lt' : result [ f . field ] = { $lt : f . value } ; break ;
585+ case 'gte' : result [ f . field ] = { $gte : f . value } ; break ;
586+ case 'lte' : result [ f . field ] = { $lte : f . value } ; break ;
587+ case 'contains' : result [ f . field ] = { $contains : f . value } ; break ;
588+ case 'in' : result [ f . field ] = { $in : f . value } ; break ;
589+ case 'notIn' : result [ f . field ] = { $nin : f . value } ; break ;
590+ }
591+ }
592+ return result ;
593+ }
594+
595+ /**
596+ * Simulate LookupField's mapFieldTypeToFilterType conversion.
597+ * This mirrors the internal function in LookupField.tsx to verify
598+ * CRM lookup_columns produce valid filter bar configurations.
599+ */
600+ function mapFieldTypeToFilterType ( fieldType : string ) : string | undefined {
601+ const mapping : Record < string , string > = {
602+ text : 'text' , number : 'number' , currency : 'number' ,
603+ percent : 'number' , select : 'select' , status : 'select' ,
604+ date : 'date' , datetime : 'date' , boolean : 'boolean' ,
605+ } ;
606+ return mapping [ fieldType ] ;
607+ }
608+
609+ it ( 'account.owner lookup_filters produce correct $filter for active users' , ( ) => {
610+ const owner = ( AccountObject . fields as any ) . owner ;
611+ const $filter = lookupFiltersToRecord ( owner . lookup_filters ) ;
612+ expect ( $filter ) . toEqual ( { active : true } ) ;
613+ } ) ;
614+
615+ it ( 'contact.account lookup_filters produce $in for type restriction' , ( ) => {
616+ const account = ( ContactObject . fields as any ) . account ;
617+ const $filter = lookupFiltersToRecord ( account . lookup_filters ) ;
618+ expect ( $filter ) . toEqual ( { type : { $in : [ 'Customer' , 'Partner' ] } } ) ;
619+ } ) ;
620+
621+ it ( 'order_item.order lookup_filters produce $ne for cancelled exclusion' , ( ) => {
622+ const order = ( OrderItemObject . fields as any ) . order ;
623+ const $filter = lookupFiltersToRecord ( order . lookup_filters ) ;
624+ expect ( $filter ) . toEqual ( { status : { $ne : 'cancelled' } } ) ;
625+ } ) ;
626+
627+ it ( 'opportunity_contact.opportunity lookup_filters produce $nin for closed stages' , ( ) => {
628+ const opp = ( OpportunityContactObject . fields as any ) . opportunity ;
629+ const $filter = lookupFiltersToRecord ( opp . lookup_filters ) ;
630+ expect ( $filter ) . toEqual ( { stage : { $nin : [ 'closed_won' , 'closed_lost' ] } } ) ;
631+ } ) ;
632+
633+ it ( 'typed lookup_columns produce valid filter bar configurations' , ( ) => {
634+ const product = ( OrderItemObject . fields as any ) . product ;
635+ const cols = product . lookup_columns as Array < { field : string ; type ?: string ; label ?: string } > ;
636+
637+ const filterColumns = cols
638+ . filter ( ( c ) => c . type )
639+ . map ( ( c ) => ( {
640+ field : c . field ,
641+ label : c . label ,
642+ type : mapFieldTypeToFilterType ( c . type ! ) ,
643+ } ) )
644+ . filter ( ( c ) => c . type !== undefined ) ;
645+
646+ // Product lookup should produce filter bar entries for select, currency(→number), number, boolean
647+ expect ( filterColumns . length ) . toBeGreaterThanOrEqual ( 3 ) ;
648+ const types = filterColumns . map ( ( c ) => c . type ) ;
649+ expect ( types ) . toContain ( 'select' ) ; // category
650+ expect ( types ) . toContain ( 'number' ) ; // price, stock
651+ expect ( types ) . toContain ( 'boolean' ) ; // is_active
652+ } ) ;
653+
654+ it ( 'opportunity.account typed columns map to valid filter bar types' , ( ) => {
655+ const account = ( OpportunityObject . fields as any ) . account ;
656+ const cols = account . lookup_columns as Array < { field : string ; type ?: string } > ;
657+
658+ const filterTypes = cols
659+ . filter ( ( c ) => c . type )
660+ . map ( ( c ) => mapFieldTypeToFilterType ( c . type ! ) )
661+ . filter ( Boolean ) ;
662+
663+ // account columns have select + currency(→number) types
664+ expect ( filterTypes ) . toContain ( 'select' ) ;
665+ expect ( filterTypes ) . toContain ( 'number' ) ;
666+ } ) ;
667+
668+ it ( 'all CRM lookup_filters convert to valid $filter records without errors' , ( ) => {
669+ for ( const obj of allObjects ) {
670+ const lookups = Object . entries ( obj . fields ) . filter (
671+ ( [ , f ] : [ string , any ] ) => f . type === 'lookup' || f . type === 'master_detail' ,
672+ ) ;
673+ for ( const [ fieldName , field ] of lookups ) {
674+ const f = field as any ;
675+ if ( ! f . lookup_filters ) continue ;
676+ const $filter = lookupFiltersToRecord ( f . lookup_filters ) ;
677+ expect (
678+ Object . keys ( $filter ) . length ,
679+ `${ obj . name } .${ fieldName } lookup_filters produced empty $filter` ,
680+ ) . toBeGreaterThan ( 0 ) ;
681+ }
682+ }
683+ } ) ;
684+ } ) ;
564685} ) ;
565686
566687// ====================================================================
0 commit comments