Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,9 @@ STOCKIT_API_TOKEN=

SLACK_API_TOKEN=
SLACK_PIN_CHANNEL=

GOGOX_CLIENT_ID=
GOGOX_CLIENT_SECRET=
GOGOX_GRANT_TYPE=
GOGOX_HOST=
GOGOX_PAYMENT_METHOD=
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ gem 'easyzpl', git: 'https://github.com/crossroads/easyzpl.git'
gem 'factory_bot_rails' # used in rake db:seed in production
gem 'fake_email_validator'
gem 'ffaker'
gem 'go_go_van_api', git: '[email protected]:crossroads/go_go_van_api.git', branch: 'master'
gem 'go_go_van_api', git: '[email protected]:crossroads/go_go_van_api.git', branch: 'GCW-3407'
gem 'guid'
gem 'http_accept_language'
gem 'jwt', '~> 2.2.2'
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
GIT
remote: [email protected]:crossroads/go_go_van_api.git
revision: 3cbc5590e2a11a7903483de276bc668be88843cb
branch: master
revision: 7c0794f1c9eebcf0d44b8a4341f26ea8bdf259c9
branch: GCW-3407
specs:
go_go_van_api (0.0.1)
nestful (~> 1)
Expand Down
71 changes: 71 additions & 0 deletions app/controllers/api/v1/transports_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module Api
module V1
class TransportsController < Api::V1::ApiController

skip_authorization_check

api :GET, '/v1/transports/providers', "List all GoodCity Tranports Options."
def providers
render json: TransportProvider.all.cached_json
end

api :POST, '/v1/transports/quotation', "Get provider quotation"
param :provider, String, desc: "Provider selected for transport"
param :vehicle_type, String, desc: "Transport vehicle-type"
param :offer_id, String, desc: "Id of the offer"
param :schedule_at, String, desc: "Scheduled time for delivery"
param :district_id, String, desc: "Id of the district"
def quotation
order_price = TransportService.new(transport_params.to_h).quotation
render json: order_price.to_json
end

api :POST, '/v1/transports/quotation', "Book transport"
param :provider, String, desc: "Provider selected for transport"
param :vehicle_type, String, desc: "Transport vehicle-type"
param :offer_id, String, desc: "Id of the offer"
param :schedule_at, String, desc: "Scheduled time for delivery"
param :district_id, String, desc: "Id of the district"
param :pickup_contact_name, String, desc: "Contact Person Name"
param :pickup_contact_phone, String, desc: "Contact Person Mobile"
param :pickup_street_address, Hash, desc: "Pickup Address"
def book
order_info = TransportService.new(transport_params.to_h).book
render json: order_info.to_json
end

api :GET, '/v1/transports/:id/order_details', "Get GoodCity Tranport order details."
def order_details
order_info = TransportService.new({booking_id: params[:id]}).status
render json: order_info.to_json
end

api :POST, '/v1/transports/:id/cancel_order', "Cancel GoodCity Tranport order."
def cancel_order
order_info = TransportService.new({booking_id: params[:id]}).cancel
render json: order_info.to_json
end

api :POST, '/v1/transports/update_gogox_order', "Webhook to update GOGOX order status"
def update_gogox_order
# setup ngrok and inspect response
# response details are not yet available from Gogox Provider
end

private

def transport_params
set_district_id unless params["district_id"].presence
params.permit([
"scheduled_at", "district_id", "offer_id", "provider", "vehicle_type",
"pickup_street_address", "pickup_contact_name", "pickup_contact_phone"
])
end

def set_district_id
params["district_id"] = User.current_user.address.district_id
end

end
end
end
3 changes: 3 additions & 0 deletions app/models/transport_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class TransportProvider < ApplicationRecord
include CacheableJson
end
2 changes: 2 additions & 0 deletions app/models/transport_provider_order.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class TransportProviderOrder < ApplicationRecord
end
5 changes: 5 additions & 0 deletions app/serializers/api/v1/transport_provider_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Api::V1
class TransportProviderSerializer < ApplicationSerializer
attributes :id, :name, :logo, :description, :metadata
end
end
127 changes: 127 additions & 0 deletions app/services/gogox.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
class Gogox

attr_accessor :params, :time, :vehicle, :district_id

VEHICLE_TYPES = ["van", "mudou", "mudou9"]

def initialize(options = {})
@params = options
@time = parse_pickup_time(options[:scheduled_at])
@vehicle = options[:vehicle]
@district_id = options[:district_id]
end

# Rsponse
# {
# "uuid" => "2f859363-5c43-4fe2-9b91-6c6c43d610d2",
# "status" => "pending",
# "vehicle_type" => "van",
# "payment_method" => "prepaid_wallet",
# "courier" => {},
# "pickup" => {
# "name" => "Swati J",
# "street_address" => "123",
# "floor_or_unit_number" => nil,
# "schedule_at" => 1609242940,
# "location" => {"lat" => 22.5029632, "lng" => 114.1277213},
# "contact" => {
# "name" => "Swati J",
# "phone_number" => "+85251111113",
# "phone_extension" => nil
# }
# },
# "destinations" => [{
# "name" => "Steve Kenworthy",
# "street_address" => "Castle Peak Rd (So Kwun Wat)",
# "floor_or_unit_number" => nil,
# "location" => {"lat" => 22.3748365, "lng" => 113.9931416},
# "contact" => {
# "name" => "Steve Kenworthy",
# "phone_number" => "+85251111111"
# }
# }],
# "note_to_courier" => nil,
# "price" => {"amount" => 15000, "currency" => "HKD"},
# "price_breakdown" => [{"key" => "fee", "amount" => 15000}]
# }
def book
GogoxApi::Transport.new(order_attributes).order
end

