spree_cm_commissioner 2.0.3.pre.pre5 → 2.0.3.pre.pre6

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/controllers/spree/api/v2/operator/event_charts_controller.rb +2 -8
  4. data/app/controllers/spree/api/v2/storefront/trip_places_controller.rb +29 -0
  5. data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +2 -0
  6. data/app/controllers/spree_cm_commissioner/admin/products_controller_decorator.rb +14 -0
  7. data/app/interactors/spree_cm_commissioner/transit/draft_order_creator.rb +17 -4
  8. data/app/models/concerns/spree_cm_commissioner/taxon_kind.rb +1 -1
  9. data/app/models/spree_cm_commissioner/address_decorator.rb +17 -3
  10. data/app/models/spree_cm_commissioner/order/address_book_decorator.rb +30 -12
  11. data/app/models/spree_cm_commissioner/order_decorator.rb +8 -1
  12. data/app/models/spree_cm_commissioner/place.rb +2 -0
  13. data/app/models/spree_cm_commissioner/product_decorator.rb +29 -0
  14. data/app/models/spree_cm_commissioner/saved_guest.rb +2 -0
  15. data/app/models/spree_cm_commissioner/trip.rb +4 -2
  16. data/app/models/spree_cm_commissioner/vendor_decorator.rb +3 -0
  17. data/app/overrides/spree/admin/products/_form/industry_taxons.html.erb.deface +26 -0
  18. data/app/overrides/spree/admin/products/_form/taxons.html.erb.deface +26 -0
  19. data/app/queries/spree_cm_commissioner/trip_query.rb +18 -18
  20. data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +2 -0
  21. data/app/serializers/spree/v2/storefront/trip_place_serializer.rb +10 -0
  22. data/app/serializers/spree/v2/storefront/trip_result_serializer.rb +2 -1
  23. data/app/serializers/spree/v2/storefront/trip_vehicle_serializer.rb +1 -1
  24. data/db/migrate/20250812015522_remove_unique_indexes_from_cm_user_identity_providers.rb +8 -0
  25. data/db/migrate/20250814073854_add_call_to_action_to_spree_products.rb +5 -0
  26. data/lib/spree_cm_commissioner/transit/leg.rb +14 -0
  27. data/lib/spree_cm_commissioner/transit/seat_selection.rb +9 -1
  28. data/lib/spree_cm_commissioner/trip_query_result.rb +34 -0
  29. data/lib/spree_cm_commissioner/trip_result.rb +8 -1
  30. data/lib/spree_cm_commissioner/version.rb +1 -1
  31. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de104e725d2f24613de706e3246bdc7d316520fac6eb9b21789a6b2f2c45ab06
4
- data.tar.gz: df640f714603d5d38bb33ad1b7ded9f66ca5126610bf2d8bdeba79890e7ac090
3
+ metadata.gz: 3386c245e402dd11660e1215a50dac52acfd59950f95afb03117bff6c56053a5
4
+ data.tar.gz: 551927bdc54caee21d0ca26601c2d42b5ee0cd14b5c4264da1a5ae3968270d4d
5
5
  SHA512:
6
- metadata.gz: 782d5091a568ece086d739de4c0e8b9e1a00a97302b68d1cc982b8f9a5718fb5c4cfa928f9310427e69e11fd34211c54893104fbacbc8293d8033bf4d2213ad0
7
- data.tar.gz: 8aeca4178f0bf6ce687b9cd2dffd5d648db14f1c2a5537964e6e728a73801362495d370d32509029408ec7d9d5d8fe88094fbd3da392b61e68446f4ad58b5e68
6
+ metadata.gz: f8c9f527ea17e43fabfcf7ecba55e70887a51a31a85849e918cf508cc8bc8d6a3b6f6165f534139fad9704902a7aea8875b58798514caf63882b2914534cc693
7
+ data.tar.gz: ca8d78212f84afd5091ac23617a88d49b096cc526e84a3aedc2c9b432fe02aaf55546a5a54bfe8c0704be30a72050d01c7be88a505b37abea3f4d634a1829ea5
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.0.3.pre.pre5)
37
+ spree_cm_commissioner (2.0.3.pre.pre6)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -11,17 +11,11 @@ module Spree
11
11
  types = ADDITIONAL_FIELDS + kyc_fields
12
12
 
13
13
  resources = types.map do |type|
14
- event_chart = SpreeCmCommissioner::EventChartQueries.new(
15
- taxon_id: params[:taxon_id],
16
- chart_type: type,
17
- refreshed: params[:refreshed] || false
18
- ).call
19
-
20
14
  # Create unique ID by using chart_type as the identifier
21
15
  SpreeCmCommissioner::EventChart.new(
22
16
  id: type.to_s,
23
- chart_type: event_chart.chart_type,
24
- product_charts: event_chart.product_charts
17
+ chart_type: type,
18
+ product_charts: nil
25
19
  )
