Class: VoucherService::Apply

Inherits:
ApplicationService show all
Includes:
DefaultErrorContainer
Defined in:
app/services/voucher_service/apply.rb

Overview

typed: ignore

Defined Under Namespace

Classes: InvalidVoucher

Instance Attribute Summary collapse

Attributes inherited from ApplicationService

#object

Instance Method Summary collapse

Methods included from DefaultErrorContainer

#error, #error_message_simple, #merge_errors

Methods inherited from ApplicationService

#execute!

Constructor Details

#initialize(reservation, vouchers, actor, payment_params = {}, redeemed_points = 0) ⇒ Apply

Returns a new instance of Apply.



7
8
9
10
11
12
13
# File 'app/services/voucher_service/apply.rb', line 7

def initialize(reservation, vouchers, actor, payment_params = {}, redeemed_points = 0)
  @reservation = reservation
  @vouchers = vouchers
  @actor = actor
  @payment_params = payment_params
  @redeemed_points = redeemed_points
end

Instance Attribute Details

#actorObject (readonly)

Returns the value of attribute actor.



4
5
6
# File 'app/services/voucher_service/apply.rb', line 4

def actor
  @actor
end

#payment_paramsObject (readonly)

Returns the value of attribute payment_params.



4
5
6
# File 'app/services/voucher_service/apply.rb', line 4

def payment_params
  @payment_params
end

#redeemed_pointsObject (readonly)

Returns the value of attribute redeemed_points.



4
5
6
# File 'app/services/voucher_service/apply.rb', line 4

def redeemed_points
  @redeemed_points
end

#reservationObject

Returns the value of attribute reservation.



5
6
7
# File 'app/services/voucher_service/apply.rb', line 5

def reservation
  @reservation
end

#vouchersObject (readonly)

Returns the value of attribute vouchers.



4
5
6
# File 'app/services/voucher_service/apply.rb', line 4

def vouchers
  @vouchers
end

Instance Method Details

#create_unpersisted_vouchersObject



48
49
50
51
52
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
# File 'app/services/voucher_service/apply.rb', line 48

def create_unpersisted_vouchers
  user = reservation.user
  unpersisted_vouchers.each do |v|
    if v.voucher_category == 'redemption'
      v.save!
      restaurant = reservation.restaurant

      reward = if redeemed_points.zero?
                 VoucherForm::Reward.new(user_id: user.id, redeemed_points: v.amount.amount.to_i)
               else
                 VoucherForm::Reward.new(user_id: user.id, redeemed_points: redeemed_points,
                                         campaign_redeemed_amount: v.amount.amount.to_i,
                                         restaurant: restaurant, country_code: restaurant&.country&.alpha3)
               end
      reward.redeem!
    elsif v.voucher_code.to_s.first(4) == User::REFERRAL_CODE_PREFIX
      referrer = User.find_referrer_based_on_voucher_code(v.voucher_code)
      if referrer.blank?
        raise InvalidVoucher, 'Invalid referral code'
      else
        user.update! r_code: referrer.short_my_r_code
      end

      ref_form = VoucherForm::Referral.new(user_id: user.id, code: v.voucher_code)
      v.voucher_code = ref_form.ref_voucher_code
      v.save!
    elsif v.first_app_voucher?
      v.voucher_code = Voucher.generate_voucher_code
      v.save!
    end
  end
end

#do_transactionObject



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'app/services/voucher_service/apply.rb', line 85