# Response:
# {
# "vehicle_type" => "van",
# "estimated_price" => {"amount" => 15000, "currency" => "HKD"},
# "estimated_price_breakdown" => [{"key" => "fee", "amount" => 15000}]
# }
def quotation
GogoxApi::Transport.new(quotation_attributes).quotation
end

class << self

def transport_status(booking_id)
GogoxApi::Transport.new.status(booking_id)
end

# Response
# Response is nil on successful cancellation of GOGOX transport
def cancel_order(booking_id)
response = GogoxApi::Transport.new.cancel(booking_id)
if !response
{
order_uuid: booking_id,
status: "cancelled"
}
end
end

end

private

def order_attributes
{
'vehicle_type': vehicle_type,
"pickup_location": params[:pickup_location],
"pickup_street_address": params[:pickup_street_address],
"scheduled_at": parse_time,
"pickup_contact_name": params[:pickup_contact_name],
"pickup_contact_phone": params[:pickup_contact_phone],
"destination_location": params[:destination_location],
"destination_street_address": params[:destination_street_address],
"destination_contact_name": params[:destination_contact_name],
"destination_contact_phone": params[:destination_contact_phone]
}
end

def quotation_attributes
{
'vehicle_type': vehicle_type,
"scheduled_at": parse_time,
"pickup_location": params[:pickup_location],
"destination_location": params[:destination_location]
}
end

def vehicle_type
if vehicle.blank? || !VEHICLE_TYPES.include?(vehicle)
raise(ValueError, "vehicle should be from #{VEHICLE_TYPES.join(', ')}")
end
vehicle
end

def parse_pickup_time(time = nil)
return time if time.present?

# next available date within next 5 days
next_available_date = DateSet.new(5, 1).available_dates.first
(next_available_date.beginning_of_day + 12.hours)
end

def parse_time
DateTime.parse(@time.to_s).to_i
end

class ValueError < StandardError; end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of just value error defining here, create a error in errors.rb
something like TransportationServiceError

end
124 changes: 124 additions & 0 deletions app/services/transport_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
class TransportService

attr_accessor :provider_name, :params, :provider, :booking_id, :user, :district_id

def initialize(options={})
@params = options
@provider_name = options && options["provider"]
@provider ||= Object::const_get(provider_name)
@booking_id = options && options["booking_id"]

fetch_user
fetch_district_id
end

def quotation
@provider.new(params).quotation
end

def book
response = @provider.new(params).book
storeOrderDetails(response)
end

def cancel
response = @provider.cancel_order(booking_id)
if response
updateOrderDetails({
status: "cancelled",
order_uuid: booking_id
})
end
end

def status
response = @provider.transport_status(booking_id)
if response
updateOrderDetails({
status: response["status"],
order_uuid: booking_id,
metadata: response
})
end
end

private

def storeOrderDetails(response)
TransportProviderOrder.create(
transport_provider_id: TransportProvider.find_by(name: provider_name).try(:id),
order_uuid: response["uuid"],
status: response["status"],
schedule_at: response["pickup"]["schedule_at"],
metadata: response,
offer_id: @params["offer_id"]
)
end

def updateOrderDetails(response)
order = TransportProviderOrder.find_by(order_uuid: response["order_uuid"])
order.update_attributes(response)
end

def quotation_attributes
{
'vehicle_type': @params["vehicle_type"],
"scheduled_at": @params["scheduled_at"],
"pickup_location": pickup_location,
"destination_location": destination_location
}
end

def pickup_location
pickup_district = District.find(@district_id)
[pickup_district.latitude, pickup_district.longitude]
end

# TODO: Update crossroads geolocation values
def destination_location
[22.3748365, 113.9931416]
end

# TODO: Change
def destination_street_address
"Castle Peak Rd (So Kwun Wat)"
end

# TODO: Change
def destination_contact_name
"Steve Kenworthy"
end

# TODO: Change
def destination_contact_phone
"+85251111111"
end

def order_attributes
{
'vehicle_type': vehicle_type,
"pickup_location": pickup_location,
"pickup_street_address": params[:pickup_street_address],
"scheduled_at": params[:schedule_at],
"pickup_contact_name": params[:pickup_contact_name] || @user.full_name,
"pickup_contact_phone": params[:pickup_contact_phone] || @user.mobile,
"destination_location": destination_location,
"destination_street_address": destination_street_address,
"destination_contact_name": destination_contact_name,
"destination_contact_phone": destination_contact_phone
}
end

def fetch_user
@user ||= if @params["user_id"].present?
User.find_by(id: @params["user_id"])
else
User.current_user
end
end

def fetch_district_id
@district_id ||= @params["district_id"].presence || @user.address.district_id
end

end
12 changes: 12 additions & 0 deletions db/migrate/20201210092143_create_transport_providers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateTransportProviders < ActiveRecord::Migration[5.2]
def change
create_table :transport_providers do |t|
t.string :name
t.string :logo
t.text :description
t.jsonb :metadata, default: '{}'

t.timestamps
end
end
end
14 changes: 14 additions & 0 deletions db/migrate/20210111115250_create_transport_provider_orders.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class CreateTransportProviderOrders < ActiveRecord::Migration[5.2]
def change
create_table :transport_provider_orders do |t|
t.integer :transport_provider_id
t.string :order_uuid
t.string :status
t.datetime :scheduled_at
t.jsonb :metadata
t.integer :offer_id

t.timestamps
end
end
end
Loading