26
20
  end
27
21
 
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class TripPlacesController < ::Spree::Api::V2::ResourceController
6
+ # override
7
+ def collection
8
+ base_scope = model_class.joins(:location_vendor_places).distinct
9
+
10
+ if params[:q].present?
11
+ @search = base_scope.ransack(params[:q])
12
+ @search.result
13
+ else
14
+ base_scope
15
+ end
16
+ end
17
+
18
+ def model_class
19
+ SpreeCmCommissioner::Place
20
+ end
21
+
22
+ def collection_serializer
23
+ Spree::V2::Storefront::TripPlaceSerializer
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -8,6 +8,8 @@ module Spree
8
8
  origin_id: params[:origin_id],
9
9
  destination_id: params[:destination_id],
10
10
  date: params[:date],
11
+ vendor_id: params[:vendor_id],
12
+ number_of_guests: params[:number_of_guests],
11
13
  params: params
12
14
  ).call
13
15
 
@@ -5,6 +5,7 @@ module SpreeCmCommissioner
5
5
  # spree update user sign_in_count
6
6
  base.around_action :set_writing_role, only: %i[index]
7
7
  base.after_action :set_tenant_after_update, only: %i[update]
8
+ base.before_action :merge_industry_taxons_into_taxons, only: [:update]
8
9
  end
9
10
 
10
11
  # Override
@@ -50,6 +51,19 @@ module SpreeCmCommissioner
50
51
 
51
52
  protected
52
53
 
54
+ def merge_industry_taxons_into_taxons
55
+ return if params[:product].blank?
56
+
57
+ industry_ids = Array(params[:product][:industry_taxon_ids]).compact_blank
58
+ taxon_ids = Array(params[:product][:taxon_ids]).compact_blank
59
+
60
+ merged_ids = (taxon_ids + industry_ids).uniq
61
+
62
+ params[:product][:taxon_ids] = merged_ids
63
+
64
+ params[:product].delete(:industry_taxon_ids)
65
+ end
66
+
53
67
  def find_resource
54
68
  product_scope.with_deleted.includes(vendor: :tenant).friendly.find(params[:id])
55
69
  end
@@ -53,6 +53,8 @@ module SpreeCmCommissioner
53
53
 
54
54
  legs.each do |leg|
55
55
  leg_line_items = build_line_items_for!(leg, order, current_leg_date)
56
+ leg_line_items = insert_saved_guests_per_line_items_leg(leg_line_items)
57
+
56
58
  all_line_items.concat(leg_line_items)
57
59
  current_leg_date = leg_line_items.last.to_date.to_date if leg_line_items.any?
58
60
  end
@@ -85,13 +87,11 @@ module SpreeCmCommissioner
85
87
  }
86
88
  )
87
89
 
88
- build_guests!(line_item, seat_selections)
89
-
90
- line_item
90
+ build_guests_for!(line_item, seat_selections)
91
91
  end
92
92
  end
93
93
 
94
- def build_guests!(line_item, seat_selections)
94
+ def build_guests_for!(line_item, seat_selections)
95
95
  block_ids = seat_selections.flat_map(&:block_ids)
96
96
 
97
97
  if block_ids.any? && block_ids.size != line_item.quantity
@@ -101,6 +101,19 @@ module SpreeCmCommissioner
101
101
  Array.new(line_item.quantity) do |index|
102
102
  line_item.guests.new(block_id: block_ids[index])
103
103
  end
104
+
105
+ line_item
106
+ end
107
+
108
+ def insert_saved_guests_per_line_items_leg(line_items)
109
+ line_items.flat_map(&:guests).each_with_index do |guest, index|
110
+ context.saved_guests ||= []
111
+ context.saved_guests << SpreeCmCommissioner::SavedGuest.new if context.saved_guests[index].blank?
112
+
113
+ guest.saved_guest = context.saved_guests[index]
114
+ end
115
+
116
+ line_items
104
117
  end
105
118
 
106
119
  def calculate_line_item_duration!(date:, departure_time:, arrival_time:)
@@ -3,7 +3,7 @@ module SpreeCmCommissioner
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- enum kind: { category: 0, cms: 1, event: 2, occupation: 3, nationality: 4, organization: 5 }
6
+ enum kind: { category: 0, cms: 1, event: 2, occupation: 3, nationality: 4, organization: 5, transit: 6, industry: 7 }
7
7
  end
8
8
  end
9
9
  end
@@ -8,6 +8,12 @@ module SpreeCmCommissioner
8
8
  base.validates :country, presence: true, if: :require_country?
9
9
  end
10
10
 
11
+ # override to return false instead of true
12
+ # in case no country
13
+ def require_zipcode?
14
+ country ? country.zipcode_required? : false
15
+ end
16
+
11
17
  def require_address1?
12
18
  false
13
19
  end
@@ -22,9 +28,17 @@ module SpreeCmCommissioner
22
28
  end
