scope summon sync by element

- accept filter param in preview_sync and import
- element joins through summon table
This commit is contained in:
Justin Edmund 2025-12-29 22:45:34 -08:00
parent 7b5b564edf
commit cbffd38cd4
2 changed files with 43 additions and 11 deletions

View file

@ -127,7 +127,8 @@ module Api
game_data, game_data,
update_existing: import_params[:update_existing] == true, update_existing: import_params[:update_existing] == true,
is_full_inventory: import_params[:is_full_inventory] == true, is_full_inventory: import_params[:is_full_inventory] == true,
reconcile_deletions: import_params[:reconcile_deletions] == true reconcile_deletions: import_params[:reconcile_deletions] == true,
filter: import_params[:filter]
) )
result = service.import result = service.import
@ -151,12 +152,13 @@ module Api
# @return [JSON] List of items that would be deleted # @return [JSON] List of items that would be deleted
def preview_sync def preview_sync
game_data = import_params[:data] game_data = import_params[:data]
filter = import_params[:filter]
unless game_data.present? unless game_data.present?
return render json: { error: 'No data provided' }, status: :bad_request return render json: { error: 'No data provided' }, status: :bad_request
end end
service = SummonImportService.new(current_user, game_data) service = SummonImportService.new(current_user, game_data, filter: filter)
items_to_delete = service.preview_deletions items_to_delete = service.preview_deletions
render json: { render json: {
@ -218,7 +220,8 @@ module Api
update_existing: params[:update_existing], update_existing: params[:update_existing],
is_full_inventory: params[:is_full_inventory], is_full_inventory: params[:is_full_inventory],
reconcile_deletions: params[:reconcile_deletions], reconcile_deletions: params[:reconcile_deletions],
data: params[:data]&.to_unsafe_h data: params[:data]&.to_unsafe_h,
filter: params[:filter]&.to_unsafe_h
} }
end end

View file

@ -20,6 +20,7 @@ class SummonImportService
@update_existing = options[:update_existing] || false @update_existing = options[:update_existing] || false
@is_full_inventory = options[:is_full_inventory] || false @is_full_inventory = options[:is_full_inventory] || false
@reconcile_deletions = options[:reconcile_deletions] || false @reconcile_deletions = options[:reconcile_deletions] || false
@filter = options[:filter] # { elements: [...] }
@created = [] @created = []
@updated = [] @updated = []
@skipped = [] @skipped = []
@ -30,6 +31,7 @@ class SummonImportService
## ##
# Previews what would be deleted in a sync operation. # Previews what would be deleted in a sync operation.
# Does not modify any data, just returns items that would be removed. # Does not modify any data, just returns items that would be removed.
# When a filter is active, only considers items matching that filter.
# #
# @return [Array<CollectionSummon>] Collection summons that would be deleted # @return [Array<CollectionSummon>] Collection summons that would be deleted
def preview_deletions def preview_deletions
@ -45,10 +47,14 @@ class SummonImportService
return [] if game_ids.empty? return [] if game_ids.empty?
# Find collection summons with game_ids NOT in the import # Find collection summons with game_ids NOT in the import
@user.collection_summons # Scoped to filter criteria if present
.includes(:summon) scope = @user.collection_summons
.where.not(game_id: nil) .includes(:summon)
.where.not(game_id: game_ids) .where.not(game_id: nil)
.where.not(game_id: game_ids)
scope = apply_filter_scope(scope)
scope
end end
## ##
@ -193,18 +199,22 @@ class SummonImportService
## ##
# Reconciles deletions by removing collection summons not in the processed list. # Reconciles deletions by removing collection summons not in the processed list.
# Only called when @is_full_inventory and @reconcile_deletions are both true. # Only called when @is_full_inventory and @reconcile_deletions are both true.
# When a filter is active, only deletes items matching that filter.
# #
# @return [Hash] Reconciliation result with deleted count and orphaned grid item IDs # @return [Hash] Reconciliation result with deleted count and orphaned grid item IDs
def reconcile_deletions def reconcile_deletions
# Find collection summons with game_ids NOT in our processed list # Find collection summons with game_ids NOT in our processed list
missing = @user.collection_summons # Scoped to filter criteria if present
.where.not(game_id: nil) scope = @user.collection_summons
.where.not(game_id: @processed_game_ids) .where.not(game_id: nil)
.where.not(game_id: @processed_game_ids)
scope = apply_filter_scope(scope)
deleted_count = 0 deleted_count = 0
orphaned_grid_item_ids = [] orphaned_grid_item_ids = []
missing.find_each do |coll_summon| scope.find_each do |coll_summon|
# Collect IDs of grid items that will be orphaned # Collect IDs of grid items that will be orphaned
grid_summon_ids = GridSummon.where(collection_summon_id: coll_summon.id).pluck(:id) grid_summon_ids = GridSummon.where(collection_summon_id: coll_summon.id).pluck(:id)
orphaned_grid_item_ids.concat(grid_summon_ids) orphaned_grid_item_ids.concat(grid_summon_ids)
@ -219,4 +229,23 @@ class SummonImportService
orphaned_grid_items: orphaned_grid_item_ids orphaned_grid_items: orphaned_grid_item_ids
} }
end end
##
# Applies element filter to a collection summons scope.
# Used to scope deletion checks to only items matching the current game filter.
#
# @param scope [ActiveRecord::Relation] The collection summons relation to filter
# @return [ActiveRecord::Relation] Filtered relation
def apply_filter_scope(scope)
return scope unless @filter.present?
# Element: always join through summon (no element on collection_summons)
if @filter[:elements].present? || @filter['elements'].present?
elements = @filter[:elements] || @filter['elements']
scope = scope.joins(:summon).where(summons: { element: elements })
end
# Summons don't have proficiency - ignore if present in filter
scope
end
end end