Class: Dscf::Marketplace::Order
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Dscf::Marketplace::Order
- Defined in:
- app/models/dscf/marketplace/order.rb
Class Method Summary collapse
- .create_from_listing(listing, user, quantity, dropoff_address = nil, payment_method = nil) ⇒ Object
- .create_from_quotation(quotation, dropoff_address = nil, payment_method = nil) ⇒ Object
- .ransackable_associations(_auth_object = nil) ⇒ Object
-
.ransackable_attributes(_auth_object = nil) ⇒ Object
Ransack configuration for secure filtering.
Instance Method Summary collapse
-
#agent_assisted? ⇒ Boolean
An agent-assisted order (agent_id present) must have its OTP verified by the retailer before it can enter the normal validate/confirm pipeline — this gate is intentionally orthogonal to
status, which already has an unrelatedwaiting_retailer_confirmationmeaning (the retailer signing off on supplier-proposed adjustments mid-fulfillment). - #all_items_validated? ⇒ Boolean
-
#awaiting_validation? ⇒ Boolean
Workflow helpers for Sprint 2 order validation + splitting.
-
#billable_order_items ⇒ Object
Lines that should count toward the order total / retailer-facing amount.
- #calculate_total_amount ⇒ Object
- #can_be_completed? ⇒ Boolean
- #confirm! ⇒ Object
- #delivery? ⇒ Boolean
- #mark_splitting! ⇒ Object
- #mark_waiting_retailer_confirmation! ⇒ Object
- #mark_waiting_supplier_confirmation! ⇒ Object
- #otp_verified? ⇒ Boolean
- #requires_delivery_order? ⇒ Boolean
- #retailer_can_confirm? ⇒ Boolean
-
#self_pickup? ⇒ Boolean
Fulfillment type methods.
- #supplier ⇒ Object
- #supplier_confirmation_complete? ⇒ Boolean
- #total_amount ⇒ Object
- #validation_summary ⇒ Object
Class Method Details
.create_from_listing(listing, user, quantity, dropoff_address = nil, payment_method = nil) ⇒ Object
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 |
# File 'app/models/dscf/marketplace/order.rb', line 92 def self.create_from_listing(listing, user, quantity, dropoff_address = nil, payment_method = nil) return nil unless listing.visible? && quantity <= listing.quantity attributes = { order_type: :direct_listing, status: :validating, fulfillment_type: dropoff_address.present? ? :delivery : :self_pickup, listing: listing, user: user, # Keep for backward compatibility ordered_by: user, ordered_to: listing.business, dropoff_address: dropoff_address, total_amount: listing.price * quantity } attributes[:payment_method] = payment_method if payment_method.present? order = create!(attributes) order.order_items.create!( listing: listing, product: listing.supplier_product.product, unit: listing.supplier_product.product.unit, quantity: quantity, unit_price: listing.price, status: :pending, validation_status: :validated ) order end |
.create_from_quotation(quotation, dropoff_address = nil, payment_method = nil) ⇒ Object
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 |
# File 'app/models/dscf/marketplace/order.rb', line 58 def self.create_from_quotation(quotation, dropoff_address = nil, payment_method = nil) return nil unless quotation.accepted? attributes = { order_type: :rfq_based, status: :validating, fulfillment_type: dropoff_address.present? ? :delivery : :self_pickup, quotation: quotation, user: quotation.request_for_quotation.user, # Keep for backward compatibility ordered_by: quotation.request_for_quotation.user, ordered_to: quotation.business, dropoff_address: dropoff_address, total_amount: quotation.total_price } attributes[:payment_method] = payment_method if payment_method.present? order = create!(attributes) quotation.quotation_items.each do |item| order.order_items.create!( quotation_item: item, product: item.product, unit: item.unit, quantity: item.quantity, unit_price: item.unit_price, status: :pending, validation_status: :validated # initial from accepted quote; validation can re-run ) end order end |
.ransackable_associations(_auth_object = nil) ⇒ Object
54 55 56 |
# File 'app/models/dscf/marketplace/order.rb', line 54 def self.ransackable_associations(_auth_object = nil) %w[quotation listing user ordered_by ordered_to delivery_order dropoff_address order_items] end |
.ransackable_attributes(_auth_object = nil) ⇒ Object
Ransack configuration for secure filtering
50 51 52 |
# File 'app/models/dscf/marketplace/order.rb', line 50 def self.ransackable_attributes(_auth_object = nil) %w[id quotation_id listing_id user_id ordered_by_id ordered_to_id delivery_order_id dropoff_address_id agent_id order_type status fulfillment_type payment_method received_bank_name transaction_reference total_amount created_at updated_at] end |
Instance Method Details
#agent_assisted? ⇒ Boolean
An agent-assisted order (agent_id present) must have its OTP verified
by the retailer before it can enter the normal validate/confirm
pipeline — this gate is intentionally orthogonal to status, which
already has an unrelated waiting_retailer_confirmation meaning (the
retailer signing off on supplier-proposed adjustments mid-fulfillment).
235 236 237 |
# File 'app/models/dscf/marketplace/order.rb', line 235 def agent_assisted? agent_id.present? end |
#all_items_validated? ⇒ Boolean
195 196 197 198 199 |
# File 'app/models/dscf/marketplace/order.rb', line 195 def all_items_validated? # A cancelled line (removed during validation) is no longer part of what # needs to ship, so it shouldn't block Split from becoming available. order_items.reject(&:cancelled?).all? { |i| i.validation_status == "validated" } end |
#awaiting_validation? ⇒ Boolean
Workflow helpers for Sprint 2 order validation + splitting.
Note: validating? is the enum-generated predicate (true only when
status == "validating"). Use this for "can still be run through
validation" — covers the actual validating status plus the states an
order can be created in (pending / legacy processing) before it does.
191 192 193 |
# File 'app/models/dscf/marketplace/order.rb', line 191 def awaiting_validation? pending? || validating? || status.to_s == "processing" end |
#billable_order_items ⇒ Object
Lines that should count toward the order total / retailer-facing amount. A cancelled line (removed during validation, or a rejected supplier allocation) is not being fulfilled, so it shouldn't inflate what the retailer is asked to confirm and pay.
138 139 140 |
# File 'app/models/dscf/marketplace/order.rb', line 138 def billable_order_items order_items.reject(&:cancelled?) end |
#calculate_total_amount ⇒ Object
182 183 184 |
# File 'app/models/dscf/marketplace/order.rb', line 182 def calculate_total_amount self.total_amount = billable_order_items.sum { |item| item.quantity * item.unit_price } end |
#can_be_completed? ⇒ Boolean
163 164 165 166 167 168 169 170 171 |
# File 'app/models/dscf/marketplace/order.rb', line 163 def can_be_completed? if self_pickup? # Self-pickup orders can be completed immediately after confirmation confirmed? || processing? else # Delivery orders require delivery_order association delivery_order.present? && delivery_order.delivered? end end |
#confirm! ⇒ Object
173 174 175 176 177 178 179 180 |
# File 'app/models/dscf/marketplace/order.rb', line 173 def confirm! return false unless pending? update!(status: :confirmed) # Update order items without reloading to avoid association issues order_items.update_all(status: OrderItem.statuses[:confirmed]) true end |
#delivery? ⇒ Boolean
155 156 157 |
# File 'app/models/dscf/marketplace/order.rb', line 155 def delivery? fulfillment_type == "delivery" end |
#mark_splitting! ⇒ Object
205 206 207 |
# File 'app/models/dscf/marketplace/order.rb', line 205 def mark_splitting! update!(status: :splitting) end |
#mark_waiting_retailer_confirmation! ⇒ Object
213 214 215 |
# File 'app/models/dscf/marketplace/order.rb', line 213 def mark_waiting_retailer_confirmation! update!(status: :waiting_retailer_confirmation) end |
#mark_waiting_supplier_confirmation! ⇒ Object
209 210 211 |
# File 'app/models/dscf/marketplace/order.rb', line 209 def mark_waiting_supplier_confirmation! update!(status: :waiting_supplier_confirmation) end |
#otp_verified? ⇒ Boolean
239 240 241 |
# File 'app/models/dscf/marketplace/order.rb', line 239 def otp_verified? otp_verifications.verified.exists? end |
#requires_delivery_order? ⇒ Boolean
159 160 161 |
# File 'app/models/dscf/marketplace/order.rb', line 159 def requires_delivery_order? delivery? && !completed? end |
#retailer_can_confirm? ⇒ Boolean
217 218 219 |
# File 'app/models/dscf/marketplace/order.rb', line 217 def retailer_can_confirm? waiting_retailer_confirmation? || confirmed? # allow re-confirm safety end |
#self_pickup? ⇒ Boolean
Fulfillment type methods
151 152 153 |
# File 'app/models/dscf/marketplace/order.rb', line 151 def self_pickup? fulfillment_type == "self_pickup" end |
#supplier ⇒ Object
142 143 144 145 146 147 148 |
# File 'app/models/dscf/marketplace/order.rb', line 142 def supplier if rfq_based? quotation&.business elsif direct_listing? listing&.business || ordered_to end end |
#supplier_confirmation_complete? ⇒ Boolean
221 222 223 224 225 226 227 228 |
# File 'app/models/dscf/marketplace/order.rb', line 221 def supplier_confirmation_complete? # A cancelled line (removed at validation time, before a source was ever # assigned, or rejected during confirmation) is done responding either # way — only a still-live line needs a source + confirmed status. order_items.all? do |i| i.cancelled? || (i.source_id.present? && i.status.to_s == "confirmed") end end |
#total_amount ⇒ Object
124 125 126 127 128 129 130 131 132 |
# File 'app/models/dscf/marketplace/order.rb', line 124 def total_amount # Return stored value if it exists and is greater than 0, otherwise calculate stored_value = super calculated_value = billable_order_items.sum { |item| item.quantity * item.unit_price } # Return stored value if it's set and valid, otherwise return calculated value return stored_value if stored_value.present? && stored_value > 0 calculated_value end |
#validation_summary ⇒ Object
201 202 203 |
# File 'app/models/dscf/marketplace/order.rb', line 201 def validation_summary order_items.group_by(&:validation_status).transform_values(&:count) end |