From 1b2f2c71c3c6c406248d52f3e753cdfe5bdd1170 Mon Sep 17 00:00:00 2001 From: souheyl gouadria <118611908+Souheyl7Gouadria@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:54:15 +0100 Subject: [PATCH] Move filter group destruction after filter operations (#22248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### The current order is: 1. Create filter groups 2. Update filter groups 3. Destroy filter groups ← **happens here** 4. Clean up store (cascade) 5. Create/update/delete filters (which may reference groups just destroyed) The fix is to move filter group destruction after filter operations, so filters that reference those groups get created/updated/deleted first. ### after fix : The persistence order is now: 1. Create filter groups 2. Update filter groups 3. Create/update/delete view filters (these can safely reference groups that still exist) 4. Destroy filter groups (only after all filter mutations are done) 5. Clean up store (cascade-deleted filters **root cause :** step 4 happened before step 3, so filter creates/updates would reference groups that had already been deleted in the same save cycle ; causing the backend to fail with "Migration execution failed" when it couldn't resolve the `viewFilterGroupId `foreign key. this fixes the bug : #21351 Review in cubic Co-authored-by: Souheyl Gouadria Co-authored-by: Charles Bochet --- ...roupFiltersToViewFiltersAndGroupFilters.ts | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts index 31589686d8..f3b9f67ada 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters.ts @@ -89,21 +89,6 @@ export const useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters = currentView, ); await performViewFilterGroupAPIUpdate(viewFilterGroupsToUpdate); - await performViewFilterGroupAPIDestroy(viewFilterGroupIdsToDestroy); - - // Mirror the DB cascade: remove cascade-deleted viewFilters from the store - if (viewFilterGroupIdsToDestroy.length > 0) { - const destroyedIdsSet = new Set(viewFilterGroupIdsToDestroy); - - store.set(metadataStoreState.atomFamily('viewFilters'), (prev) => ({ - ...prev, - current: (prev.current as FlatViewFilter[]).filter( - (viewFilter) => - !isDefined(viewFilter.viewFilterGroupId) || - !destroyedIdsSet.has(viewFilter.viewFilterGroupId), - ), - })); - } const currentViewFilters = currentView.viewFilters ?? []; const currentRecordFilters = store.get( @@ -195,6 +180,22 @@ export const useSaveRecordFiltersAndGroupFiltersToViewFiltersAndGroupFilters = if (deleteResult.status === 'failed') { return; } + + await performViewFilterGroupAPIDestroy(viewFilterGroupIdsToDestroy); + + // Mirror the DB cascade: remove cascade-deleted viewFilters from the store + if (viewFilterGroupIdsToDestroy.length > 0) { + const destroyedIdsSet = new Set(viewFilterGroupIdsToDestroy); + + store.set(metadataStoreState.atomFamily('viewFilters'), (prev) => ({ + ...prev, + current: (prev.current as FlatViewFilter[]).filter( + (viewFilter) => + !isDefined(viewFilter.viewFilterGroupId) || + !destroyedIdsSet.has(viewFilter.viewFilterGroupId), + ), + })); + } }, [ canPersistChanges, currentView,