Module: SpreeCmCommissioner::OrderStateMachine
- Extended by:
- ActiveSupport::Concern
- Defined in:
- app/models/concerns/spree_cm_commissioner/order_state_machine.rb
Overview
rubocop:disable Metrics/ModuleLength
Instance Method Summary collapse
-
#accepted_by(user) ⇒ Object
allow authorized user to accept all requested line items.
- #allocate_resources ⇒ Object
-
#can_accept_all? ⇒ Boolean
can_accepted? already use by ransack/visitor.rb.
- #can_alert_request_to_vendor? ⇒ Boolean
- #can_reject_all? ⇒ Boolean
-
#clear_cancellation_details ⇒ Object
Reset the cancellation info when an order is resumed.
- #de_allocate_resources ⇒ Object
- #decrement_user_request_orders_count ⇒ Object
-
#deliver_order_confirmation_email ⇒ Object
override.
- #finalize_guests_and_bibs ⇒ Object
-
#generate_completion_steps! ⇒ Object
This is called before complete, which is already wrapped by the transaction of unstock_inventory!.
- #increment_user_request_orders_count ⇒ Object
- #need_confirmation? ⇒ Boolean
- #notify_order_complete_app_notification_to_user ⇒ Object
- #notify_order_complete_telegram_notification_to_user ⇒ Object
- #precalculate_conversion ⇒ Object
- #product_completion_steps_exist? ⇒ Boolean
- #rejected_by(user) ⇒ Object
- #requested_state? ⇒ Boolean
- #restock_inventory! ⇒ Object
- #run_order_complete_alerts ⇒ Object
- #send_dynamic_field_notification_to_guests ⇒ Object
- #send_order_accepted_app_notification_to_user ⇒ Object
- #send_order_accepted_telegram_alert_to_store ⇒ Object
- #send_order_complete_telegram_alert_to_store ⇒ Object
- #send_order_complete_telegram_alert_to_vendors ⇒ Object
- #send_order_rejected_app_notification_to_user ⇒ Object
- #send_order_rejected_telegram_alert_to_store ⇒ Object
- #send_order_request_telegram_confirmation_alert_to_vendor ⇒ Object
- #send_order_requested_app_notification_to_user ⇒ Object
- #send_order_requested_telegram_alert_to_store ⇒ Object
- #send_transaction_email_to_user ⇒ Object
-
#telegram_alert_enabled? ⇒ Boolean
Telegram order alerts are sent unless EVERY purchased product opts out.
- #unstock_inventory! ⇒ Object
- #user_has_email? ⇒ Boolean
-
#vendor_alert_title(vendor) ⇒ Object
White-label vendors announce their own brand; platform vendors stay BookMe+.
Instance Method Details
#accepted_by(user) ⇒ Object
allow authorized user to accept all requested line items
144 145 146 147 148 149 150 151 152 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 144 def accepted_by(user) transaction do accept! line_items.each do |line_item| line_item.accepted_by(user) end end end |
#allocate_resources ⇒ Object
388 389 390 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 388 def allocate_resources SpreeCmCommissioner::VotingCreditAllocationJob.perform_later(order_id: id) end |
#can_accept_all? ⇒ Boolean
can_accepted? already use by ransack/visitor.rb
250 251 252 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 250 def can_accept_all? approved? && requested? end |
#can_alert_request_to_vendor? ⇒ Boolean
258 259 260 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 258 def can_alert_request_to_vendor? !accepted? && approved? && need_confirmation? end |
#can_reject_all? ⇒ Boolean
254 255 256 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 254 def can_reject_all? approved? && requested? end |
#clear_cancellation_details ⇒ Object
Reset the cancellation info when an order is resumed. We only assign the values here; the resume transition already saves the order, so they get saved together with it (one validated save, no update_columns).
399 400 401 402 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 399 def clear_cancellation_details self.canceled_at = nil self.canceler_id = nil end |
#de_allocate_resources ⇒ Object
392 393 394 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 392 def de_allocate_resources SpreeCmCommissioner::VotingCreditDeAllocationJob.perform_later(order_id: id) end |
#decrement_user_request_orders_count ⇒ Object
117 118 119 120 121 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 117 def decrement_user_request_orders_count return if user.nil? SpreeCmCommissioner::UserCountersService.decrement_request_orders_count(user) end |
#deliver_order_confirmation_email ⇒ Object
override
104 105 106 107 108 109 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 104 def deliver_order_confirmation_email return if requested? return if any_app_only_products? super end |
#finalize_guests_and_bibs ⇒ Object
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 196 def finalize_guests_and_bibs ActiveRecord::Base.transaction do line_items.each do |line_item| line_item.generate_remaining_guests line_item.guests.each do |guest| result = SpreeCmCommissioner::Guests::Finalize.call( guest: guest, user: user, event: line_item.event, variant: line_item.variant ) unless result.success? guest.errors.add(:base, result.error.value || result.error.to_s) raise(ActiveRecord::RecordInvalid, guest) end end end end true rescue ActiveRecord::RecordInvalid => e e.record.errors.each do |msg| errors.add(:guests, msg) end false end |
#generate_completion_steps! ⇒ Object
This is called before complete, which is already wrapped by the transaction of unstock_inventory!
128 129 130 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 128 def generate_completion_steps! line_items.each(&:generate_completion_steps!) end |
#increment_user_request_orders_count ⇒ Object
111 112 113 114 115 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 111 def increment_user_request_orders_count return if user.nil? SpreeCmCommissioner::UserCountersService.increment_request_orders_count(user) end |
#need_confirmation? ⇒ Boolean
266 267 268 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 266 def need_confirmation? line_items.any?(&:need_confirmation) end |
#notify_order_complete_app_notification_to_user ⇒ Object
318 319 320 321 322 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 318 def notify_order_complete_app_notification_to_user return if products.all?(&:vote_package?) # disable app push notification for vote package orders SpreeCmCommissioner::OrderCompleteNotificationSender.call(order: self) end |
#notify_order_complete_telegram_notification_to_user ⇒ Object
324 325 326 327 328 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 324 def notify_order_complete_telegram_notification_to_user return unless telegram_alert_enabled? SpreeCmCommissioner::OrderCompleteTelegramSenderJob.perform_later(order_id: id) if user_id.present? end |
#precalculate_conversion ⇒ Object
132 133 134 135 136 137 138 139 140 141 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 132 def precalculate_conversion event_ids = line_items.pluck(:event_id).compact.uniq event_ids.each do |event_id| # Just create task, CRON job will pick it up and execute. SpreeCmCommissioner::MaintenanceTasks::Event.pending.create_or_find_by( maintainable_type: 'Spree::Taxon', maintainable_id: event_id ) end end |
#product_completion_steps_exist? ⇒ Boolean
123 124 125 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 123 def product_completion_steps_exist? product_completion_steps.any? end |
#rejected_by(user) ⇒ Object
226 227 228 229 230 231 232 233 234 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 226 def rejected_by(user) transaction do reject! line_items.each do |line_item| line_item.rejected_by(user) end end end |
#requested_state? ⇒ Boolean
262 263 264 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 262 def requested_state? request_state == 'requested' end |
#restock_inventory! ⇒ Object
181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 181 def restock_inventory! ActiveRecord::Base.transaction do restock_inventory_from_external_system! release_order_holds! restock_inventory_in_redis! end rescue StandardError => e CmAppLogger.log( label: 'SpreeCmCommissioner::OrderStateMachine#restock_inventory! failed', data: { order_id: id, error: e., type: e.class.name, backtrace: e.backtrace&.first(5)&.join("\n") } ) raise e end |
#run_order_complete_alerts ⇒ Object
369 370 371 372 373 374 375 376 377 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 369 def run_order_complete_alerts return unless ENV['ORDER_INTEGRITY_TELEGRAM_ALERT_ENABLED'] == 'yes' return unless telegram_alert_enabled? # Allow async payment processors time to settle before checking for alerts SpreeCmCommissioner::TelegramAlerts::OrderIntegrityCheckJob .set(wait: 2.minutes) .perform_later(order_id: id) end |
#send_dynamic_field_notification_to_guests ⇒ Object
281 282 283 284 285 286 287 288 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 281 def send_dynamic_field_notification_to_guests incomplete_guests = line_items.flat_map(&:guests).select(&:incomplete?) return if incomplete_guests.empty? incomplete_guests.each do |guest| SpreeCmCommissioner::Guests::SendDynamicFieldNotification.call(guest: guest) end end |
#send_order_accepted_app_notification_to_user ⇒ Object
334 335 336 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 334 def send_order_accepted_app_notification_to_user SpreeCmCommissioner::OrderAcceptedNotificationSender.call(order: self) end |
#send_order_accepted_telegram_alert_to_store ⇒ Object
351 352 353 354 355 356 357 358 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 351 def send_order_accepted_telegram_alert_to_store return unless telegram_alert_enabled? title = '✅ --- [ORDER ACCEPTED BY VENDOR] ---' chat_id = store.preferred_telegram_order_request_alert_chat_id factory = OrderTelegramMessageFactory.new(title: title, order: self) TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory., parse_mode: factory.parse_mode) end |
#send_order_complete_telegram_alert_to_store ⇒ Object
379 380 381 382 383 384 385 386 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 379 def send_order_complete_telegram_alert_to_store return unless telegram_alert_enabled? title = '🎫 --- [NEW ORDER] ---' chat_id = store.preferred_telegram_order_alert_chat_id factory = OrderTelegramMessageFactory.new(title: title, order: self) TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory., parse_mode: factory.parse_mode) end |
#send_order_complete_telegram_alert_to_vendors ⇒ Object
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 290 def send_order_complete_telegram_alert_to_vendors return unless telegram_alert_enabled? vendor_list.each do |vendor| # `next`, not `return`: the mask is per-vendor, so one opted-out vendor on a # multi-vendor order must not suppress the others. next unless vendor.alerts_telegram_for_channel?(channel_key) chat_id = vendor.preferred_telegram_chat_id next if chat_id.blank? factory = OrderTelegramMessageFactory.new(title: vendor_alert_title(vendor), order: self, vendor: vendor) TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory., parse_mode: factory.parse_mode) end end |
#send_order_rejected_app_notification_to_user ⇒ Object
338 339 340 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 338 def send_order_rejected_app_notification_to_user SpreeCmCommissioner::OrderRejectedNotificationSender.call(order: self) end |
#send_order_rejected_telegram_alert_to_store ⇒ Object
360 361 362 363 364 365 366 367 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 360 def send_order_rejected_telegram_alert_to_store return unless telegram_alert_enabled? title = '❌ --- [ORDER REJECTED BY VENDOR] ---' chat_id = store.preferred_telegram_order_request_alert_chat_id factory = OrderTelegramMessageFactory.new(title: title, order: self) TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory., parse_mode: factory.parse_mode) end |
#send_order_request_telegram_confirmation_alert_to_vendor ⇒ Object
270 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 270 def send_order_request_telegram_confirmation_alert_to_vendor; end |
#send_order_requested_app_notification_to_user ⇒ Object
330 331 332 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 330 def send_order_requested_app_notification_to_user SpreeCmCommissioner::OrderRequestedNotificationSender.call(order: self) end |
#send_order_requested_telegram_alert_to_store ⇒ Object
342 343 344 345 346 347 348 349 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 342 def send_order_requested_telegram_alert_to_store return unless telegram_alert_enabled? title = '🔔 --- [NEW REQUESTED BY USER] ---' chat_id = store.preferred_telegram_order_request_alert_chat_id factory = OrderTelegramMessageFactory.new(title: title, order: self) TelegramNotificationSenderJob.perform_later(chat_id: chat_id, message: factory., parse_mode: factory.parse_mode) end |
#send_transaction_email_to_user ⇒ Object
236 237 238 239 240 241 242 243 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 236 def send_transaction_email_to_user line_items.each do |line_item| next if line_item.event.nil? next unless line_item. SpreeCmCommissioner::EventTransactionalMailer.send_to_participant(line_item.event_id, user.id).deliver_later end end |
#telegram_alert_enabled? ⇒ Boolean
Telegram order alerts are sent unless EVERY purchased product opts out. Default is true (see product_decorator), so existing orders are unaffected. Checked at enqueue time so disabled products create no telegram jobs at all.
275 276 277 278 279 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 275 def telegram_alert_enabled? return @telegram_alert_enabled if defined?(@telegram_alert_enabled) @telegram_alert_enabled = line_items.any?(&:enable_telegram_alert?) end |
#unstock_inventory! ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 154 def unstock_inventory! ActiveRecord::Base.transaction do yield if block_given? # Equal to block.call # After the transition is complete, the following code will execute first before proceeding to other `after_transition` callbacks. # This ensures that if `reserve_order_holds!` or `unstock_inventory_in_redis!` fails, the state will be rolled back, # and neither the `finalize!` method nor any notifications will be triggered. # The payment will be reversed in vPago gem, and `Spree::Checkout::Complete` will be called, which checks `order.reload.complete?`. # This is critical because if the order state is complete, the payment will be marked as paid. unstock_inventory_from_external_system! reserve_order_holds! unstock_inventory_in_redis! # We rollback only order state, and we keep payment state as it is. # We implement payment in vPago gem, and it will be reversed in the gem. # Some bank has api for refund, but some don't have the api to refund yet. So we keep the payment state as it is and refund manually. end rescue StandardError => e CmAppLogger.log( label: 'SpreeCmCommissioner::OrderStateMachine#unstock_inventory! failed', data: { order_id: id, error: e., type: e.class.name, backtrace: e.backtrace&.first(5)&.join("\n") } ) raise e end |
#user_has_email? ⇒ Boolean
245 246 247 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 245 def user_has_email? user.present? && user.email.present? end |
#vendor_alert_title(vendor) ⇒ Object
White-label vendors announce their own brand; platform vendors stay BookMe+.
vendor.name is operator-supplied data interpolated into an HTML parse_mode message (TelegramMessageFactory#header wraps the title in ), so it MUST be escaped — Telegram rejects unescaped &, < and > with "400 can't parse entities", and TelegramNotificationSender swallows that error, so the alert would vanish silently.
312 313 314 315 316 |
# File 'app/models/concerns/spree_cm_commissioner/order_state_machine.rb', line 312 def vendor_alert_title(vendor) return '🎫 --- [NEW ORDER FROM BOOKME+] ---' unless vendor.tenant_present? "🎫 --- [NEW ORDER FROM #{CGI.escapeHTML(vendor.name.to_s)}] ---" end |