Class: SpreeCmCommissioner::Integrations::BookMeBusV1::Polling::SyncTrips

Inherits:
Object
  • Object
show all
Defined in:
app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb

Overview

SyncTrips synchronizes transit trip data from BookMeBus API to BookMe+

This service creates full, sellable trips with all related entities:

  • Product & Variant (with pricing)

  • VehicleType (bus type, capacity)

  • SeatLayout & Blocks (seat inventory)

  • ServiceCalendar (availability dates)

  • VendorPlaces for stops/branches (created by SyncTripStop)

  • TripStops (boarding/drop-off points)

  • RouteStops (shared route structure)

  • StockItem (inventory management)

Sync Flow:

  1. Fetch trips from BookMeBus API for a route/date

  2. For each trip:

    1. Sync VehicleType

    2. Sync origin/destination places (locations only)

    3. Sync Product & Variant

    4. Create/update Trip record

    5. Sync ServiceCalendar

    6. Sync inventory (StockItem)

    7. Sync SeatLayout & assign blocks to variant

    8. Sync TripStops (per-trip via SyncTripStop)

      • SyncTripStop creates VendorPlace records for stops/branches

      • Distinguishes between :stop and :branch based on operator_branch_id

  3. Sync RouteStops (using collected trip mappings)

  4. Cleanup stale mappings

  5. Touch route mapping timestamp

Sample API Response Payload: {

"data": [
  {
    "id": "41",
    "type": "transit_stop_time_results",
    "attributes": {
      "operator": {
        "id": 7,
        "name": "VIRAKBUNTHAM TRAVEL AND TOUR",
        "name_khmer": "ដឹកអ្នកដំណើរ",
        "phone": "077 21 58 58 / 016 21 58 58 / 088 321 58 58",
        "email": "info@bpatransport.com",
        "website": "https://virakbuntham.com",
        "address": "#87, st310 and 199, chomka mon district",
        "notice": "<p>Passengers have to attend to bus terminal 20 minutes before departure time.</p>\n",
        "receipt_note": "",
        "slug": "virakbuntham-tour",
        "logo": {
          "thumb": "/uploads/operator/logo/7/thumb_profile_vet-express-compressor.png",
          "medium": "/uploads/operator/logo/7/medium_profile_vet-express-compressor.png"
        },
        "rate": 4.09524,
        "setting_start_of_day": 4,
        "setting_return_trip_discount": 0,
        "setting_package_delivery_policy_kh": "",
        "setting_package_delivery_policy_en": "",
        "setting_package_delivery_legal_terms_kh": "",
        "setting_package_delivery_legal_terms_en": "",
        "setting_enable_membership_input": "no",
        "scanning_object_on_package": "",
        "prefer_lang": "en",
        "is_using_manual_input_reference_code": false,
        "nationality_sensitivity": true,
        "setting_is_using_reference_code": true,
        "setting_show_pickup_hotels_on_sale_ticket": "no",
        "base_currency_code": "usd",
        "country_code": "kh",
        "max_number_stop": 2
      },
      "vehicle_type": {
        "id": 6,
        "code": null,
        "name": "Sleeping Bus",
        "amenities": [
          "wifi",
          "water",
          "video",
          "guide",
          "napkin",
          "charger",
          "toilet"
        ],
        "type_name": null,
        "route_type": "bus",
        "vehicle_type_galleries": []
      },
      "stop_times": [
        {
          "id": 119,
          "stop_id": 60,
          "departure_time": 82800,
          "arrival_time": 104400,
          "is_transfer": 0,
          "stop": {
            "id": 60,
            "code": "",
            "name": "Night Market",
            "lat": "11.574439",
            "lon": "104.927673",
            "description": "",
            "timezone": "Phnom Penh",
            "url": "",
            "place_type": "unspecific",
            "shared": false,
            "operator_id": 7,
            "operator_branch_id": 22,
            "location_id": 1,
            "address": "St. Riverside, North of Night Market, Wat Phnom, Khan Daun Penh, Phnom Penh City, Kingdom ofCambodia."
          },
          "location": {
            "id": 1,
            "name": "Phnom Penh",
            "name_lower": "phnom penh",
            "abbreviation": "PP",
            "city_tel_code": "023",
            "photo": {
              "thumb": "/uploads/location/photo/1/thumb_phnom_penh.png",
              "standard": "/uploads/location/photo/1/standard_phnom_penh.png"
            },
            "name_khmer": "ភ្នំពេញ",
            "country_code": "KH",
            "country_name": "Cambodia",
            "hits_origin": 154621,
            "hits_destination": 69147,
            "slug": "phnom-penh"
          },
          "route_type": "bus",
          "number_of_seats": 63,
          "total_sold": 0
        },
        {
          "id": 120,
          "stop_id": 55,
          "departure_time": 0,
          "arrival_time": 0,
          "is_transfer": 0,
          "stop": {
            "id": 55,
            "code": "",
            "name": "Siem Reap Office",
            "lat": "12.4652",
            "lon": "103.891197",
            "description": "",
            "timezone": "Phnom Penh",
            "url": "",
            "place_type": "unspecific",
            "shared": false,
            "operator_id": 7,
            "operator_branch_id": 65,
            "location_id": 2,
            "address": "#249 next to Nakpeoun Old Market roundabout, St. Sevot Tha vay District Dongkum, Siem Reap  "
          },
          "location": {
            "id": 2,
            "name": "Siem Reap",
            "name_lower": "siem reap",
            "abbreviation": "SR",
            "city_tel_code": "063",
            "photo": {
              "thumb": "/uploads/location/photo/2/thumb_siem_reap.png",
              "standard": "/uploads/location/photo/2/standard_siem_reap.png"
            },
            "name_khmer": "សៀមរាប",
            "country_code": "KH",
            "country_name": "Cambodia",
            "hits_origin": 52700,
            "hits_destination": 116564,
            "slug": "siem-reap"
          },
          "route_type": "bus",
          "number_of_seats": 0,
          "total_sold": 0
        }
      ],
      "service_available": true,
      "has_discount": false,
      "return_included": false,
      "readable_discount": null,
      "currency_code": "USD",
      "route_type": "bus",
      "stop_time_route_types": [
        "bus"
      ],
      "local_price": 8.0,
      "original_local_price": 8.0,
      "non_local_price": 8.0,
      "original_non_local_price": 8.0,
      "head_sign": "",
      "short_name": "PP-SR Direct Sleeping 11:00PM",
      "description": null,
      "trip_id": 41,
      "on_date": "2026-03-30",
      "is_bus5": false,
      "seats_remaining": 63,
      "short_description": null,
      "service_calendar": {
        "id": 15,
        "name": "Permanent",
        "monday": true,
        "tuesday": true,
        "wednesday": true,
        "thursday": true,
        "friday": true,
        "saturday": true,
        "sunday": true,
        "start_date": "2010-10-10",
        "end_date": "2090-10-10",
        "service_type": 1,
        "not_available_reason": "",
        "service_exception_rules": []
      },
      "price": {
        "local": {
          "price": 8.0,
          "currency_type": "usd",
          "price_rule_by_nationality": null,
          "campaign_discount": 0
        },
        "non_local": {
          "price": 8.0,
          "currency_type": "usd",
          "price_rule_by_nationality": null,
          "campaign_discount": 0
        },
        "max_price": {
          "price": 8.0,
          "currency_type": "usd",
          "ages": null,
          "campaign_discount": 0
        },
        "min_price": {
          "price": 8.0,
          "currency_type": "usd",
          "ages": null,
          "campaign_discount": 0
        },
        "nationalities": {
          "kh": {
            "price": 8.0,
            "currency_type": "usd",
            "ages": null,
            "campaign_discount": 0
          },
          "other": {
            "price": 8.0,
            "currency_type": "usd",
            "ages": null,
            "campaign_discount": 0
          }
        },
        "price_by_age": [],
        "include_return": false
      },
      "round_trip_price": null,
      "auto_assigned_seat": false,
      "departure_over": false,
      "is_able_to_hold": true,
      "route_id": 32,
      "allow_pickup": true,
      "departure_date": "2026-03-30",
      "arrival_date": "2026-03-31",
      "number_of_seats": 63,
      "departure_time": 82800,
      "arrival_time": 18000,
      "travel_duration": 21600,
      "instant_book": true,
      "not_allowed": false,
      "is_selectable": true,
      "from_stop_id": 60,
      "to_stop_id": 55,
      "flag": 0,
      "airport_shuttle": false,
      "external_ref": null
    }
  }
]

}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(integration:, sync_result: nil, route: nil) ⇒ SyncTrips

