Files
sure/app/controllers/pending_duplicate_merges_controller.rb
soky srm 560c9fbff3 Family sharing (#1272)
* Initial account sharing changes

* Update schema.rb

* Update schema.rb

* Change sharing UI to modal

* UX fixes and sharing controls

* Scope include in finances better

* Update totals.rb

* Update totals.rb

* Scope reports to finance account scope

* Update impersonation_sessions_controller_test.rb

* Review fixes

* Update schema.rb

* Update show.html.erb

* FIX db validation

* Refine edit permissions

* Review items

* Review

* Review

* Add application level helper

* Critical review

* Address remaining review items

* Fix modals

* more scoping

* linter

* small UI fix

* Fix: Sync broadcasts push unscoped balance sheet to all users

* Update sync_complete_event.rb

 The fix removes the sidebar broadcasts (which rendered unscoped account groups using family.balance_sheet without user context)
  along with the now-unused sidebar_targets, account_group, and family_balance_sheet private methods.

  The sidebar will still update correctly — when the sync completes, Family::SyncCompleteEvent#broadcast fires family.broadcast_refresh, which triggers a
  morph-based page refresh for each user with their own authenticated session, rendering properly scoped sidebar content.
2026-03-25 10:50:23 +01:00

89 lines
3.3 KiB
Ruby

class PendingDuplicateMergesController < ApplicationController
before_action :set_transaction
def new
@limit = 10
# Ensure offset is non-negative to prevent abuse
@offset = [ (params[:offset] || 0).to_i, 0 ].max
# Fetch one extra to determine if there are more results
candidates = @transaction.pending_duplicate_candidates(limit: @limit + 1, offset: @offset).to_a
@has_more = candidates.size > @limit
@potential_duplicates = candidates.first(@limit)
# Calculate range for display (e.g., "1-10", "11-20")
@range_start = @offset + 1
@range_end = @offset + @potential_duplicates.count
end
def create
permission = @transaction.entry.account.permission_for(Current.user)
unless permission.in?([ :owner, :full_control ])
redirect_back_or_to transactions_path, alert: t("accounts.not_authorized")
return
end
# Manually merge the pending transaction with the selected posted transaction
unless merge_params[:posted_entry_id].present?
redirect_back_or_to transactions_path, alert: "Please select a posted transaction to merge with"
return
end
# Validate the posted entry is an eligible candidate (same account, currency, not pending)
posted_entry = find_eligible_posted_entry(merge_params[:posted_entry_id])
unless posted_entry
redirect_back_or_to transactions_path, alert: "Invalid transaction selected for merge"
return
end
# Store the merge suggestion and immediately execute it
@transaction.update!(
extra: (@transaction.extra || {}).merge(
"potential_posted_match" => {
"entry_id" => posted_entry.id,
"reason" => "manual_match",
"posted_amount" => posted_entry.amount.to_s,
"confidence" => "high", # Manual matches are high confidence
"detected_at" => Date.current.to_s
}
)
)
# Immediately merge
if @transaction.merge_with_duplicate!
redirect_back_or_to transactions_path, notice: "Pending transaction merged with posted transaction"
else
redirect_back_or_to transactions_path, alert: "Could not merge transactions"
end
end
private
def set_transaction
entry = Current.accessible_entries.find(params[:transaction_id])
@transaction = entry.entryable
unless @transaction.is_a?(Transaction) && @transaction.pending?
redirect_to transactions_path, alert: "This feature is only available for pending transactions"
end
end
def find_eligible_posted_entry(entry_id)
# Constrain to same account, currency, and ensure it's a posted transaction
# Use the same logic as pending_duplicate_candidates to ensure consistency
conditions = Transaction::PENDING_PROVIDERS.map { |provider| "(transactions.extra -> '#{provider}' ->> 'pending')::boolean IS NOT TRUE" }
@transaction.entry.account.entries
.joins("INNER JOIN transactions ON transactions.id = entries.entryable_id AND entries.entryable_type = 'Transaction'")
.where(id: entry_id)
.where(currency: @transaction.entry.currency)
.where.not(id: @transaction.entry.id)
.where(conditions.join(" AND "))
.first
end
def merge_params
params.require(:pending_duplicate_merges).permit(:posted_entry_id)
end
end