diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts index 7cd02af8cca..9a20d83577c 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts @@ -209,7 +209,7 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = { .primaryPhoneCallingCode, isImportable: true, isFilterable: true, - isIncludedInUniqueConstraint: false, + isIncludedInUniqueConstraint: true, }, { subFieldName: diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/__tests__/get-conflicting-fields.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/__tests__/get-conflicting-fields.util.spec.ts index 9d72bc0e650..306f3b1f43a 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/__tests__/get-conflicting-fields.util.spec.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/__tests__/get-conflicting-fields.util.spec.ts @@ -61,6 +61,13 @@ describe('getConflictingFields', () => { isUnique: false, }); + const phonesUniqueField = createMockField({ + id: 'phones-unique-id', + name: 'phonesField', + type: FieldMetadataType.PHONES, + isUnique: true, + }); + const addressUniqueFieldNoIncludedProp = createMockField({ id: 'address-unique-id', name: 'addressField', @@ -170,6 +177,33 @@ describe('getConflictingFields', () => { expect(result).toEqual([{ baseField: 'id', fullPath: 'id', column: 'id' }]); }); + it('returns all unique constraint subfields for composite fields with multiple included properties', () => { + const fields = [idField, phonesUniqueField]; + const flatObjectMetadata = buildFlatObjectMetadata(fields); + const flatFieldMetadataMaps = buildFlatFieldMetadataMaps(fields); + + const result = getConflictingFields( + flatObjectMetadata, + flatFieldMetadataMaps, + ); + + expect(result).toEqual( + expect.arrayContaining([ + { baseField: 'id', fullPath: 'id', column: 'id' }, + { + baseField: 'phonesField', + fullPath: 'phonesField.primaryPhoneNumber', + column: 'phonesFieldPrimaryPhoneNumber', + }, + { + baseField: 'phonesField', + fullPath: 'phonesField.primaryPhoneCallingCode', + column: 'phonesFieldPrimaryPhoneCallingCode', + }, + ]), + ); + }); + it('ignores non-unique fields', () => { const fields = [idField, phonesNotUniqueField]; const flatObjectMetadata = buildFlatObjectMetadata(fields); diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/get-conflicting-fields.util.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/get-conflicting-fields.util.ts index 4207ef12da4..7a85a6c43ae 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/get-conflicting-fields.util.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/utils/get-conflicting-fields.util.ts @@ -32,18 +32,14 @@ export const getConflictingFields = ( ]; } - const property = compositeType.properties.find( + const uniqueProperties = compositeType.properties.filter( (prop) => prop.isIncludedInUniqueConstraint, ); - return property - ? [ - { - baseField: field.name, - fullPath: `${field.name}.${property.name}`, - column: `${field.name}${capitalize(property.name)}`, - }, - ] - : []; + return uniqueProperties.map((property) => ({ + baseField: field.name, + fullPath: `${field.name}.${property.name}`, + column: `${field.name}${capitalize(property.name)}`, + })); }); }; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/find-conflicting-record.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/find-conflicting-record.util.ts index d3ea578dcff..0f75d90fbdb 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/find-conflicting-record.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/find-conflicting-record.util.ts @@ -28,17 +28,15 @@ export const findConflictingRecord = async ( return field.name === columnName; } - const property = compositeType.properties.find( + const uniqueProperties = compositeType.properties.filter( (prop) => prop.isIncludedInUniqueConstraint, ); - if (!property) { - return false; - } + return uniqueProperties.some((property) => { + const expectedColumnName = `${field.name}${capitalize(property.name)}`; - const expectedColumnName = `${field.name}${capitalize(property.name)}`; - - return expectedColumnName === columnName; + return expectedColumnName === columnName; + }); }); if (!matchingField) { diff --git a/packages/twenty-shared/src/types/composite-types/phones.composite-type.ts b/packages/twenty-shared/src/types/composite-types/phones.composite-type.ts index dbba0a336c8..022877d49bc 100644 --- a/packages/twenty-shared/src/types/composite-types/phones.composite-type.ts +++ b/packages/twenty-shared/src/types/composite-types/phones.composite-type.ts @@ -23,6 +23,7 @@ export const phonesCompositeType: CompositeType = { type: FieldMetadataType.TEXT, hidden: false, isRequired: false, + isIncludedInUniqueConstraint: true, }, { name: 'additionalPhones',