Module: SpreeCmCommissioner::OrderDecorator
- Defined in:
- app/models/spree_cm_commissioner/order_decorator.rb
Class Method Summary collapse
-
.prepended(base) ⇒ Object
rubocop:disable Metrics/MethodLength,Metrics/AbcSize.
Instance Method Summary collapse
- #any_app_only_products? ⇒ Boolean
-
#associate_user!(user, override_email = true) ⇒ Object
overrided add phone_number trigger when update customer detail.
-
#canceled_by(user, cancellation_reason: nil) ⇒ Object
override to allow storing cancellation reason in one DB write.
-
#clear_has_incomplete_guest_info_cache ⇒ Object
Clears this order's own cache plus the purchaser's cross-order aggregate cache — anything that can change the former can also change the latter.
-
#collect_payment_methods(store = nil) ⇒ Object
override.
- #collect_payment_methods_for_early_adopter(store = nil) ⇒ Object
-
#connected_line_item_ids(direction: nil) ⇒ Object
Returns arrays of connected line_item IDs grouped by connected_trip_id.
-
#create_default_payment_if_eligble ⇒ Object
assume check is default payment method for subscription.
-
#create_service_fee_charge! ⇒ Object
Mirrors create_tax_charge! (core/app/models/spree/order.rb) — wired into the same checkout transitions via SpreeCmCommissioner::OrderStateMachine, plus called directly from TransitOrder::Create and DraftOrders::Create, which build orders outside the checkout state machine entirely.
- #customer_address ⇒ Object
-
#delivery_required? ⇒ Boolean
override.
- #display_outstanding_balance ⇒ Object
- #enable_send_receipt? ⇒ Boolean
- #guests_in_transfer? ⇒ Boolean
-
#has_incomplete_guest_info? ⇒ Boolean
rubocop:disable Naming/PredicateName.
- #incomplete_guest_info_cache_key ⇒ Object
-
#insufficient_stock_lines ⇒ Object
override spree use this method to check stock availability & consider whether :order can continue to next state.
- #mark_as_archive ⇒ Object
- #order_completed? ⇒ Boolean
- #payment_fulfilled? ⇒ Boolean
-
#payment_host ⇒ Object
override spree_vpago method.
-
#payment_required? ⇒ Boolean
overrided.
-
#purchased_from_tenant? ⇒ Boolean
Returns true when the order was created under a tenant context.
-
#restart_checkout_flow ⇒ Object
override spree_core behavior to intentionally avoid calling
next!. -
#send_cancel_email ⇒ Object
override: imported orders are bulk-loaded from CSV and the contact email often belongs to a third party (organizer, importer) rather than the buyer, so the cancellation email would notify the wrong recipient.
- #subscription? ⇒ Boolean
- #ticket_seller_user? ⇒ Boolean
-
#valid_promotion_ids ⇒ Object
overrided avoid raise error when source_id is nil.
Class Method Details
.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 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 3 def self.prepended(base) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize base.include SpreeCmCommissioner::OrderChannelBitwise base.include SpreeCmCommissioner::StoreMetadata base.include SpreeCmCommissioner::PhoneNumberSanitizer base.include SpreeCmCommissioner::OrderIntegration base.include SpreeCmCommissioner::OrderHoldable base.include SpreeCmCommissioner::OrderStateMachine base.include SpreeCmCommissioner::RouteOrderCountable base.include SpreeCmCommissioner::OrderScopes base.include SpreeCmCommissioner::TicketTransferable base.include SpreeCmCommissioner::OrderQrCodeable base.before_create :link_by_phone_number base.before_create :associate_customer base.before_create :set_tenant_id base.after_commit :increment_route_order_count, on: :create base. :preload_trip_ids, :array, default: [] base. :preload_main_trip_ids, :array, default: [] base. :order_options, :array, default: [] base.money_methods :service_fee_total base.validates :promo_total, base::MONEY_VALIDATION base.validates :phone_number, presence: true, if: :require_phone_number base.has_one :invoice, dependent: :destroy, class_name: 'SpreeCmCommissioner::Invoice' base.has_one :customer, class_name: 'SpreeCmCommissioner::Customer', through: :subscription base.has_one :imported_order, class_name: 'SpreeCmCommissioner::ImportedOrder', dependent: :destroy, inverse_of: :order base.belongs_to :tenant, class_name: 'SpreeCmCommissioner::Tenant', optional: true base.belongs_to :subscription, class_name: 'SpreeCmCommissioner::Subscription', optional: true base.has_many :taxons, class_name: 'Spree::Taxon', through: :customer base.has_many :vendors, through: :products, class_name: 'Spree::Vendor' base.has_many :taxons, through: :products, class_name: 'Spree::Taxon' base.has_many :guests, through: :line_items, class_name: 'SpreeCmCommissioner::Guest' base.has_many :saved_guests, -> { reorder(nil).distinct }, through: :guests, source: :saved_guest, class_name: 'SpreeCmCommissioner::SavedGuest' base.has_many :blocks, through: :guests, class_name: 'SpreeCmCommissioner::Block', source: :block base.has_many :reserved_blocks, through: :guests, class_name: 'SpreeCmCommissioner::ReservedBlock' base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variants base.has_many :product_completion_steps, class_name: 'SpreeCmCommissioner::ProductCompletionStep', through: :line_items base.delegate :customer, to: :user, allow_nil: true base.whitelisted_ransackable_associations |= %w[customer taxon payments guests invoice] base.whitelisted_ransackable_attributes |= %w[intel_phone_number phone_number email number state] base.accepts_nested_attributes_for :saved_guests, allow_destroy: true def base.search_by_qr_data!(data) token = data.match(/^R\d{9,}-([A-Za-z0-9_\-]+)$/)&.captures raise ActiveRecord::RecordNotFound, "Couldn't find Spree::Order with QR data: #{data}" unless token find_by!(token: token) end end |
Instance Method Details
#any_app_only_products? ⇒ Boolean
176 177 178 179 180 181 182 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 176 def any_app_only_products? if products.loaded? products.any?(&:purchasable_on_app?) else products.exists?(purchasable_on: :app) end end |
#associate_user!(user, override_email = true) ⇒ Object
overrided add phone_number trigger when update customer detail
187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 187 def associate_user!(user, override_email = true) # rubocop:disable Style/OptionalBooleanParameter self.user = user self.email = user.email if override_email self.phone_number = user.phone_number if user.phone_number.present? self.created_by ||= user self.bill_address ||= user.bill_address.try(:clone) self.ship_address ||= user.ship_address.try(:clone) changes = slice(:user_id, :email, :phone_number, :created_by_id, :bill_address_id, :ship_address_id) self.class.unscoped.where(id: self).update_all(changes) # rubocop:disable Rails/SkipsModelValidations end |
#canceled_by(user, cancellation_reason: nil) ⇒ Object
override to allow storing cancellation reason in one DB write
72 73 74 75 76 77 78 79 80 81 82 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 72 def canceled_by(user, cancellation_reason: nil) transaction do cancel! update_columns( # rubocop:disable Rails/SkipsModelValidations canceler_id: user.id, canceled_at: Time.current, internal_note: cancellation_reason ) end end |
#clear_has_incomplete_guest_info_cache ⇒ Object
Clears this order's own cache plus the purchaser's cross-order aggregate cache — anything that can change the former can also change the latter.
284 285 286 287 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 284 def clear_has_incomplete_guest_info_cache Rails.cache.delete(incomplete_guest_info_cache_key) user&.clear_has_incomplete_guest_info_cache end |
#collect_payment_methods(store = nil) ⇒ Object
override
144 145 146 147 148 149 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 144 def collect_payment_methods(store = nil) return super if user.blank? return super unless user.early_adopter? collect_payment_methods_for_early_adopter(store) end |
#collect_payment_methods_for_early_adopter(store = nil) ⇒ Object
151 152 153 154 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 151 def collect_payment_methods_for_early_adopter(store = nil) store ||= self.store store.payment_methods.available_on_frontend_for_early_adopter.select { |pm| pm.available_for_order?(self) } end |
#connected_line_item_ids(direction: nil) ⇒ Object
Returns arrays of connected line_item IDs grouped by connected_trip_id. Example: [[1,2,3], [4,5], [6]]
Line items with the same connected_trip_id are grouped together. Line items without a group_id are returned as singletons.
311 312 313 314 315 316 317 318 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 311 def connected_line_item_ids(direction: nil) scoped = line_items scoped = scoped.select { |li| li.direction == direction } if direction.present? scoped.group_by { |li| li.connected_trip_id || li.id } .values .map { |group| group.map(&:id).sort } end |
#create_default_payment_if_eligble ⇒ Object
assume check is default payment method for subscription
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 221 def create_default_payment_if_eligble return unless subscription? if covered_by_store_credit payment_method = Spree::PaymentMethod::StoreCredit.available_on_back_end.first_or_create! do |method| method.name ||= 'StoreCredit' method.stores = [Spree::Store.default] if method.stores.empty? end source_id = user.store_credit_ids.last source_type = 'Spree::StoreCredit' else payment_method = Spree::PaymentMethod::Check.available_on_back_end.first_or_create! do |method| method.name ||= 'Invoice' method.stores = [Spree::Store.default] if method.stores.empty? end end payments.create!( payment_method: payment_method, amount: total, source_id: source_id, source_type: source_type ) Spree::Checkout::Advance.call(order: self) end |
#create_service_fee_charge! ⇒ Object
Mirrors create_tax_charge! (core/app/models/spree/order.rb) — wired into the same checkout transitions via SpreeCmCommissioner::OrderStateMachine, plus called directly from TransitOrder::Create and DraftOrders::Create, which build orders outside the checkout state machine entirely.
165 166 167 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 165 def create_service_fee_charge! SpreeCmCommissioner::ServiceFeeRule.adjust(self, line_items) end |
#customer_address ⇒ Object
255 256 257 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 255 def customer_address bill_address || ship_address end |
#delivery_required? ⇒ Boolean
override
157 158 159 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 157 def delivery_required? line_items.any?(&:delivery_required?) end |
#display_outstanding_balance ⇒ Object
297 298 299 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 297 def display_outstanding_balance Spree::Money.new(outstanding_balance, currency: currency).to_s end |
#enable_send_receipt? ⇒ Boolean
293 294 295 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 293 def enable_send_receipt? products.any?(&:enable_send_receipt?) end |
#guests_in_transfer? ⇒ Boolean
263 264 265 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 263 def guests_in_transfer? guests.exists?(state: i[locked transferred]) end |
#has_incomplete_guest_info? ⇒ Boolean
rubocop:disable Naming/PredicateName
267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 267 def has_incomplete_guest_info? # rubocop:disable Naming/PredicateName # expires_in is a safety net, not the primary invalidation path — clear_has_incomplete_guest_info_cache # is still called explicitly wherever the underlying guest data can change. This just bounds # how long a missed invalidation could leave a stale value in place. # # Reads the persisted data_fill_stage column via a join instead of Guest#incomplete? (which # recomputes live per guest). post_registration is the only stage that matters here: a # completed order already forces pre_registration complete (guest_data_fill_stage_concern.rb), # and during_check_in is check-in crew's concern, not something this banner surfaces — # matches find_orders_with_incomplete_guest_info in orders_controller_decorator.rb. Rails.cache.fetch(incomplete_guest_info_cache_key, expires_in: 1.hour) do guests.post_registration.exists? end end |
#incomplete_guest_info_cache_key ⇒ Object
289 290 291 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 289 def incomplete_guest_info_cache_key "order/#{id}/has_incomplete_guest_info" end |
#insufficient_stock_lines ⇒ Object
override spree use this method to check stock availability & consider whether :order can continue to next state.
132 133 134 135 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 132 def insufficient_stock_lines checker = SpreeCmCommissioner::Stock::OrderAvailabilityChecker.new(self) checker.insufficient_stock_lines end |
#mark_as_archive ⇒ Object
200 201 202 203 204 205 206 207 208 209 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 200 def mark_as_archive released = begin release_order_holds! true rescue StandardError false end update(archived_at: Time.current) if released end |
#order_completed? ⇒ Boolean
259 260 261 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 259 def order_completed? complete? && need_confirmation? == false end |
#payment_fulfilled? ⇒ Boolean
126 127 128 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 126 def payment_fulfilled? payment_state.present? && payment_state == 'paid' end |
#payment_host ⇒ Object
override spree_vpago method
302 303 304 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 302 def payment_host tenant&.payment_url.presence || tenant&.formatted_url.presence || ENV.fetch('PAYMENT_BASE_URL') end |
#payment_required? ⇒ Boolean
overrided
170 171 172 173 174 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 170 def payment_required? return false if need_confirmation? super end |
#purchased_from_tenant? ⇒ Boolean
Returns true when the order was created under a tenant context.
251 252 253 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 251 def purchased_from_tenant? !tenant_id.nil? end |
#restart_checkout_flow ⇒ Object
override spree_core behavior to intentionally avoid calling next!.
Goal: keep the order state at 'cart' when restarting checkout, especially when seats were held during the address step.
Flow summary:
- User goes from cart -> address: we hold seats.
- User navigates back from address: we call this method to cancel the holds.
We do NOT call
next!here; otherwise the order state machine would triggerhold_order_holdsagain and re-hold seats unnecessarily.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 104 def restart_checkout_flow ActiveRecord::Base.transaction do release_order_holds! log_state_changes(state_name: 'cart', old_state: state, new_state: 'cart') update_columns( # rubocop:disable Rails/SkipsModelValidations state: 'cart', updated_at: Time.current ) end rescue StandardError => e CmAppLogger.error( label: 'SpreeCmCommissioner::OrderDecorator#restart_checkout_flow failed', data: { order_id: id, error_class: e.class.name, error_message: e., backtrace: e.backtrace&.first(5)&.join("\n") } ) raise end |
#send_cancel_email ⇒ Object
override: imported orders are bulk-loaded from CSV and the contact email often belongs to a third party (organizer, importer) rather than the buyer, so the cancellation email would notify the wrong recipient. Normal orders still receive the cancel email via the spree_emails decorator.
88 89 90 91 92 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 88 def send_cancel_email return if imported_order.present? super end |
#subscription? ⇒ Boolean
246 247 248 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 246 def subscription? subscription.present? end |
#ticket_seller_user? ⇒ Boolean
137 138 139 140 141 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 137 def ticket_seller_user? return false if user.nil? user.has_spree_role?('ticket_seller') end |
#valid_promotion_ids ⇒ Object
overrided avoid raise error when source_id is nil. https://github.com/channainfo/commissioner/pull/843
214 215 216 217 218 |
# File 'app/models/spree_cm_commissioner/order_decorator.rb', line 214 def valid_promotion_ids all_adjustments.eligible.nonzero.promotion .where.not(source_id: nil) .map { |a| a.source.promotion_id }.uniq end |