Class: SpreeCmCommissioner::Integrations::BookMeBusV1::Polling::SyncTrips
- Inherits:
-
Object
- Object
- SpreeCmCommissioner::Integrations::BookMeBusV1::Polling::SyncTrips
- 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:
-
Fetch trips from BookMeBus API for a route/date
-
For each trip:
-
Sync VehicleType
-
Sync origin/destination places (locations only)
-
Sync Product & Variant
-
Create/update Trip record
-
Sync ServiceCalendar
-
Sync inventory (StockItem)
-
Sync SeatLayout & assign blocks to variant
-
Sync TripStops (per-trip via SyncTripStop)
-
SyncTripStop creates VendorPlace records for stops/branches
-
Distinguishes between :stop and :branch based on operator_branch_id
-
-
-
Sync RouteStops (using collected trip mappings)
-
Cleanup stale mappings
-
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
-
#integration ⇒ Object
readonly
rubocop:disable Metrics/ClassLength.
-
#route ⇒ Object
readonly
rubocop:disable Metrics/ClassLength.
-
#sync_result ⇒ Object
readonly
rubocop:disable Metrics/ClassLength.
-
#vendor ⇒ Object
readonly
rubocop:disable Metrics/ClassLength.
Instance Method Summary collapse
- #call(on_date: nil) ⇒ Object
-
#initialize(integration:, sync_result: nil, route: nil) ⇒ SyncTrips
constructor
A new instance of SyncTrips.
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
#integration ⇒ Object (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 |
#route ⇒ Object (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_result ⇒ Object (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 |
#vendor ⇒ Object (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 |