23
29
  end
24
30
 
25
- unless Spree::Address.included_modules.include?(SpreeCmCommissioner::AddressDecorator)
26
- # remove validators for the specified attributes
27
- %i[address1 city country].each { |attr| Spree::Address._validators.delete(attr) }
31
+ if Spree::Address.included_modules.exclude?(SpreeCmCommissioner::AddressDecorator)
32
+ keys = %i[address1 city country]
33
+
34
+ Spree::Address._validators.reject! { |key, _| keys.include?(key) }
35
+ Spree::Address._validate_callbacks.each do |c|
36
+ next unless c.filter.respond_to?(:attributes)
37
+
38
+ c.filter.attributes.delete(:address1)
39
+ c.filter.attributes.delete(:city)
40
+ c.filter.attributes.delete(:country)
41
+ end
28
42
 
29
43
  Spree::Address.prepend(SpreeCmCommissioner::AddressDecorator)
30
44
  end
@@ -5,30 +5,48 @@
5
5
  module SpreeCmCommissioner
6
6
  module Order
7
7
  module AddressBookDecorator
8
- def guest_order? = user.blank?
9
-
10
8
  # override
11
9
  def bill_address_attributes=(attributes)
12
- attributes[:id] = bill_address&.id if attributes[:id].blank?
13
- return if guest_order? && !basic_info_included?(attributes)
10
+ attributes[:id] = bill_address_id if attributes[:id].blank?
11
+ attributes[:phone] = intel_phone_number if intel_phone_number.present?
14
12
 
15
- super
13
+ self.bill_address = assign_address_attributes(attributes)
14
+ user.bill_address = bill_address if user && user.bill_address.nil?
16
15
  end
17
16
 
18
17
  # override
19
18
  def ship_address_attributes=(attributes)
20
- attributes[:id] = bill_address&.id if attributes[:id].blank?
21
- return if guest_order? && !basic_info_included?(attributes)
19
+ attributes[:id] = ship_address_id if attributes[:id].blank?
20
+ attributes[:phone] = intel_phone_number if intel_phone_number.present?
22
21
 
23
- super
22
+ self.ship_address = assign_address_attributes(attributes)
23
+ user.ship_address = ship_address if user && user.ship_address.nil?
24
24
  end
25
25
 
26
- private
26
+ def assign_address_attributes(attributes = {})
27
+ return if attributes.blank?
28
+
29
+ attributes.transform_values! { |v| v == '' ? nil : v }
30
+ attributes = attributes.to_h.symbolize_keys
27
31
 
28
- def basic_info_included?(attributes)
29
- return true if attributes[:id].present?
32
+ existing_address = attributes[:id].present? ? find_existing_address(attributes[:id]) : nil
33
+
34
+ if existing_address&.editable?
35
+ existing_address.assign_attributes(attributes)
36
+ existing_address
37
+ else
38
+ ::Spree::Address.new(attributes.except(:id, :updated_at, :created_at))
39
+ end
40
+ end
30
41
 
31
- %i[firstname lastname phone].all? { |key| attributes[key].present? }
42
+ def find_existing_address(id)
43
+ if user.present?
44
+ ::Spree::Address.where('(user_id = ? OR user_id IS NULL) AND deleted_at IS NULL', user.id)
45
+ .find_by(id: id)
46
+ else
47
+ ::Spree::Address.where(user_id: nil, deleted_at: nil)
48
+ .find_by(id: id)
49
+ end
32
50
  end
33
51
  end
34
52
  end
@@ -43,7 +43,12 @@ module SpreeCmCommissioner
43
43
  base.has_many :vendors, through: :products, class_name: 'Spree::Vendor'
44
44
  base.has_many :taxons, through: :products, class_name: 'Spree::Taxon'
45
45
  base.has_many :guests, through: :line_items, class_name: 'SpreeCmCommissioner::Guest'
46
- base.has_many :saved_guests, -> { distinct }, through: :guests, class_name: 'SpreeCmCommissioner::SavedGuest'
46
+
47
+ base.has_many :saved_guests, -> { distinct.reorder('cm_saved_guests.created_at DESC') },
48
+ through: :guests,
49
+ source: :saved_guest,
50
+ class_name: 'SpreeCmCommissioner::SavedGuest'
51
+
47
52
  base.has_many :blocks, through: :guests, class_name: 'SpreeCmCommissioner::Block', source: :block
48
53
  base.has_many :reserved_blocks, through: :guests, class_name: 'SpreeCmCommissioner::ReservedBlock'
49
54
  base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variants
@@ -53,6 +58,8 @@ module SpreeCmCommissioner
53
58
  base.whitelisted_ransackable_associations |= %w[customer taxon payments guests invoice]
54
59
  base.whitelisted_ransackable_attributes |= %w[intel_phone_number phone_number email number state]
55
60
 
