Class: SpreeCmCommissioner::VotingCredits::ClaimFreeVotes

Inherits:
Object
  • Object
show all
Includes:
Spree::ServiceModule::Base
Defined in:
app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(show:, user:, tenant_id:, votable_type: nil, votable_id: nil) ⇒ ClaimFreeVotes

Returns a new instance of ClaimFreeVotes.



43
44
45
46
47
48
49
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 43

def initialize(show:, user:, tenant_id:, votable_type: nil, votable_id: nil)
  @show = show
  @user = user
  @tenant_id = tenant_id
  @votable_type = votable_type
  @votable_id = votable_id
end

Instance Attribute Details

#showObject (readonly)

Returns the value of attribute show.



41
42
43
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 41

def show
  @show
end

#tenant_idObject (readonly)

Returns the value of attribute tenant_id.



41
42
43
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 41

def tenant_id
  @tenant_id
end

#userObject (readonly)

Returns the value of attribute user.



41
42
43
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 41

def user
  @user
end

#votable_idObject (readonly)

Returns the value of attribute votable_id.



41
42
43
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 41

def votable_id
  @votable_id
end

#votable_typeObject (readonly)

Returns the value of attribute votable_type.



41
42
43
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 41

def votable_type
  @votable_type
end

Instance Method Details

#callObject

Returns the VotingCredit after granting free votes, or returns msg if free votes cannot be granted (e.g. limit reached).



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 53

def call # rubocop:disable Metrics/AbcSize
  return success(nil) unless show.free_vote_limit_type

  idempotency_key = show.free_vote_idempotency_key(user_id: user.id, votable_id: votable_id)

  # idempotency_key is uniquely indexed — faster than scoping through user.voting_transactions association.
  if SpreeCmCommissioner::VotingCreditTransaction.exists?(idempotency_key: idempotency_key)
    return failure(nil, 'Free votes have already been claimed')
  end

  amount = show.free_vote_limit.to_i

  return failure(nil, "Free votes are not available for #{show.voting_credit_scope}") if amount <= 0

  case show.free_vote_limit_type
  when 'per_episode', 'per_voting_session'
    if votable_type.blank? || votable_id.blank?
      return failure(nil, 'votable_type and votable_id are required for per_episode/per_voting_session')
    end
  end

  credit = nil

  ActiveRecord::Base.transaction do
    credit = user.voting_credits.active.find_or_create_by!(
      tenant_id: tenant_id,
      votable: votable
    ) { |c| c.category = :promo }

    credit.with_lock do
      credit.free_votes_amount += amount
      credit.save!
    end

    credit.voting_credit_transactions.create!(
      tenant_id: tenant_id,
      action: :free_claim,
      amount: amount,
      originator: show,
      free_claim_votable_id: votable_id,
      user_total_amount: credit.available_votes,
      memo: "Free claim: #{amount} votes | credit_scope: #{show.voting_credit_scope} | key: #{idempotency_key}"
    )
  end

  success(credit)
rescue ActiveRecord::RecordNotUnique
  # Two concurrent requests can both pass the existence check before either commits.
  # The one that loses the unique-index race lands here — treat it as already claimed.
  failure(nil, 'Free votes have already been claimed')
rescue StandardError => e
  failure(nil, e.message)
end

#votableObject

Always find votable by the show’s credit_votable_type, to ensure consistency with where the credits are allocated. Resolves through the show association to prevent claiming against unrelated votable_ids.



109
110
111
112
113
114
115
116
# File 'app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb', line 109

def votable
  case show.credit_votable_type
  when 'SpreeCmCommissioner::Show'          then show
  when 'SpreeCmCommissioner::ShowEpisode'   then show.episodes.find(votable_id)
  when 'SpreeCmCommissioner::VotingSession' then show.voting_sessions.find(votable_id)
  else raise ActiveRecord::RecordNotFound, "Unknown credit_votable_type: #{show.credit_votable_type}"
  end
end