spree_cm_commissioner 2.0.3.pre.pre6 → 2.0.3.pre.pre8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/locations_controller.rb +62 -0
- data/app/controllers/spree/api/v2/platform/places_controller.rb +4 -1
- data/app/controllers/spree/api/v2/storefront/dynamic_fields_controller.rb +42 -0
- data/app/controllers/spree/api/v2/storefront/guests_controller.rb +2 -0
- data/app/controllers/spree/api/v2/storefront/trip_places_controller.rb +1 -1
- data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +1 -1
- data/app/controllers/spree/api/v2/storefront/trips_controller.rb +24 -0
- data/app/interactors/spree_cm_commissioner/billing_address_creator.rb +33 -0
- data/app/interactors/spree_cm_commissioner/create_vendor.rb +11 -1
- data/app/interactors/spree_cm_commissioner/google_places_fetcher.rb +1 -0
- data/app/interactors/spree_cm_commissioner/intercity_taxi_order_creator.rb +106 -0
- data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +1 -0
- data/app/models/spree_cm_commissioner/guest.rb +105 -56
- data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +37 -27
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +7 -0
- data/app/models/spree_cm_commissioner/user_decorator.rb +1 -1
- data/app/models/spree_cm_commissioner/variant_options.rb +4 -0
- data/app/models/spree_cm_commissioner/vehicle.rb +2 -0
- data/app/overrides/spree/admin/shared/sub_menu/_configuration/locations_tab.html.erb.deface +5 -0
- data/app/serializers/{spree → spree_cm_commissioner}/v2/storefront/amenity_serializer.rb +2 -2
- data/app/serializers/spree_cm_commissioner/v2/storefront/block_serializer.rb +9 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +1 -3
- data/app/serializers/spree_cm_commissioner/v2/storefront/seat_layout_serializer.rb +12 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/seat_section_serializer.rb +11 -0
- data/app/serializers/{spree → spree_cm_commissioner}/v2/storefront/trip_place_serializer.rb +1 -1
- data/app/serializers/{spree → spree_cm_commissioner}/v2/storefront/trip_query_result_serializer.rb +2 -2
- data/app/serializers/{spree → spree_cm_commissioner}/v2/storefront/trip_result_serializer.rb +4 -4
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_serializer.rb +17 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +9 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_variant_serializer.rb +12 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_serializer.rb +16 -0
- data/app/serializers/{spree → spree_cm_commissioner}/v2/storefront/trip_vendor_serializer.rb +4 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/variant_block_serializer.rb +9 -0
- data/app/views/shared/_map.html.erb +5 -4
- data/app/views/spree/admin/locations/_form.html.erb +133 -0
- data/app/views/spree/admin/locations/edit.html.erb +11 -0
- data/app/views/spree/admin/locations/index.html.erb +67 -0
- data/app/views/spree/admin/locations/new.html.erb +11 -0
- data/config/locales/en.yml +2 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20250812121745_add_data_fill_stage_phase_to_cm_guests.rb +5 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +26 -8
- data/app/serializers/spree/v2/storefront/trip_vehicle_serializer.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a6380c1548ccc1bbd8756837c2a3d4fc629613fa39f4f4949580c62f3c6fd97
|
4
|
+
data.tar.gz: 30371e0f95da706ac53f81141745cd36d88a9c631feb9de813fd6c2a00c4d641
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 362e42693a4d2ef816db1a3465efe55f789753d1ee1ffa9a9795dbe727be4b03b0088f9c888274589ddb604b76cc6143c7119388f9094a6b66c185e9f8b6b8dc
|
7
|
+
data.tar.gz: ab28c8455539f16d82b0c7c24fc74ab9f1947679e296b4b849785820fba719ed09aadfb0338c570304b1c874d83d6d20743cd40f176801c6bf529a90f3401bee
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Spree
|
2
|
+
module Admin
|
3
|
+
class LocationsController < Spree::Admin::ResourceController
|
4
|
+
def index
|
5
|
+
@search = model_class.ransack(params[:q])
|
6
|
+
@locations = @search.result.order(created_at: :desc).page(params[:page]).per(15)
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
if location_exist?
|
11
|
+
flash[:error] = I18n.t('location.already_exist')
|
12
|
+
render :new, status: :unprocessable_entity and return
|
13
|
+
end
|
14
|
+
|
15
|
+
location = model_class.new(permitted_resource_params)
|
16
|
+
|
17
|
+
if location.save
|
18
|
+
flash[:success] = flash_message_for(location, :successfully_created)
|
19
|
+
redirect_to spree.admin_locations_path
|
20
|
+
else
|
21
|
+
flash.now[:error] = location.errors.full_messages.to_sentence
|
22
|
+
render :new, status: :unprocessable_entity
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def location_exist?
|
29
|
+
SpreeCmCommissioner::Place.find_by(reference: permitted_resource_params[:reference])
|
30
|
+
end
|
31
|
+
|
32
|
+
# overrided
|
33
|
+
def model_class
|
34
|
+
SpreeCmCommissioner::Place
|
35
|
+
end
|
36
|
+
|
37
|
+
# override
|
38
|
+
def object_name
|
39
|
+
'spree_cm_commissioner_location'
|
40
|
+
end
|
41
|
+
|
42
|
+
def collection_url(options = {})
|
43
|
+
admin_locations_url(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def edit_object_url(object = nil, options = {})
|
47
|
+
edit_admin_location_url(object || @object, options)
|
48
|
+
end
|
49
|
+
|
50
|
+
# overrided
|
51
|
+
def new_object_url(options = {})
|
52
|
+
new_admin_location_url(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def permitted_resource_params
|
56
|
+
params.require(:spree_cm_commissioner_place).permit(:name, :lat, :lon, :formatted_address, :types, :reference,
|
57
|
+
:vicinity, :icon, :rating
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -41,10 +41,13 @@ module Spree
|
|
41
41
|
|
42
42
|
def struct_places_with_id(places)
|
43
43
|
places.map do |place|
|
44
|
+
short_addr = place.vicinity || place.formatted_address
|
45
|
+
name = "#{place.name} - #{short_addr}"
|
46
|
+
|
44
47
|
place_struct = Struct.new(:id, :name, :base_64_content)
|
45
48
|
place_struct.new(
|
46
49
|
id: place.reference,
|
47
|
-
name:
|
50
|
+
name: name,
|
48
51
|
base_64_content: Base64.strict_encode64(place.to_json)
|
49
52
|
)
|
50
53
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Spree
|
2
|
+
module Api
|
3
|
+
module V2
|
4
|
+
module Storefront
|
5
|
+
class DynamicFieldsController < ::Spree::Api::V2::ResourceController
|
6
|
+
before_action :load_product
|
7
|
+
|
8
|
+
# override
|
9
|
+
def collection
|
10
|
+
return @collection if defined?(@collection)
|
11
|
+
|
12
|
+
dynamic_fields = @product.dynamic_fields
|
13
|
+
if params[:phase].present?
|
14
|
+
requested_phase = params[:phase].to_s
|
15
|
+
if SpreeCmCommissioner::DynamicField.data_fill_stages.key?(requested_phase)
|
16
|
+
dynamic_fields = dynamic_fields.where(data_fill_stage: requested_phase)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
@collection = dynamic_fields.order(:position)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def load_product
|
26
|
+
return render_error_payload({ error: 'product_id is required' }, 400) if params[:product_id].blank?
|
27
|
+
|
28
|
+
@product ||= Spree::Product.find(params[:product_id])
|
29
|
+
end
|
30
|
+
|
31
|
+
def collection_serializer
|
32
|
+
SpreeCmCommissioner::V2::Storefront::DynamicFieldSerializer
|
33
|
+
end
|
34
|
+
|
35
|
+
def model_class
|
36
|
+
SpreeCmCommissioner::DynamicField
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -29,6 +29,7 @@ module Spree
|
|
29
29
|
resource = scope.new(guest_params)
|
30
30
|
|
31
31
|
if resource.save
|
32
|
+
resource.save_and_move_to_next_stage
|
32
33
|
render_serialized_payload(201) { serialize_resource(resource) }
|
33
34
|
else
|
34
35
|
render_error_payload(resource.errors, 400)
|
@@ -54,6 +55,7 @@ module Spree
|
|
54
55
|
resource.save!
|
55
56
|
end
|
56
57
|
|
58
|
+
resource.save_and_move_to_next_stage
|
57
59
|
render_serialized_payload { serialize_resource(resource) }
|
58
60
|
rescue ActiveRecord::RecordInvalid => e
|
59
61
|
render_error_payload(e.record.errors, 400)
|
@@ -29,7 +29,7 @@ module Spree
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def serialize_collection(collection)
|
32
|
-
serialized_data =
|
32
|
+
serialized_data = SpreeCmCommissioner::V2::Storefront::TripQueryResultSerializer.new(
|
33
33
|
collection,
|
34
34
|
include: default_resource_includes,
|
35
35
|
params: serializer_params
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Spree
|
2
|
+
module Api
|
3
|
+
module V2
|
4
|
+
module Storefront
|
5
|
+
class TripsController < ::Spree::Api::V2::ResourceController
|
6
|
+
# override
|
7
|
+
def resource
|
8
|
+
model_class.find(params[:id])
|
9
|
+
end
|
10
|
+
|
11
|
+
# override
|
12
|
+
def model_class
|
13
|
+
SpreeCmCommissioner::Trip
|
14
|
+
end
|
15
|
+
|
16
|
+
# override
|
17
|
+
def resource_serializer
|
18
|
+
SpreeCmCommissioner::V2::Storefront::TripSerializer
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class BillingAddressCreator < BaseInteractor
|
3
|
+
delegate :order, to: :context
|
4
|
+
|
5
|
+
def call
|
6
|
+
validate_context!
|
7
|
+
create_address
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def validate_context!
|
13
|
+
context.fail!(message: 'Order is missing') unless order
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_address
|
17
|
+
line_item = order.line_items.first
|
18
|
+
guest = line_item&.guests&.first
|
19
|
+
|
20
|
+
billing_address = order.build_bill_address(
|
21
|
+
firstname: guest&.first_name,
|
22
|
+
lastname: guest&.last_name,
|
23
|
+
phone: order.intel_phone_number || order.phone_number
|
24
|
+
)
|
25
|
+
|
26
|
+
context.fail!(message: billing_address.errors.full_messages.to_sentence) unless billing_address.save!
|
27
|
+
# Persist association on the order so subsequent reloads reflect the new billing address
|
28
|
+
order.update!(bill_address: billing_address)
|
29
|
+
|
30
|
+
context.billing_address = billing_address
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -8,6 +8,7 @@ module SpreeCmCommissioner
|
|
8
8
|
ActiveRecord::Base.transaction do
|
9
9
|
create_vendor
|
10
10
|
assign_vendor_to_user
|
11
|
+
create_logo
|
11
12
|
create_role
|
12
13
|
end
|
13
14
|
end
|
@@ -30,9 +31,18 @@ module SpreeCmCommissioner
|
|
30
31
|
context.user.vendors << @vendor
|
31
32
|
end
|
32
33
|
|
34
|
+
def create_logo
|
35
|
+
return if context.logo.blank?
|
36
|
+
|
37
|
+
vendor_logo = SpreeCmCommissioner::VendorLogo.create(viewable: @vendor, attachment: context.logo)
|
38
|
+
return if vendor_logo.persisted?
|
39
|
+
|
40
|
+
context.fail!(message: vendor_logo.errors.full_messages.join(', '))
|
41
|
+
end
|
42
|
+
|
33
43
|
def create_role
|
34
44
|
role = context.user.spree_roles.create(
|
35
|
-
name: "#{@vendor.
|
45
|
+
name: "#{@vendor.id}-organizer",
|
36
46
|
vendor_id: @vendor.id,
|
37
47
|
presentation: 'Organizer'
|
38
48
|
)
|
@@ -11,6 +11,7 @@ module SpreeCmCommissioner
|
|
11
11
|
types: json['types'].blank? ? '' : json['types'][0],
|
12
12
|
name: json['name'],
|
13
13
|
vicinity: json['vicinity'],
|
14
|
+
formatted_address: json['formatted_address'],
|
14
15
|
lat: json['geometry']['location']['lat'],
|
15
16
|
lon: json['geometry']['location']['lng'],
|
16
17
|
icon: json['icon'],
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class IntercityTaxiOrderCreator < BaseInteractor
|
3
|
+
delegate :trip, :raw_params, :current_vendor, to: :context
|
4
|
+
|
5
|
+
def call
|
6
|
+
validate_context!
|
7
|
+
|
8
|
+
ActiveRecord::Base.transaction do
|
9
|
+
build_order_params
|
10
|
+
create_order
|
11
|
+
create_billing_address
|
12
|
+
advance_order
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def validate_context!
|
19
|
+
context.fail!(message: 'Trip is missing') unless trip
|
20
|
+
context.fail!(message: 'Raw parameters are missing') unless raw_params
|
21
|
+
context.fail!(message: 'Current vendor is missing') unless current_vendor
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_order_params
|
25
|
+
context.order_params = build_taxi_booking_params
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_taxi_booking_params
|
29
|
+
permitted_params = raw_params.require(:order).permit(
|
30
|
+
:intel_phone_number,
|
31
|
+
:email,
|
32
|
+
:store_id,
|
33
|
+
line_items_attributes: [
|
34
|
+
:remark,
|
35
|
+
:variant_id,
|
36
|
+
:quantity,
|
37
|
+
:vendor_id,
|
38
|
+
:from_date,
|
39
|
+
:to_date,
|
40
|
+
:currency,
|
41
|
+
:direction,
|
42
|
+
:trip_id,
|
43
|
+
:pickup_place_name,
|
44
|
+
:drop_off_place_name,
|
45
|
+
:pickup_lat,
|
46
|
+
:pickup_lng,
|
47
|
+
:drop_off_lat,
|
48
|
+
:drop_off_lng,
|
49
|
+
:passenger_count,
|
50
|
+
:pickup_oob_confirmed,
|
51
|
+
:drop_off_oob_confirmed,
|
52
|
+
{ guests_attributes: %i[
|
53
|
+
first_name last_name gender age nationality_id
|
54
|
+
]
|
55
|
+
}
|
56
|
+
]
|
57
|
+
)
|
58
|
+
|
59
|
+
process_line_items_attributes(permitted_params)
|
60
|
+
permitted_params
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_line_items_attributes(params)
|
64
|
+
date_param = raw_params[:date] || Date.current.to_s
|
65
|
+
|
66
|
+
params[:line_items_attributes].each_value do |item|
|
67
|
+
pickup_time_minutes = item[:from_date]
|
68
|
+
combined_datetime = "#{date_param} #{pickup_time_minutes}"
|
69
|
+
|
70
|
+
item[:from_date] = combined_datetime
|
71
|
+
item[:to_date] = combined_datetime
|
72
|
+
item[:variant_id] = load_variant
|
73
|
+
item[:vendor_id] = current_vendor.id
|
74
|
+
item[:quantity] = 1
|
75
|
+
item[:currency] = 'USD'
|
76
|
+
item[:direction] = 'outbound'
|
77
|
+
item[:trip_id] = trip.id
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_order
|
82
|
+
context.order = Spree::Order.create(context.order_params)
|
83
|
+
|
84
|
+
context.fail!(message: context.order.errors.full_messages.to_sentence) unless context.order.persisted?
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_billing_address
|
88
|
+
return if context.order.bill_address.present?
|
89
|
+
|
90
|
+
SpreeCmCommissioner::BillingAddressCreator.call(
|
91
|
+
order: context.order
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
def advance_order
|
96
|
+
context.order.update_with_updater!
|
97
|
+
|
98
|
+
advance_service = Spree::Api::Dependencies.storefront_checkout_advance_service.constantize
|
99
|
+
advance_service.call(order: context.order)
|
100
|
+
end
|
101
|
+
|
102
|
+
def load_variant
|
103
|
+
trip.product.master.id
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -226,62 +226,6 @@ module SpreeCmCommissioner
|
|
226
226
|
save!
|
227
227
|
end
|
228
228
|
|
229
|
-
def missing_dynamic_fields_for_stage(stage)
|
230
|
-
required_fields = line_item.product.dynamic_fields.where(data_fill_stage: stage)
|
231
|
-
filled_ids = guest_dynamic_fields.map(&:dynamic_field_id)
|
232
|
-
|
233
|
-
required_fields.reject { |field| filled_ids.include?(field.id) }
|
234
|
-
end
|
235
|
-
|
236
|
-
def all_required_dynamic_fields_completed?(stage)
|
237
|
-
missing_dynamic_fields_for_stage(stage).blank?
|
238
|
-
end
|
239
|
-
|
240
|
-
def validate_dynamic_fields_for_phase(phase)
|
241
|
-
required_fields = line_item.product.dynamic_fields.where(data_fill_stage: phase)
|
242
|
-
filled_ids = guest_dynamic_fields.map(&:dynamic_field_id)
|
243
|
-
|
244
|
-
required_fields.each do |field|
|
245
|
-
errors.add(:base, "#{field.label} is required for #{phase} phase") unless filled_ids.include?(field.id)
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
def pre_registration_completed?
|
250
|
-
return true unless line_item.product.dynamic_fields.pre_registration.any?
|
251
|
-
|
252
|
-
pre_registration_fields_completed?
|
253
|
-
end
|
254
|
-
|
255
|
-
def post_registration_completed?
|
256
|
-
return true unless line_item.product.dynamic_fields.post_registration.any?
|
257
|
-
|
258
|
-
post_registration_fields_completed?
|
259
|
-
end
|
260
|
-
|
261
|
-
def check_in_completed?
|
262
|
-
return true unless line_item.product.dynamic_fields.during_check_in.any?
|
263
|
-
|
264
|
-
check_in_fields_completed?
|
265
|
-
end
|
266
|
-
|
267
|
-
def current_collection_phase
|
268
|
-
if check_in.present?
|
269
|
-
:during_check_in
|
270
|
-
elsif line_item.order.completed?
|
271
|
-
:post_registration
|
272
|
-
else
|
273
|
-
:pre_registration
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def phase_completion_status
|
278
|
-
{
|
279
|
-
pre_registration: pre_registration_completed?,
|
280
|
-
post_registration: post_registration_completed?,
|
281
|
-
during_check_in: check_in_completed?
|
282
|
-
}
|
283
|
-
end
|
284
|
-
|
285
229
|
# bib_number: 345, bib_prefix: 5KM, bib_zerofill: 5 => return 5KM00345
|
286
230
|
# bib_number: 345, bib_prefix: 5KM, bib_zerofill: 2 => return 5KM345
|
287
231
|
def formatted_bib_number
|
@@ -318,6 +262,111 @@ module SpreeCmCommissioner
|
|
318
262
|
block_ids = line_item.order.blocks.pluck(:id)
|
319
263
|
line_item.order.update(preload_block_ids: block_ids)
|
320
264
|
end
|
265
|
+
|
266
|
+
def all_required_dynamic_fields_completed?(stage)
|
267
|
+
missing_dynamic_fields_for_stage(stage).blank?
|
268
|
+
end
|
269
|
+
|
270
|
+
state_machine :data_fill_stage_phase, initial: 'pre_registration' do
|
271
|
+
state 'pre_registration', value: 'pre_registration'
|
272
|
+
state 'post_registration', value: 'post_registration'
|
273
|
+
state 'during_check_in', value: 'during_check_in'
|
274
|
+
state 'completed', value: 'completed'
|
275
|
+
|
276
|
+
event :complete_pre_registration do
|
277
|
+
transition 'pre_registration' => 'post_registration',
|
278
|
+
if: -> (guest) { guest.pre_registration_completed? && guest.post_registration_fields? }
|
279
|
+
transition 'pre_registration' => 'during_check_in',
|
280
|
+
if: -> (guest) { guest.pre_registration_completed? && !guest.post_registration_fields? && guest.during_check_in_fields? }
|
281
|
+
transition 'pre_registration' => 'completed',
|
282
|
+
if: -> (guest) { guest.pre_registration_completed? && !guest.post_registration_fields? && !guest.during_check_in_fields? }
|
283
|
+
end
|
284
|
+
|
285
|
+
event :complete_post_registration do
|
286
|
+
transition 'post_registration' => 'during_check_in',
|
287
|
+
if: -> (guest) { guest.post_registration_completed? && guest.during_check_in_fields? }
|
288
|
+
transition 'post_registration' => 'completed',
|
289
|
+
if: -> (guest) { guest.post_registration_completed? && !guest.during_check_in_fields? }
|
290
|
+
end
|
291
|
+
|
292
|
+
event :complete_check_in do
|
293
|
+
transition 'during_check_in' => 'completed',
|
294
|
+
if: -> (guest) { guest.check_in_completed? }
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def save_and_move_to_next_stage(_transition = nil)
|
299
|
+
# Trigger state machine transitions based on completion status
|
300
|
+
case data_fill_stage_phase
|
301
|
+
when 'pre_registration'
|
302
|
+
complete_pre_registration! if pre_registration_completed?
|
303
|
+
when 'post_registration'
|
304
|
+
complete_post_registration! if post_registration_completed?
|
305
|
+
when 'during_check_in'
|
306
|
+
complete_check_in! if check_in_completed?
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def completed_phases
|
311
|
+
phases = []
|
312
|
+
phases << 'pre_registration' if pre_registration_completed?
|
313
|
+
phases << 'post_registration' if post_registration_completed?
|
314
|
+
phases << 'during_check_in' if check_in_completed?
|
315
|
+
phases
|
316
|
+
end
|
317
|
+
|
318
|
+
def pre_registration_completed?
|
319
|
+
return true unless line_item&.product&.dynamic_fields&.pre_registration&.any?
|
320
|
+
|
321
|
+
required_fields = line_item.product.dynamic_fields.pre_registration.pluck(:id)
|
322
|
+
filled_fields = guest_dynamic_fields.where(dynamic_field_id: required_fields).pluck(:dynamic_field_id)
|
323
|
+
(required_fields - filled_fields).empty?
|
324
|
+
end
|
325
|
+
|
326
|
+
def post_registration_completed?
|
327
|
+
return true unless line_item&.product&.dynamic_fields&.post_registration&.any?
|
328
|
+
|
329
|
+
required_fields = line_item.product.dynamic_fields.post_registration.pluck(:id)
|
330
|
+
filled_fields = guest_dynamic_fields.where(dynamic_field_id: required_fields).pluck(:dynamic_field_id)
|
331
|
+
(required_fields - filled_fields).empty?
|
332
|
+
end
|
333
|
+
|
334
|
+
def check_in_completed?
|
335
|
+
return true unless line_item&.product&.dynamic_fields&.during_check_in&.any?
|
336
|
+
|
337
|
+
required_fields = line_item.product.dynamic_fields.during_check_in.pluck(:id)
|
338
|
+
filled_fields = guest_dynamic_fields.where(dynamic_field_id: required_fields).pluck(:dynamic_field_id)
|
339
|
+
(required_fields - filled_fields).empty?
|
340
|
+
end
|
341
|
+
|
342
|
+
def pre_registration_fields?
|
343
|
+
line_item&.product&.dynamic_fields&.pre_registration&.any?
|
344
|
+
end
|
345
|
+
|
346
|
+
def post_registration_fields?
|
347
|
+
line_item&.product&.dynamic_fields&.post_registration&.any?
|
348
|
+
end
|
349
|
+
|
350
|
+
def during_check_in_fields?
|
351
|
+
line_item&.product&.dynamic_fields&.during_check_in&.any?
|
352
|
+
end
|
353
|
+
|
354
|
+
private
|
355
|
+
|
356
|
+
def product_dynamic_fields
|
357
|
+
@product_dynamic_fields ||= line_item.product.dynamic_fields
|
358
|
+
end
|
359
|
+
|
360
|
+
def missing_dynamic_fields_for_stage(stage)
|
361
|
+
return [] if line_item.blank? || line_item.product.blank?
|
362
|
+
|
363
|
+
# Cache product dynamic fields to avoid multiple queries
|
364
|
+
@product_dynamic_fields ||= line_item.product.dynamic_fields
|
365
|
+
required_fields = @product_dynamic_fields.where(data_fill_stage: stage)
|
366
|
+
filled_ids = guest_dynamic_fields&.pluck(:dynamic_field_id) || []
|
367
|
+
|
368
|
+
required_fields.reject { |field| filled_ids.include?(field.id) }
|
369
|
+
end
|
321
370
|
end
|
322
371
|
end
|
323
372
|
# rubocop:enable Metrics/ClassLength
|
@@ -4,10 +4,13 @@ module SpreeCmCommissioner
|
|
4
4
|
belongs_to :dynamic_field, class_name: 'SpreeCmCommissioner::DynamicField', optional: false
|
5
5
|
belongs_to :dynamic_field_option, class_name: 'SpreeCmCommissioner::DynamicFieldOption', optional: true
|
6
6
|
|
7
|
-
validates :value, presence: true
|
8
7
|
validate :validate_value_format, if: -> { value.present? && dynamic_field.present? }
|
9
8
|
validate :validate_option_reference, if: -> { dynamic_field_option.present? }
|
9
|
+
validate :validate_phase_sequence
|
10
10
|
validate :check_required_fields_based_on_phase
|
11
|
+
validate :reject_empty_string_for_text_fields
|
12
|
+
|
13
|
+
after_commit :sync_guest_data_fill_stage_phase
|
11
14
|
|
12
15
|
private
|
13
16
|
|
@@ -15,6 +18,8 @@ module SpreeCmCommissioner
|
|
15
18
|
return if dynamic_field.blank?
|
16
19
|
|
17
20
|
case dynamic_field.data_type.to_s
|
21
|
+
when 'text', 'textarea'
|
22
|
+
errors.add(:value, "can't be blank") if value.is_a?(String) && value.strip == ''
|
18
23
|
when 'number'
|
19
24
|
errors.add(:value, 'must be a number') unless value.to_s.match?(/\A\d+\z/)
|
20
25
|
when 'boolean'
|
@@ -40,44 +45,49 @@ module SpreeCmCommissioner
|
|
40
45
|
errors.add(:dynamic_field_option, 'must belong to the same dynamic field')
|
41
46
|
end
|
42
47
|
|
43
|
-
def
|
44
|
-
|
45
|
-
end
|
48
|
+
def validate_phase_sequence
|
49
|
+
return if dynamic_field.blank? || guest.blank?
|
46
50
|
|
47
|
-
|
48
|
-
|
51
|
+
case dynamic_field.data_fill_stage.to_sym
|
52
|
+
when :post_registration
|
53
|
+
errors.add(:base, 'Must complete pre_registration fields first') unless guest.pre_registration_completed?
|
54
|
+
when :during_check_in
|
55
|
+
unless guest.post_registration_completed? || !guest.post_registration_fields?
|
56
|
+
errors.add(:base, 'Must complete post_registration fields first')
|
57
|
+
end
|
58
|
+
end
|
49
59
|
end
|
50
60
|
|
51
|
-
def
|
52
|
-
|
53
|
-
end
|
61
|
+
def field_required_in_current_phase?
|
62
|
+
return false if dynamic_field.blank? || guest.blank?
|
54
63
|
|
55
|
-
|
56
|
-
|
64
|
+
case dynamic_field.data_fill_stage.to_sym
|
65
|
+
when :pre_registration, :post_registration
|
57
66
|
guest.line_item.order.completed? && guest.check_in.blank?
|
67
|
+
when :during_check_in
|
68
|
+
guest.check_in.present?
|
69
|
+
else
|
70
|
+
false
|
71
|
+
end
|
58
72
|
end
|
59
73
|
|
60
|
-
def
|
61
|
-
dynamic_field.
|
62
|
-
|
74
|
+
def check_required_fields_based_on_phase
|
75
|
+
return if dynamic_field.blank? || value.present?
|
76
|
+
return unless field_required_in_current_phase?
|
77
|
+
|
78
|
+
errors.add(:value, I18n.t("guest_dynamic_field.errors.#{dynamic_field.data_fill_stage}_required"))
|
63
79
|
end
|
64
80
|
|
65
|
-
def
|
66
|
-
|
67
|
-
guest.check_in.present?
|
81
|
+
def sync_guest_data_fill_stage_phase
|
82
|
+
guest&.save_and_move_to_next_stage
|
68
83
|
end
|
69
84
|
|
70
|
-
def
|
71
|
-
return if dynamic_field.blank?
|
85
|
+
def reject_empty_string_for_text_fields
|
86
|
+
return if dynamic_field.blank?
|
87
|
+
return unless %w[text textarea].include?(dynamic_field.data_type.to_s)
|
88
|
+
return unless value.is_a?(String)
|
72
89
|
|
73
|
-
|
74
|
-
when :pre_registration
|
75
|
-
errors.add(:value, I18n.t('guest_dynamic_field.errors.pre_registration_required')) if pre_registration_required?
|
76
|
-
when :post_registration
|
77
|
-
errors.add(:value, I18n.t('guest_dynamic_field.errors.post_registration_required')) if post_registration_required?
|
78
|
-
when :during_check_in
|
79
|
-
errors.add(:value, I18n.t('guest_dynamic_field.errors.during_check_in_required')) if during_check_in_required?
|
80
|
-
end
|
90
|
+
errors.add(:value, "can't be blank") if value.strip == ''
|
81
91
|
end
|
82
92
|
end
|
83
93
|
end
|