61
+ base.accepts_nested_attributes_for :saved_guests, allow_destroy: true
62
+
56
63
  def base.search_by_qr_data!(data)
57
64
  token = data.match(/^R\d{9,}-([A-Za-z0-9_\-]+)$/)&.captures
58
65
 
@@ -14,6 +14,8 @@ module SpreeCmCommissioner
14
14
  has_many :product_places, class_name: 'SpreeCmCommissioner::ProductPlace', dependent: :destroy
15
15
  has_many :products, through: :product_places
16
16
 
17
+ has_many :location_vendor_places, -> { where(place_type: :location) }, class_name: 'SpreeCmCommissioner::VendorPlace'
18
+
17
19
  has_many :children, -> { order(:lft) }, class_name: 'SpreeCmCommissioner::Place', foreign_key: :parent_id, dependent: :destroy
18
20
 
19
21
  has_many :taxon_places, class_name: 'SpreeCmCommissioner::TaxonPlace', dependent: :destroy
@@ -48,6 +48,9 @@ module SpreeCmCommissioner
48
48
 
49
49
  base.belongs_to :event, class_name: 'Spree::Taxon', optional: true
50
50
 
51
+ base.has_many :industry_classifications, -> { joins(:taxon).where(spree_taxons: { kind: :industry }) }, class_name: 'Spree::Classification'
52
+ base.has_many :industry_taxons, through: :industry_classifications, source: :taxon
53
+
51
54
  base.scope :min_price, lambda { |vendor|
52
55
  joins(:prices_including_master)
53
56
  .where(vendor_id: vendor.id, product_type: vendor.primary_product_type)
@@ -74,6 +77,16 @@ module SpreeCmCommissioner
74
77
  base.after_update :sync_event_id_to_children, if: :saved_change_to_event_id?
75
78
 
76
79
  base.enum purchasable_on: { both: 0, web: 1, app: 2 }
80
+ base.enum call_to_action: { buy_now: 0,
81
+ book_now: 1,
82
+ reserve: 2,
83
+ register: 3,
84
+ apply: 4,
85
+ get_ticket: 5,
86
+ secure_your_seat: 6,
87
+ join_the_event: 7,
88
+ request_to_book: 8
89
+ }
77
90
 
78
91
  base.multi_tenant :tenant, class_name: 'SpreeCmCommissioner::Tenant'
79
92
  base.before_save :set_tenant
@@ -83,6 +96,22 @@ module SpreeCmCommissioner
83
96
  "#{Spree::Store.default.formatted_url}/tickets/#{slug}"
84
97
  end
85
98
 
99
+ def action_button
100
+ return :request_to_book if need_confirmation?
101
+ return :reserve if free?
102
+ return call_to_action.to_sym if call_to_action.present?
103
+
104
+ :buy_now
105
+ end
106
+
107
+ def free?
108
+ return false if prices_including_master.empty?
109
+
110
+ prices_including_master.all? do |master_price|
111
+ master_price.price.zero? && (master_price.compare_at_price.nil? || master_price.compare_at_price.zero?)
112
+ end
113
+ end
114
+
86
115
  def dynamic_fields_by_collection_phase
87
116
  {
88
117
  pre_registration: dynamic_fields.pre_registration.order(:position),
@@ -9,5 +9,7 @@ module SpreeCmCommissioner
9
9
  belongs_to :nationality, class_name: 'Spree::Taxon', optional: true
10
10
 
11
11
  has_many :guests, class_name: 'SpreeCmCommissioner::Guest', dependent: :nullify
12
+
13
+ accepts_nested_attributes_for :guests, allow_destroy: true
12
14
  end
13
15
  end
@@ -8,6 +8,7 @@ module SpreeCmCommissioner
8
8
 
9
9
  belongs_to :vendor, class_name: 'Spree::Vendor', inverse_of: :trips, optional: false
10
10
  belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional: false
11
+
11
12
  belongs_to :vehicle, class_name: 'SpreeCmCommissioner::Vehicle', optional: false
12
13
 
13
14
  belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
@@ -17,8 +18,8 @@ module SpreeCmCommissioner
17
18
  has_many :blazer_queries, through: :trip_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
18
19
 
19
20
  has_many :trip_stops, class_name: 'SpreeCmCommissioner::TripStop', dependent: :destroy
20
- has_many :boarding_trip_stops, -> { boarding }, class_name: 'SpreeCmCommissioner::TripStop'
21
- has_many :drop_off_trip_stops, -> { drop_off }, class_name: 'SpreeCmCommissioner::TripStop'
21
+ has_many :boarding_trip_stops, -> { boarding.order('departure_time ASC NULLS LAST, id ASC') }, class_name: 'SpreeCmCommissioner::TripStop'
22
+ has_many :drop_off_trip_stops, -> { drop_off.order('arrival_time ASC NULLS LAST') }, class_name: 'SpreeCmCommissioner::TripStop'
22
23
 
23
24
  has_many :variants, through: :product
24
25
  has_many :inventory_items, through: :variants
@@ -32,6 +33,7 @@ module SpreeCmCommissioner
32
33
  accepts_nested_attributes_for :trip_stops, :product, allow_destroy: true
33
34
 
34
35
  self.whitelisted_ransackable_associations = %w[product vehicle]
36
+ self.whitelisted_ransackable_attributes = %w[origin_place_id destination_place_id]
35
37
 
36
38
  def convert_duration_to_seconds
37
39
  return if hours.blank? && minutes.blank? && seconds.blank?
@@ -84,6 +84,9 @@ module SpreeCmCommissioner
84
84
 
85
85
  base.has_many :vehicles, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
86
86
  base.has_many :trips, class_name: 'SpreeCmCommissioner::Trip', dependent: :destroy
87
+ base.has_many :trip_stops, through: :trips, class_name: 'SpreeCmCommissioner::TripStop'
88
+ base.has_many :boarding_trip_stops, through: :trips, class_name: 'SpreeCmCommissioner::TripStop', source: :boarding_trip_stops
89
+ base.has_many :drop_off_trip_stops, through: :trips, class_name: 'SpreeCmCommissioner::TripStop', source: :drop_off_trip_stops
87
90
 
88
91
  base.validates :account_name, :account_number, presence: true, if: lambda {
89
92
  payment_qrcode.present? && Spree::Store.default.code.include?('billing')
@@ -0,0 +1,26 @@
1
+ <!-- insert_after "[data-hook='admin_product_form_taxons']" -->
2
+
3
+ <div data-hook="admin_product_form_industry_taxons">
4
+ <%= f.field_container :industry_taxons do %>
5
+ <%= f.label :industry_taxon_ids, Spree.t(:Industries) %>
6
+
7
+ <% if can? :modify, Spree::Classification %>
8
+ <%= f.select :industry_taxon_ids, options_from_collection_for_select(@product.industry_taxons, :id, :pretty_name, @product.industry_taxon_ids),
9
+ { include_hidden: true },
10
+ multiple: true,
11
+ data: { autocomplete_url_value: 'taxons_api_v2',
12
+ autocomplete_return_attr_value: 'pretty_name',
13
+ autocomplete_multiple_value: true,
14
+ autocomplete_additional_url_params_value: "q[kind_eq]=7" } %>
15
+ <% elsif @product.industry_taxons.any? %>
16
+ <ul class="text_list">
17
+ <% @product.industry_taxons.each do |taxon| %>
18
+ <li><%= taxon.name %></li>
19
+ <% end %>
20
+ </ul>
21
+ <% else %>
22
+ <div class="alert alert-info"><%= Spree.t(:no_resource_found, resource: :taxons) %></div>
23
+ <% end %>
24
+
25
+ <% end %>
26
+ </div>
@@ -0,0 +1,26 @@
1
+ <!-- replace "[data-hook='admin_product_form_taxons']" -->
2
+
3
+ <div data-hook="admin_product_form_taxons">
4
+ <%= f.field_container :taxons do %>
5
+ <%= f.label :taxon_ids, Spree.t(:taxons) %>
6
+
7
+ <% if can? :modify, Spree::Classification %>
8
+ <%= f.select :taxon_ids, options_from_collection_for_select(@product.taxons, :id, :pretty_name, @product.taxon_ids.reject { |id| @product.industry_taxon_ids.include?(id) }),
9
+ { include_hidden: true },
10
+ multiple: true,
11
+ data: { autocomplete_url_value: 'taxons_api_v2',
12
+ autocomplete_return_attr_value: 'pretty_name',
13
+ autocomplete_multiple_value: true,
14
+ autocomplete_additional_url_params_value: "q[kind_not_eq]=7" } %>
15
+ <% elsif @product.taxons.any? %>
16
+ <ul class="text_list">
17
+ <% @product.taxons.each do |taxon| %>
18
+ <li><%= taxon.name %></li>
19
+ <% end %>
20
+ </ul>
21
+ <% else %>
22
+ <div class="alert alert-info"><%= Spree.t(:no_resource_found, resource: :taxons) %></div>
23
+ <% end %>
24
+
25
+ <% end %>
26
+ </div>
@@ -1,14 +1,16 @@
1
1
  module SpreeCmCommissioner
2
2
  class TripQuery
3
- attr_reader :origin_id, :destination_id, :date
3
+ attr_reader :origin_id, :destination_id, :date, :vendor_id, :number_of_guests, :params
4
4
 
5
- def initialize(origin_id:, destination_id:, date:, params: {})
5
+ def initialize(origin_id:, destination_id:, date:, vendor_id: nil, number_of_guests: nil, params: {}) # rubocop:disable Metrics/ParameterLists
6
6
  @origin_id = origin_id
7
7
  @destination_id = destination_id
8
8
  @date = date.to_date == Time.zone.now.to_date ? Time.zone.now : Time.zone.parse(date.to_s)
9
+ @vendor_id = vendor_id
10
+ @number_of_guests = number_of_guests || 1
9
11
  @params = params
10
12
  @page = (params[:page] || 1).to_i
11
- @per_page = (params[:per_page] || 25).to_i
13
+ @per_page = params[:per_page]
12
14
  end
13
15
 
14
16
  def call
@@ -17,15 +19,16 @@ module SpreeCmCommissioner
17
19
  paginated_relation = direct_trips
18
20
  return Kaminari.paginate_array([]) if paginated_relation.empty?
19
21
 
20
- results_array = paginated_relation.map do |trip|
22
+ unique_trips = paginated_relation.uniq(&:id)
23
+ results_array = unique_trips.map do |trip|
21
24
  result = build_trip_result(trip)
22
25
  SpreeCmCommissioner::TripQueryResult.new([result])
23
26
  end
24
27
 
25
28
  Kaminari.paginate_array(
26
29
  results_array,
27
- total_count: paginated_relation.total_count,
28
- limit: @per_page,
30
+ total_count: unique_trips.size,
31
+ limit: unique_trips.size,
29
32
  offset: paginated_relation.offset_value
30
33
  )
31
34
  end
@@ -51,17 +54,17 @@ module SpreeCmCommissioner
51
54
  INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = cm_trips.id AND drop_off.stop_type = 1
52
55
  INNER JOIN cm_places AS origin_places ON origin_places.id = cm_trips.origin_place_id
53
56
  INNER JOIN cm_places AS dest_places ON dest_places.id = cm_trips.destination_place_id
54
- INNER JOIN spree_products ON spree_products.id = cm_trips.product_id
55
- INNER JOIN spree_variants AS master ON master.product_id = spree_products.id AND master.is_master = true
57
+ INNER JOIN spree_variants AS master ON master.product_id = cm_trips.product_id AND master.is_master = true
56
58
  INNER JOIN spree_prices AS prices ON prices.variant_id = master.id AND prices.deleted_at IS NULL
57
59
  SQL
58
- .where('(boarding.location_place_id = ? OR boarding.stop_place_id = ?)
59
- AND (drop_off.location_place_id = ? OR drop_off.stop_place_id = ?)', origin_id, origin_id, destination_id, destination_id
60
+ .where('(boarding.location_place_id = ? OR boarding.stop_place_id = ? OR cm_trips.origin_place_id = ?)
61
+ AND (drop_off.location_place_id = ? OR drop_off.stop_place_id = ? OR cm_trips.destination_place_id = ?)',
62
+ origin_id, origin_id, origin_id, destination_id, destination_id, destination_id
60
63
  )
61
- .joins("LEFT JOIN (#{@inventory_sql.to_sql}) iv ON cm_trips.product_id = iv.product_id")
64
+ .joins("INNER JOIN (#{@inventory_sql.to_sql}) iv ON cm_trips.product_id = iv.product_id")
62
65
 
63
- result = result.where(vendor_id: @params[:vendor_id]) if @params[:vendor_id].present?
64
- result.page(@page).per(@per_page)
66
+ result = result.where(vendor_id: vendor_id) if vendor_id.present?
67
+ @per_page.to_i.positive? ? result.page(@page).per(@per_page) : result.page(@page)
65
68
  end
66
69
 
67
70
  def product_inventory_totals
@@ -72,16 +75,12 @@ module SpreeCmCommissioner
72
75
  SUM(cm_inventory_items.quantity_available) AS quantity_available'
73
76
  )
74
77
  .joins(variants: :inventory_items)
75
- .where(cm_inventory_items: { inventory_date: date.to_date })
78
+ .where('cm_inventory_items.inventory_date = ? AND cm_inventory_items.quantity_available >= ?', @date.to_date, @number_of_guests)
76
79
  .group('spree_products.id')
77
80
  end
78
81
 
79
82
  private
80
83
 
81
- def cached_inventory_sql
82
- @inventory_sql ||= product_inventory_totals
83
- end
84
-
85
84
  def build_trip_result(trip)
86
85
  vehicle = trip&.vehicle
87
86
  vendor = trip&.vendor
@@ -90,6 +89,7 @@ module SpreeCmCommissioner
90
89
  departure_time: trip&.departure_time,
91
90
  duration: trip&.duration,
92
91
  allow_seat_selection: trip&.allow_seat_selection,
92
+ route_type: trip&.route_type,
93
93
  origin_place: {
94
94
  id: trip&.origin_place_id,
95
95
  name: trip&.origin_place_name
@@ -44,6 +44,8 @@ module Spree
44
44
  end
45
45
 
46
46
  base.cache_options store: nil
47
+
48
+ base.attribute :action_button, &:action_button
47
49
  end
48
50
  end
49
51
  end
@@ -0,0 +1,10 @@
1
+ module Spree
2
+ module V2
3
+ module Storefront
4
+ class TripPlaceSerializer < BaseSerializer
5
+ attributes :reference, :name, :vicinity, :lat, :lon, :icon, :url, :rating,
6
+ :formatted_phone_number, :formatted_address, :address_components, :types
7
+ end
8
+ end
9
+ end
10
+ end
@@ -5,8 +5,9 @@ module Spree
5
5
  set_type :trip_result
6
6
 
7
7
  attributes :origin_place, :destination_place, :boarding, :drop_off, :price, :compare_at_amount, :currency, :quantity_available, :max_capacity,
8
- :allow_seat_selection, :departure_time
8
+ :allow_seat_selection, :departure_time, :route_type
9
9
  attribute :duration_in_hms, &:duration_in_hms
10
+ attribute :arrival_time, &:arrival_time
10
11
  belongs_to :vendor, serializer: Spree::V2::Storefront::TripVendorSerializer
11
12
  belongs_to :vehicle, serializer: Spree::V2::Storefront::TripVehicleSerializer
12
13
  has_many :amenities, serializer: Spree::V2::Storefront::AmenitySerializer
@@ -4,7 +4,7 @@ module Spree
4
4
  class TripVehicleSerializer < BaseSerializer
5
5
  set_type :vehicle
6
6
 
7
- attributes :id, :code
7
+ attributes :id, :code, :vehicle_type
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,8 @@
1
+ class RemoveUniqueIndexesFromCmUserIdentityProviders < ActiveRecord::Migration[7.0]
2
+ def change
3
+ if index_exists?(:cm_user_identity_providers, [:identity_type, :sub], name: "index_cm_user_identity_providers_on_identity_type_and_sub")
4
+ remove_index :cm_user_identity_providers,
5
+ name: "index_cm_user_identity_providers_on_identity_type_and_sub"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ class AddCallToActionToSpreeProducts < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :spree_products, :call_to_action, :integer, null: true, if_not_exists: true
4
+ end
5
+ end
@@ -19,5 +19,19 @@ module SpreeCmCommissioner::Transit
19
19
  seat_selections: (hash[:seat_selections] || []).map { |seat_selection| SeatSelection.from_hash(seat_selection) }
20
20
  )
21
21
  end
22
+
23
+ def quantity
24
+ @seat_selections.sum(&:quantity).to_i
25
+ end
26
+
27
+ def to_h
28
+ {
29
+ direction: @direction,
30
+ trip_id: @trip_id,
31
+ boarding_trip_stop_id: @boarding_trip_stop_id,
32
+ drop_off_trip_stop_id: @drop_off_trip_stop_id,
33
+ seat_selections: @seat_selections.map(&:to_h)
34
+ }
35
+ end
22
36
  end
23
37
  end
@@ -4,7 +4,7 @@ module SpreeCmCommissioner::Transit
4
4
 
5
5
  def initialize(options = {})
6
6
  @variant_id = options[:variant_id]
7
- @quantity = options[:quantity]
7
+ @quantity = options[:quantity]&.to_i
8
8
  @block_ids = options[:block_ids] || []
9
9
  end
10
10
 
@@ -15,5 +15,13 @@ module SpreeCmCommissioner::Transit
15
15
  block_ids: hash[:block_ids] || [] # empty when no seat selection.
16
16
  )
17
17
  end
18
+
19
+ def to_h
20
+ {
21
+ variant_id: @variant_id,
22
+ quantity: @quantity,
23
+ block_ids: @block_ids
24
+ }
25
+ end
18
26
  end
19
27
  end
@@ -26,5 +26,39 @@ module SpreeCmCommissioner
26
26
  def max_capacity
27
27
  @trips.map(&:max_capacity).compact.min
28
28
  end
29
+
30
+ def total_price
31
+ @trips.map(&:price).compact.sum
32
+ end
33
+
34
+ def total_duration
35
+ @trips.map(&:duration).compact.sum
36
+ end
37
+
38
+ def total_duration_in_hms
39
+ total_seconds = total_duration || 0
40
+ return { hours: 0, minutes: 0, seconds: 0 } if total_seconds.nil? || total_seconds <= 0
41
+
42
+ hours = total_seconds / 3600
43
+ minutes = (total_seconds % 3600) / 60
44
+ seconds = total_seconds % 60
45
+ { hours: hours, minutes: minutes, seconds: seconds }
46
+ end
47
+
48
+ def departure_time
49
+ @trips.first.departure_time
50
+ end
51
+
52
+ def arrival_time
53
+ @trips.last.arrival_time
54
+ end
55
+
56
+ def vendors
57
+ @trips.map(&:vendor).uniq
58
+ end
59
+
60
+ def vehicle_types
61
+ @trips.map(&:vehicle).map(&:vehicle_type).uniq
62
+ end
29
63
  end
30
64
  end
@@ -1,6 +1,7 @@
1
1
  module SpreeCmCommissioner
2
2
  class TripResult
3
- attr_accessor :id, :origin_place, :destination_place, :allow_seat_selection, :departure_time, :duration,
3
+ attr_accessor :id, :origin_place, :destination_place, :allow_seat_selection,
4
+ :departure_time, :duration, :route_type,
4
5
  :vehicle_id, :vehicle, :vendor_id, :vendor,
5
6
  :quantity_available, :max_capacity, :boarding, :drop_off,
6
7
  :product_id, :amenities, :price, :compare_at_amount, :currency
@@ -29,5 +30,11 @@ module SpreeCmCommissioner
29
30
  seconds = duration % 60
30
31
  { hours: hours, minutes: minutes, seconds: seconds }
31
32
  end
33
+
34
+ def arrival_time
35
+ return nil if departure_time.nil? || duration.nil?
36
+
37
+ departure_time + duration.seconds
38
+ end
32
39
  end
33
40
  end
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '2.0.3-pre5'.freeze
2
+ VERSION = '2.0.3-pre6'.freeze
3
3
 
4
4
  module_function
5
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_cm_commissioner
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3.pre.pre5
4
+ version: 2.0.3.pre.pre6
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-12 00:00:00.000000000 Z
11
+ date: 2025-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spree
@@ -941,6 +941,7 @@ files:
941
941
  - app/controllers/spree/api/v2/storefront/reset_passwords_controller.rb
942
942
  - app/controllers/spree/api/v2/storefront/s3_signed_urls_controller.rb
943
943
  - app/controllers/spree/api/v2/storefront/self_check_in_controller.rb
944
+ - app/controllers/spree/api/v2/storefront/trip_places_controller.rb
944
945
  - app/controllers/spree/api/v2/storefront/trip_search_controller.rb
945
946
  - app/controllers/spree/api/v2/storefront/user_account_linkages_controller.rb
946
947
  - app/controllers/spree/api/v2/storefront/user_contacts_controller.rb
@@ -1541,6 +1542,7 @@ files:
1541
1542
  - app/overrides/spree/admin/products/_form/allow_self_check_in.html.erb.deface
1542
1543
  - app/overrides/spree/admin/products/_form/available_on.html.erb.deface
1543
1544
  - app/overrides/spree/admin/products/_form/discontinue_on.html.erb.deface
1545
+ - app/overrides/spree/admin/products/_form/industry_taxons.html.erb.deface
1544
1546
  - app/overrides/spree/admin/products/_form/need_confirmation_checkbox.html.erb.deface
1545
1547
  - app/overrides/spree/admin/products/_form/option_types.html.erb.deface
1546
1548
  - app/overrides/spree/admin/products/_form/product_types.html.erb.deface
@@ -1549,6 +1551,7 @@ files:
1549
1551
  - app/overrides/spree/admin/products/_form/required_self_check_in_location.html.erb.deface
1550
1552
  - app/overrides/spree/admin/products/_form/reveal_description_to_user.html.erb.deface
1551
1553
  - app/overrides/spree/admin/products/_form/subscribable_checkbox.html.erb.deface
1554
+ - app/overrides/spree/admin/products/_form/taxons.html.erb.deface
1552
1555
  - app/overrides/spree/admin/products/_form/use_video_as_default.html.erb.deface
1553
1556
  - app/overrides/spree/admin/products/edit/clear_cache_button.html.erb.deface
1554
1557
  - app/overrides/spree/admin/products/index/product_vendor_link.html.erb.deface
@@ -1705,6 +1708,7 @@ files:
1705
1708
  - app/serializers/spree/v2/storefront/stock_location_serializer_decorator.rb
1706
1709
  - app/serializers/spree/v2/storefront/store_promotion_serializer.rb
1707
1710
  - app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb
1711
+ - app/serializers/spree/v2/storefront/trip_place_serializer.rb
1708
1712
  - app/serializers/spree/v2/storefront/trip_query_result_serializer.rb
1709
1713
  - app/serializers/spree/v2/storefront/trip_result_serializer.rb
1710
1714
  - app/serializers/spree/v2/storefront/trip_vehicle_serializer.rb
@@ -2686,6 +2690,8 @@ files:
2686
2690
  - db/migrate/20250731062816_add_departure_time_and_arrival_time_to_trip_stop.rb
2687
2691
  - db/migrate/20250807033035_create_spree_cm_commissioner_saved_guests.rb
2688
2692
  - db/migrate/20250808054835_add_saved_guest_reference_to_cm_blocks.rb
2693
+ - db/migrate/20250812015522_remove_unique_indexes_from_cm_user_identity_providers.rb
2694
+ - db/migrate/20250814073854_add_call_to_action_to_spree_products.rb
2689
2695
  - docker-compose.yml
2690
2696
  - docs/option_types/attr_types.md
2691
2697
  - docs/private_key.pem