diff --git a/backend/src/object/object.service.ts b/backend/src/object/object.service.ts index 64957a2..9e96032 100644 --- a/backend/src/object/object.service.ts +++ b/backend/src/object/object.service.ts @@ -81,8 +81,10 @@ export class ObjectService { .where({ objectDefinitionId: obj.id }) .orderBy('label', 'asc'); - // Normalize all fields to ensure system fields are properly marked - const normalizedFields = fields.map((field: any) => this.normalizeField(field)); + // Normalize all fields to ensure system fields are properly marked and add any missing system fields + const normalizedFields = this.addMissingSystemFields( + fields.map((field: any) => this.normalizeField(field)), + ); // Get app information if object belongs to an app let app = null; @@ -552,6 +554,12 @@ export class ObjectService { throw new NotFoundException(`Object ${objectApiName} not found`); } + // Normalize and enrich fields to include system fields for downstream permissions/search + const normalizedFields = this.addMissingSystemFields( + (objectDefModel.fields || []).map((field: any) => this.normalizeField(field)), + ); + objectDefModel.fields = normalizedFields; + await this.ensureModelRegistered(resolvedTenantId, objectApiName, objectDefModel); const boundModel = await this.modelService.getBoundModel(resolvedTenantId, objectApiName); @@ -835,7 +843,10 @@ export class ObjectService { userId, ); - const validFields = new Set(objectDefModel.fields?.map((field: any) => field.apiName)); + const validFields = new Set([ + ...(objectDefModel.fields?.map((field: any) => field.apiName) || []), + ...this.getSystemFieldNames(), + ]); this.applySearchFilters(query, filters, validFields); if (sort?.field && validFields.has(sort.field)) { @@ -1343,15 +1354,35 @@ export class ObjectService { } private isSystemField(apiName: string): boolean { - return [ - 'id', - 'ownerId', - 'created_at', - 'updated_at', - 'createdAt', - 'updatedAt', - 'tenantId', - ].includes(apiName); + return this.getSystemFieldNames().includes(apiName); + } + + private getSystemFieldNames(): string[] { + return ['id', 'ownerId', 'created_at', 'updated_at', 'createdAt', 'updatedAt', 'tenantId']; + } + + private addMissingSystemFields(fields: any[]): any[] { + const existing = new Map((fields || []).map((field) => [field.apiName, field])); + const systemDefaults = [ + { apiName: 'id', label: 'ID', type: 'STRING' }, + { apiName: 'created_at', label: 'Created At', type: 'DATE_TIME' }, + { apiName: 'updated_at', label: 'Updated At', type: 'DATE_TIME' }, + { apiName: 'ownerId', label: 'Owner', type: 'LOOKUP', referenceObject: 'User' }, + ]; + + const merged = [...fields]; + for (const sysField of systemDefaults) { + if (!existing.has(sysField.apiName)) { + merged.push({ + ...sysField, + isSystem: true, + isCustom: false, + isRequired: false, + }); + } + } + + return merged; } private isKeywordField(type: string | undefined): boolean {