Module: SpreeCmCommissioner::LineItemDecorator

Defined in:
app/models/spree_cm_commissioner/line_item_decorator.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.include_modules(base) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 72

def self.include_modules(base)
  base.include Spree::Core::NumberGenerator.new(prefix: 'L')
  base.include SpreeCmCommissioner::StoreMetadata
  base.include SpreeCmCommissioner::LineItemDurationable
  base.include SpreeCmCommissioner::LineItemsFilterScope
  base.include SpreeCmCommissioner::LineItemGuestsConcern
  base.include SpreeCmCommissioner::LineItemTransitable
  base.include SpreeCmCommissioner::ProductType
  base.include SpreeCmCommissioner::ProductDelegation
  base.include SpreeCmCommissioner::KycBitwise
end

.pending_guests_queryObject



88
89
90
91
92
93
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 88

def self.pending_guests_query
  lambda {
    left_outer_joins(:id_card)
      .where(upload_later: true, id_card: { front_image: nil })
  }
end

.prepended(base) ⇒ Object

rubocop:disable Metrics/MethodLength,Metrics/AbcSize



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 3

def self.prepended(base) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
  include_modules(base)

  base.belongs_to :accepter, class_name: 'Spree::User', optional: true
  base.belongs_to :rejecter, class_name: 'Spree::User', optional: true
  base.belongs_to :event, class_name: 'Spree::Taxon', optional: true, inverse_of: :line_items

  base.has_one :google_wallet, class_name: 'SpreeCmCommissioner::GoogleWallet', through: :product

  base.has_many :option_types, through: :product

  base.has_many :inventory_items, lambda { |line_item|
    where(inventory_date: nil).or(where(inventory_date: line_item.date_range))
  }, through: :variant

  base.has_many :taxons, class_name: 'Spree::Taxon', through: :product
  base.has_many :guests, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
  base.has_many :guests_with_blocks, -> { where.not(block_id: nil) }, class_name: 'SpreeCmCommissioner::Guest'
  base.has_many :reserved_blocks, through: :guests, class_name: 'SpreeCmCommissioner::ReservedBlock'
  base.has_many :pending_guests, pending_guests_query, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
  base.has_many :product_completion_steps, class_name: 'SpreeCmCommissioner::ProductCompletionStep', through: :product

  base.has_many :saved_guests,
                through: :guests,
                source: :saved_guest,
                class_name: 'SpreeCmCommissioner::SavedGuest'

  base.before_validation -> { self.product_type = variant.product_type || product.product_type }, if: -> { product_type.nil? }

  base.before_save :update_vendor_id

  base.before_create :add_due_date, if: :subscription?

  base.validate :ensure_not_exceed_max_quantity_per_order, if: -> { variant&.max_quantity_per_order.present? }
  base.validate :ensure_all_guests_have_blocks, if: -> { variant&.blocks&.any? }

  base.whitelisted_ransackable_associations |= %w[guests order]
  base.whitelisted_ransackable_attributes |= %w[number to_date from_date vendor_id]

  base.delegate :delivery_required?, :high_demand,
                to: :variant

  base. :completion_steps, :array

  base.accepts_nested_attributes_for :guests, allow_destroy: true

  def base.json_api_columns
    json_api_columns = column_names.reject { |c| c.match(/_id$|id|preferences|(.*)password|(.*)token|(.*)api_key/) }
    json_api_columns << :options_text
    json_api_columns << :vendor_id
  end

  def discontinue_on
    variant.discontinue_on || product.discontinue_on
  end

  def base.search_by_qr_data!(data)
    matches = data.match(/(R\d+)-([A-Za-z0-9_\-]+)-(L\d+)/)&.captures

    raise ActiveRecord::RecordNotFound, "Couldn't find Spree::LineItem with QR data: #{data}" unless matches

    order_number, order_token, line_item_id = matches
    line_item_id = line_item_id.delete('L').to_i

    Spree::LineItem.joins(:order)
                   .find_by(id: line_item_id, spree_orders: { number: order_number, token: order_token })
  end
end

Instance Method Details

#accepted?Boolean

Returns:

  • (Boolean)


146
147
148
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 146

def accepted?
  accepted_at.present?
end

#accepted_by(user) ⇒ Object



154
155
156
157
158
159
160
161
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 154

def accepted_by(user)
  return if accepted_at.present? && accepter_id.present?

  update(
    accepted_at: Time.current,
    accepter: user
  )
end

#allowed_self_check_in?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 130

def allowed_self_check_in?
  allow_self_check_in?
end

#amount_per_guestObject