def do_transaction
  ActiveRecord::Base.transaction do
    reservation.save!
    reservation.voucher_added_at = Time.zone.now
    reservation.vouchers.push(vouchers)
    reservation.reservation_vouchers.includes(:voucher).each do |rv|
      rv.redeemed_points = redeemed_points
      rv.adaptive_campaign_name = rv.voucher.adaptive_points_ratio&.campaign_name
      rv.amount = rv.voucher.amount
      rv.save
      # Update LimitPointsUsage record, to limit user to redeem points as per current limit points adjustment campaign
      # Check if voucher is redemption voucher & active & user is not a guest user
      if rv.redeemed_points.present? && rv.redeemed_points.positive? && rv.active && reservation.user_id.present?
        limit_points_usage = LimitPointsUsage.find_or_initialize_by(user_id: reservation.user_id)
        limit_points_adjustment_active = LimitPointsAdjustment.find_by(status: 1)
        defaults = LimitPointsAdjustment.column_defaults
        if limit_points_usage
          # if user record not exist in limit_points_usages table
          # (case when user don't have any active counter bcoz user didn't redeem points within user_limit_days),
          # then consider remaining_limit_points from limit point adjustment active campaign
          # if active campaign not exist, then consider from defaults
          remaining_limit_points = limit_points_usage.remaining_limit_points ||
            limit_points_adjustment_active&.user_limit_points ||
            defaults['user_limit_points']
          # if record exists in limit_points_usages table,
          # then keep same limit days since we have LimitPointsResetWorker to update limit days every night,
          # or else consider from limit point adjustment active campaign or defaults accordingly.
          remaining_limit_days = limit_points_usage.remaining_limit_days ||
            limit_points_adjustment_active&.user_limit_days ||
            defaults['user_limit_days']
          # calculate balance limit points after redemption
          balance_points = remaining_limit_points - redeemed_points
          remaining_limit_points_after_redemption = balance_points > 0 ? balance_points : 0
          # create/update limit points usage record to enforce limit points adjustment campaign for next time
          limit_points_usage.remaining_limit_points = remaining_limit_points_after_redemption
          limit_points_usage.remaining_limit_days = remaining_limit_days
          Retriable.retriable on: [ActiveRecord::Deadlocked], tries: 10 do
            limit_points_usage.save!
          end
        end
      end
    end
    reservation.save!

    return true if reservation.user_id.blank?

    user = reservation.user
    vouchers.each do |voucher|
      next if user.r_code.blank?
      next unless Voucher.valid_referral_code?(voucher.voucher_code)

      time = reservation.reservation_time.twenty_four_hours_later
      # used for staging tests — set time to 10 minutes from now if the feature flag is enabled
      if ENV['RAILS_ENV_REAL'] == 'staging' && Flipper.enabled?(:instant_referral_reward)
        time = 10.minutes.from_now
      end

      RewardWorkers::UserReferralReward.perform_in(time, reservation.id)
    end
  end

  merge_errors(reservation.errors) if reservation.errors.present?
end

#executeObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'app/services/voucher_service/apply.rb', line 15

def execute
  errors.clear

  verify_redeemed_points
  validate_vouchers

  create_unpersisted_vouchers if unpersisted_vouchers.present?

  if errors.blank?
    do_transaction
    ServiceResult.new data: reservation
  else
    ServiceResult.new errors: errors, message: error_message_simple
  end
rescue InvalidVoucher => e
  errors.add :base, e.message
  ServiceResult.new errors: errors, message: error_message_simple
end

#unpersisted_vouchersObject



81
82
83
# File 'app/services/voucher_service/apply.rb', line 81

def unpersisted_vouchers
  vouchers.reject(&:persisted?)
end

#validate_vouchersObject



36
37
38
39
40
41
42
43
44
45
46
# File 'app/services/voucher_service/apply.rb', line 36

def validate_vouchers
  delivery_fee = reservation.delivery? ? calculate[:delivery_fee] : 0
  skip_prepayment = actor == :admin || (calculate[:charge_price].zero? && calculate[:total_price].zero? && delivery_fee.zero?)

  # exclude unpersisted_vouchers validation
  (vouchers - unpersisted_vouchers).each do |v|
    validation = VoucherService::Validation.new(reservation, v, actor, payment_params,
                                                skip_prepayment: skip_prepayment)
    errors.add :base, validation.errors.full_messages.to_sentence unless validation.validate
  end
end