class SpreeCmCommissioner::TripDistanceCalculator
-
distance_km, directions_url, ordered_points, estimated_time_minutes (when optimize true)
- base_km, detour_pickup_km, detour_dropoff_km: Floats or nil
- payload: Hash (compatible with previous controller response)
Outputs via context on success:
- boundary_km: Numeric (optional, default 0)
- base_price_usd: Numeric
- dropoff_oob_confirmed: Boolean (optional)
- pickup_oob_confirmed: Boolean (optional)
- dropoffs: Array<{ lat:, lng: }> (optional)
- pickups: Array<{ lat:, lng: }> (optional)
- configured_destination: { lat:, lng: }
- origin: { lat:, lng: }
Inputs via context:
Computes trip distance details and pricing payload.
def assign_context(details_ctx, base_km, detour_pickup_km, detour_dropoff_km, payload)
def assign_context(details_ctx, base_km, detour_pickup_km, detour_dropoff_km, payload) context.payload = payload context.distance_km = details_ctx.distance_km context.directions_url = details_ctx.directions_url context.ordered_points = details_ctx.ordered_points context.estimated_time_minutes = details_ctx.estimated_time_minutes context.base_km = base_km context.detour_pickup_km = detour_pickup_km context.detour_dropoff_km = detour_dropoff_km end
def build_payload(details_ctx, base_km, detour_pickup_km, detour_dropoff_km, data)
def build_payload(details_ctx, base_km, detour_pickup_km, detour_dropoff_km, data) { distance_km: details_ctx.distance_km, directions_url: details_ctx.directions_url, ordered_points: details_ctx.ordered_points, estimated_time_minutes: details_ctx.estimated_time_minutes, base_price_usd: base_price_usd.to_f, total_price_usd: data[:total_price_usd], extra_pickup_km: data[:extra_pickup_km], extra_dropoff_km: data[:extra_dropoff_km], extra_pickup_charge_usd: data[:extra_pickup_charge_usd], extra_dropoff_charge_usd: data[:extra_dropoff_charge_usd], extra_charge_usd: data[:extra_charge_usd], base_km: base_km, detour_pickup_km: detour_pickup_km, detour_dropoff_km: detour_dropoff_km } end
def call
def call validate_inputs pickups_points, dropoffs_points, final_destination = prepare_points base_km, detour_pickup_km, detour_dropoff_km = compute_route_kms(pickups_points, dropoffs_points, final_destination) details_ctx = fetch_details(final_destination, pickups_points, dropoffs_points) extra_data = compute_extra_data(base_km, detour_pickup_km, detour_dropoff_km) payload = build_payload(details_ctx, base_km, detour_pickup_km, detour_dropoff_km, extra_data) assign_context(details_ctx, base_km, detour_pickup_km, detour_dropoff_km, payload) end
def charge_for_extra_km(extra_km)
def charge_for_extra_km(extra_km) x = extra_km.to_f return 0.0 if x <= 0 return 2.0 if x <= 5.0 return 5.0 if x <= 10.0 return 10.0 if x <= 20.0 (10.0 + ((x - 20.0) * 0.5)).round(2) end
def compute_extra_data(base_km, detour_pickup_km, detour_dropoff_km)
def compute_extra_data(base_km, detour_pickup_km, detour_dropoff_km) extra_pickup_km = compute_extra_km(detour_pickup_km, base_km, pickup_oob_confirmed) extra_dropoff_km = compute_extra_km(detour_dropoff_km, base_km, dropoff_oob_confirmed) extra_pickup_charge_usd = charge_for_extra_km(extra_pickup_km) extra_dropoff_charge_usd = charge_for_extra_km(extra_dropoff_km) extra_charge_usd = (extra_pickup_charge_usd + extra_dropoff_charge_usd).round(2) total_price_usd = (base_price_usd.to_f + extra_charge_usd).round(2) { extra_pickup_km: extra_pickup_km, extra_dropoff_km: extra_dropoff_km, extra_pickup_charge_usd: extra_pickup_charge_usd, extra_dropoff_charge_usd: extra_dropoff_charge_usd, total_price_usd: total_price_usd } end
def compute_extra_km(detour_km, base_km, confirmed)
def compute_extra_km(detour_km, base_km, confirmed) return 0.0 unless confirmed && detour_km && base_km [(detour_km - base_km - boundary_km), 0.0].max end
def compute_km(origin:, destination:, pickups: nil, dropoffs: nil, optimize: nil)
def compute_km(origin:, destination:, pickups: nil, dropoffs: nil, optimize: nil) ctx = SpreeCmCommissioner::GoogleRoutesDistanceCalculator.call( origin: origin, destination: destination, pickups: pickups, dropoffs: dropoffs, optimize: optimize ) return nil unless ctx.success? ctx.distance_km end
def compute_route_kms(pickups_points, dropoffs_points, final_destination)
def compute_route_kms(pickups_points, dropoffs_points, final_destination) base_km = compute_km(origin: origin, destination: configured_destination) detour_pickup_km = if pickups_points.any? compute_km( origin: origin, destination: configured_destination, pickups: pickups_points, optimize: false ) end detour_dropoff_km = if dropoffs_points.any? compute_km( origin: origin, destination: final_destination, dropoffs: dropoffs_points, optimize: false ) end [base_km, detour_pickup_km, detour_dropoff_km] end
def fetch_details(final_destination, pickups_points, dropoffs_points)
def fetch_details(final_destination, pickups_points, dropoffs_points) ctx = SpreeCmCommissioner::GoogleRoutesDistanceCalculator.call( origin: origin, destination: final_destination, pickups: pickups_points, dropoffs: dropoffs_points, optimize: true ) context.fail!(message: ctx.message || 'Unable to calculate') unless ctx.success? ctx end
def prepare_points
def prepare_points pickups_points = Array(pickups).compact dropoffs_points = Array(dropoffs).compact final_destination = dropoffs_points.last || configured_destination [pickups_points, dropoffs_points, final_destination] end
def validate_inputs
def validate_inputs context.fail!(message: 'origin is required') if origin.blank? context.fail!(message: 'configured_destination is required') if configured_destination.blank? end