138
139
140
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 138

def amount_per_guest
  amount / quantity
end

#commission_rateObject



142
143
144
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 142

def commission_rate
  product.commission_rate || vendor&.commission_rate || 0
end

#discontinue_onObject



55
56
57
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 55

def discontinue_on
  variant.discontinue_on || product.discontinue_on
end

#generate_completion_stepsObject



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 206

def generate_completion_steps
  generate_completion_steps!
  true
rescue StandardError => e
  CmAppLogger.error(
    label: "#{self.class.name}#generate_completion_steps",
    data: {
      line_item_id: id,
      order_id: order_id,
      error_class: e.class.name,
      error_message: e.message,
      backtrace: e.backtrace&.first(10)&.join("\n")
    }
  )
  errors.add(:completion_steps, e.message)
  false
end

#generate_completion_steps!Object



224
225
226
227
228
229
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 224

def generate_completion_steps!
  self.completion_steps = product_completion_steps.map do |completion_step|
    completion_step.construct_hash(line_item: self)
  end
  save!
end

#generate_png_qr(size = 120) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 184

def generate_png_qr(size = 120)
  qrcode = RQRCode::QRCode.new(qr_data)
  qrcode.as_png(
    bit_depth: 1,
    border_modules: 1,
    color_mode: ChunkyPNG::COLOR_GRAYSCALE,
    color: 'black',
    file: nil,
    fill: 'white',
    module_px_size: 4,
    resize_exactly_to: false,
    resize_gte_to: false,
    size: size
  )
end

#generate_svg_qrObject



172
173
174
175
176
177
178
179
180
181
182
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 172

def generate_svg_qr
  qrcode = RQRCode::QRCode.new(qr_data)
  qrcode.as_svg(
    color: '000',
    shape_rendering: 'crispEdges',
    module_size: 5,
    standalone: true,
    use_path: true,
    viewbox: '0 0 20 10'
  )
end

#jwt_tokenObject



240
241
242
243
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 240

def jwt_token
  payload = { order_number: order.number, line_item_id: id }
  SpreeCmCommissioner::OrderJwtToken.encode(payload, order.token)
end

#kycObject



84
85
86
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 84

def kyc
  variant.kyc || product.kyc
end

#monthObject



236
237
238
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 236

def month
  to_date.strftime('%B %Y')
end

#qr_dataObject



200
201
202
203
204
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 200

def qr_data
  return nil if order.nil?

  "#{order.number}-#{order.token}-L#{id}"
end

#rejected?Boolean

Returns:

  • (Boolean)


150
151
152
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 150

def rejected?
  rejected_at.present?
end

#rejected_by(user) ⇒ Object



163
164
165
166
167
168
169
170
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 163

def rejected_by(user)
  return if rejected_at.present? && rejecter_id.present?

  update(
    rejected_at: Time.current,
    rejecter: user
  )
end

#required_self_check_in_location?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 134

def required_self_check_in_location?
  required_self_check_in_location
end

#sufficient_stock?Boolean

override

Returns:

  • (Boolean)


232
233
234
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 232

def sufficient_stock?
  SpreeCmCommissioner::Stock::LineItemAvailabilityChecker.new(self).can_supply?(quantity)
end

#update_priceObject

override to calculate price per date for permanent stock



118
119
120
121
122
123
124
125
126
127
128
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 118

def update_price
  base_price = variant.price_in(order.currency)
  self.price = if permanent_stock?
                 date_range.map do |date|
                   selected_price = variant.price_for_date(date: date, currency: order.currency) || base_price
                   selected_price.amount || 0
                 end.sum
               else
                 base_price.amount || 0
               end
end

#update_price_from_modifier(currency, opts) ⇒ Object

override to calculate price per date for permanent stock



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'app/models/spree_cm_commissioner/line_item_decorator.rb', line 96

def update_price_from_modifier(currency, opts)
  self.currency = currency

  # these fields can be null during this process below.
  # it is needed for permanent_stock?, date_range to work.
  self.product_type = variant.product_type
  self.from_date = opts.delete(:from_date)&.to_datetime
  self.to_date = opts.delete(:to_date)&.to_datetime

  base_price = variant.price_in(currency)

  self.price = if permanent_stock?
                 date_range.map do |date|
                   selected_price = variant.price_for_date(date: date, currency: currency) || base_price
                   (selected_price.amount || 0) + variant.price_modifier_amount_in(currency, opts.merge(date: date))
                 end.sum
               else
                 (base_price.amount || 0) + variant.price_modifier_amount_in(currency, opts)
               end
end