Returns a new instance of SyncTrips.



282
283
284
285
286
287
288
289
# File 'app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb', line 282

def initialize(integration:, sync_result: nil, route: nil)
  @integration = integration
  @sync_result = sync_result || SpreeCmCommissioner::Integrations::Base::SyncResult.new
  @route = route
  @vendor = integration.vendor
  @location_mappings = {}
  @location_id_cache = {}
end

Instance Attribute Details

#integrationObject (readonly)

rubocop:disable Metrics/ClassLength



280
281
282
# File 'app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb', line 280

def integration
  @integration
end

#routeObject (readonly)

rubocop:disable Metrics/ClassLength



280
281
282
# File 'app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb', line 280

def route
  @route
end

#sync_resultObject (readonly)

rubocop:disable Metrics/ClassLength



280
281
282
# File 'app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb', line 280

def sync_result
  @sync_result
end

#vendorObject (readonly)

rubocop:disable Metrics/ClassLength



280
281
282
# File 'app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb', line 280

def vendor
  @vendor
end

Instance Method Details

#call(on_date: nil) ⇒ Object



291
292
293
294
295
# File 'app/services/spree_cm_commissioner/integrations/book_me_bus_v1/polling/sync_trips.rb', line 291

def call(on_date: nil)
  preload_location_mappings!
  preload_location_id_cache!
  sync_items!(on_date)
end