spree_cm_commissioner 2.5.0.pre.pre13 → 2.5.0.pre.pre14

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +4 -0
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/spree/admin/base_controller_decorator.rb +7 -33
  5. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +7 -51
  6. data/app/controllers/spree/api/v2/storefront/intercity_taxi/distance_calculator_controller.rb +47 -0
  7. data/app/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +7 -5
  8. data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +2 -2
  9. data/app/controllers/spree/api/v2/tenant/intercity_taxi/distance_calculator_controller.rb +47 -0
  10. data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +7 -5
  11. data/app/controllers/spree/api/v2/tenant/trip_search_controller.rb +2 -2
  12. data/app/interactors/spree_cm_commissioner/google_routes_distance_calculator.rb +0 -13
  13. data/app/interactors/spree_cm_commissioner/invalidate_cache_request.rb +3 -3
  14. data/app/interactors/spree_cm_commissioner/vehicle_type_updater.rb +41 -0
  15. data/app/jobs/spree_cm_commissioner/invalidate_cache_request_job.rb +1 -1
  16. data/app/mailers/spree/order_mailer_decorator.rb +22 -1
  17. data/app/models/concerns/spree_cm_commissioner/line_item_seat_selection.rb +40 -0
  18. data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +1 -1
  19. data/app/models/concerns/spree_cm_commissioner/vehicle_kind.rb +21 -0
  20. data/app/models/spree_cm_commissioner/adjustable/adjuster/pricing_action.rb +25 -0
  21. data/app/models/spree_cm_commissioner/guest.rb +1 -1
  22. data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +3 -0
  23. data/app/models/spree_cm_commissioner/homepage_background_app_image.rb +1 -1
  24. data/app/models/spree_cm_commissioner/invite_guest.rb +2 -5
  25. data/app/models/spree_cm_commissioner/line_item_decorator.rb +2 -2
  26. data/app/models/spree_cm_commissioner/option_type_decorator.rb +4 -1
  27. data/app/models/spree_cm_commissioner/option_value_decorator.rb +4 -1
  28. data/app/models/spree_cm_commissioner/{option_value_vehicle.rb → option_value_vehicle_type.rb} +2 -2
  29. data/app/models/spree_cm_commissioner/trip.rb +7 -6
  30. data/app/models/spree_cm_commissioner/vehicle.rb +8 -20
  31. data/app/models/spree_cm_commissioner/vehicle_type.rb +28 -0
  32. data/app/models/spree_cm_commissioner/vehicle_type_option_type.rb +6 -0
  33. data/app/models/spree_cm_commissioner/vendor_decorator.rb +1 -0
  34. data/app/overrides/spree/admin/products/edit/clear_cache_button.html.erb.deface +1 -1
  35. data/app/overrides/spree/admin/taxons/edit/clear_cache_button.html.erb.deface +3 -2
  36. data/app/queries/spree_cm_commissioner/trip_query.rb +5 -5
  37. data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_update_schema.rb +2 -0
  38. data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +12 -0
  39. data/app/serializers/spree/v2/tenant/line_item_serializer.rb +14 -1
  40. data/app/serializers/spree_cm_commissioner/v2/storefront/distance_serializer.rb +26 -0
  41. data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_line_item_serializer.rb +3 -3
  42. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +2 -2
  43. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_serializer.rb +1 -1
  44. data/app/serializers/spree_cm_commissioner/v2/storefront/{trip_vehicle_serializer.rb → trip_vehicle_type_serializer.rb} +2 -4
  45. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +98 -0
  46. data/app/services/spree_cm_commissioner/calculate_distance.rb +279 -0
  47. data/app/services/spree_cm_commissioner/cart/recalculate_decorator.rb +28 -1
  48. data/app/services/spree_cm_commissioner/guests/claim_invite_guest_service.rb +65 -0
  49. data/app/services/spree_cm_commissioner/intercity_taxi_order/calculate_distance.rb +224 -0
  50. data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +30 -3
  51. data/app/services/spree_cm_commissioner/line_items/apply_pricing_models.rb +27 -0
  52. data/app/services/spree_cm_commissioner/pricing_models/apply.rb +4 -12
  53. data/app/services/spree_cm_commissioner/signing/sign_data.rb +42 -0
  54. data/app/services/spree_cm_commissioner/signing/verify_signature.rb +52 -0
  55. data/app/services/spree_cm_commissioner/transit/export_order.rb +146 -0
  56. data/app/services/spree_cm_commissioner/update_guest_service.rb +129 -0
  57. data/app/views/spree/admin/homepage_section/edit.html.erb +7 -2
  58. data/app/views/spree/order_mailer/cancel_email.html.erb +19 -0
  59. data/app/views/spree_cm_commissioner/cancel_order_mailer/_cancel_email.html.erb +12 -0
  60. data/app/views/spree_cm_commissioner/cancel_order_mailer/_footer.html.erb +8 -0
  61. data/app/views/spree_cm_commissioner/cancel_order_mailer/_header.html.erb +3 -0
  62. data/app/views/spree_cm_commissioner/cancel_order_mailer/_mailer_stylesheets.html.erb +139 -0
  63. data/app/views/spree_cm_commissioner/cancel_order_mailer/_your_booking.html.erb +8 -0
  64. data/app/views/spree_cm_commissioner/cancel_order_mailer/purchased_items/_items.html.erb +53 -0
  65. data/app/views/spree_cm_commissioner/cancel_order_mailer/purchased_items/_summary.html.erb +108 -0
  66. data/app/views/spree_cm_commissioner/cancel_order_mailer/tenant/_cancel_email.html.erb +12 -0
  67. data/app/views/spree_cm_commissioner/cancel_order_mailer/tenant/_footer.html.erb +8 -0
  68. data/app/views/spree_cm_commissioner/cancel_order_mailer/tenant/_header.html.erb +15 -0
  69. data/app/views/spree_cm_commissioner/layouts/cancel_order_mailer.html.erb +16 -0
  70. data/config/locales/en.yml +15 -0
  71. data/config/locales/km.yml +27 -2
  72. data/config/routes.rb +2 -0
  73. data/db/migrate/20251127074809_change_guest_dynamic_fields_value_from_jsonb_to_text.rb +40 -0
  74. data/db/migrate/20251219035243_add_migrations_to_support_vehicle_types.rb +36 -0
  75. data/lib/spree_cm_commissioner/distance.rb +88 -0
  76. data/lib/spree_cm_commissioner/engine.rb +3 -0
  77. data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +2 -2
  78. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +3 -3
  79. data/lib/spree_cm_commissioner/test_helper/factories/pricing_model_variant_factory.rb +6 -0
  80. data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +1 -1
  81. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +1 -0
  82. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +4 -1
  83. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +7 -0
  84. data/lib/spree_cm_commissioner/trip_query_result.rb +1 -1
  85. data/lib/spree_cm_commissioner/trip_result.rb +7 -1
  86. data/lib/spree_cm_commissioner/version.rb +1 -1
  87. data/lib/spree_cm_commissioner.rb +1 -1
  88. metadata +39 -10
  89. data/app/interactors/spree_cm_commissioner/trip_distance_calculator.rb +0 -165
  90. data/app/interactors/spree_cm_commissioner/vehicle_updater.rb +0 -41
  91. data/app/models/concerns/spree_cm_commissioner/vehicle_type.rb +0 -11
  92. data/app/models/spree_cm_commissioner/vehicle_option_type.rb +0 -6
  93. data/lib/spree_cm_commissioner/intercity_taxi/distance.rb +0 -38
  94. data/lib/spree_cm_commissioner/test_helper/factories/vendor_route_factory.rb +0 -7
@@ -0,0 +1,108 @@
1
+ <table class="purchase purchase-summary">
2
+ <tr>
3
+ <td valign="middle" colspan="2">
4
+ <p class="f-fallback purchase_total purchase_total--label">
5
+ <%= Spree.t('order_mailer.subtotal') %>
6
+ </p>
7
+ </td>
8
+ <td valign="middle">
9
+ <p class="f-fallback purchase_total align-right">
10
+ <%= order.display_item_total %>
11
+ </p>
12
+ </td>
13
+ </tr>
14
+ <% if order.line_item_adjustments.exists? %>
15
+ <% if order.all_adjustments.promotion.eligible.exists? %>
16
+ <% order.all_adjustments.promotion.eligible.group_by(&:label).each do |label, adjustments| %>
17
+ <tr class="purchase-summary-item">
18
+ <td class="purchase_footer" valign="middle">
19
+ <p class="f-fallback purchase_total purchase_total--label">
20
+ <%= Spree.t(:promotion) %>
21
+ </p>
22
+ </td>
23
+ <td class="purchase_footer" valign="middle">
24
+ <p class="f-fallback purchase_total--name purchase_total--label">
25
+ <%= label %>
26
+ </p>
27
+ </td>
28
+ <td class="purchase_footer" valign="middle">
29
+ <p class="f-fallback purchase_total align-right">
30
+ <%= Spree::Money.new(adjustments.sum(&:amount), currency: order.currency) %>
31
+ </p>
32
+ </td>
33
+ </tr>
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+ <% order.shipments.group_by { |s| s.selected_shipping_rate&.name }.each do |name, shipments| %>
38
+ <tr class="purchase-summary-item">
39
+ <td class="purchase_footer" valign="middle">
40
+ <p class="f-fallback purchase_total purchase_total--label">
41
+ <%= Spree.t(:shipping) %>
42
+ </p>
43
+ </td>
44
+ <td class="purchase_footer" valign="middle">
45
+ <p class="f-fallback purchase_total--name purchase_total--label">
46
+ <%= name %>
47
+ </p>
48
+ </td>
49
+ <td class="purchase_footer" valign="middle">
50
+ <p class="f-fallback purchase_total align-right">
51
+ <%= Spree::Money.new(shipments.sum(&:discounted_cost), currency: order.currency) %>
52
+ </p>
53
+ </td>
54
+ </tr>
55
+ <%end%>
56
+ <% if order.additional_tax_total != 0 %>
57
+ <tr class="purchase-summary-item">
58
+ <td class="purchase_footer" valign="middle" colspan="2">
59
+ <p class="f-fallback purchase_total purchase_total--label">
60
+ <%= Spree.t(:tax) %>
61
+ </p>
62
+ </td>
63
+ <td class="purchase_footer" valign="middle">
64
+ <p class="f-fallback purchase_total align-right">
65
+ <%= order.display_additional_tax_total.to_html %>
66
+ </p>
67
+ </td>
68
+ </tr>
69
+ <% end %>
70
+
71
+ <% order.adjustments.eligible.each do |adjustment| %>
72
+ <% next if (adjustment.source_type == 'Spree::TaxRate') || (adjustment.amount == 0) %>
73
+ <tr>
74
+ <td class="purchase_footer" valign="middle">
75
+ <p class="f-fallback purchase_total purchase_total--label">
76
+ <%= Spree.t(:adjustments) %>
77
+ </p>
78
+ </td>
79
+ <td class="purchase_footer" valign="middle">
80
+ <p class="f-fallback purchase_total--name purchase_total--label">
81
+ <%= raw(adjustment.label) %>:
82
+ </p>
83
+ </td>
84
+ <td class="purchase_total-col align-right">
85
+ <p class="f-fallback purchase_total">
86
+ <%= Spree::Money.new(adjustment.amount, currency: order.currency) %>
87
+ </p>
88
+ </td>
89
+ </tr>
90
+ <% end %>
91
+ <tr>
92
+ <td class="purchase_footer" colspan="2">
93
+ <p class="f-fallback purchase_total purchase_total--label">
94
+ <strong><%= I18n.t('mail.order_mailer.etotal') %></strong> (<%= I18n.t('mail.order_mailer.including_vat') %>)
95
+ </p>
96
+ </td>
97
+ <td class="purchase_total-col align-right">
98
+ <p class="f-fallback purchase_total">
99
+ <strong><%= order.display_total %></strong>
100
+ </p>
101
+ </td>
102
+ </tr>
103
+ <tr>
104
+ <td colspan="4">
105
+ <hr style="border-top-width: 1px; border-top-color: #E5E7EB; height: 0; margin: 1.5rem 0 0; border-style: solid none none;" />
106
+ </td>
107
+ </tr>
108
+ </table>
@@ -0,0 +1,12 @@
1
+ <div class="cancel-greeting">
2
+ <h1>
3
+ <%= I18n.t('mail.order_mailer.hello', full_name: user_full_name(order))%>
4
+ </h1>
5
+ <p class="description">
6
+ <%= I18n.t('mail.order_mailer.cancel_description') %>
7
+ </p>
8
+ <b>
9
+ <%= I18n.t('mail.order_mailer.order_cancelled', order_number: order.number || 'NA') %>
10
+ </b>
11
+ <div style="border-bottom: 1px solid #E5E7EB; margin-top: 1.5rem;">
12
+ </div>
@@ -0,0 +1,8 @@
1
+ <div class="footer">
2
+ <p>
3
+ <%= I18n.t('mail.order_mailer.cancel_thank') %>
4
+ </p>
5
+ <p class="team">
6
+ <%= I18n.t('mail.order_mailer.from_team', store: name) %>
7
+ </p>
8
+ </div>
@@ -0,0 +1,15 @@
1
+ <div class="header-tenant">
2
+ <table width="100%" cellpadding="0" cellspacing="0">
3
+ <tr>
4
+ <td class="tenant-name">
5
+ <%= name %>
6
+ </td>
7
+
8
+ <% if logo.present? %>
9
+ <td class="logo-tenant" align="right">
10
+ <img src="<%= logo %>">
11
+ </td>
12
+ <% end %>
13
+ </tr>
14
+ </table>
15
+ </div>
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
5
+ <meta name="x-apple-disable-message-reformatting"/>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
7
+ <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
9
+ <%= render partial: 'spree_cm_commissioner/cancel_order_mailer/mailer_stylesheets' %>
10
+ </head>
11
+ <body>
12
+ <table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%">
13
+ <%= yield %>
14
+ </table>
15
+ </body>
16
+ </html>
@@ -119,6 +119,17 @@ en:
119
119
  product_kind_option_types:
120
120
  empty_info: "<b>Note:</b> Set service option types to service first. To edit, go to <b>Details</b> tab > <b>Option Types</b>"
121
121
 
122
+ vehicle_type:
123
+ suv: "SUV"
124
+ sedan: "Sedan"
125
+ van: "Van"
126
+ minivan: "Minivan"
127
+ sleeping_bus: "Sleeping Bus"
128
+ luxury_van: "Luxury Van"
129
+ air_bus: "Air Bus"
130
+ bus: "Bus"
131
+ ferry: "Ferry"
132
+
122
133
  line_item:
123
134
  validation:
124
135
  exceeded_max_quantity_per_order: "Exceeded maximum quantity per order"
@@ -528,6 +539,10 @@ en:
528
539
  hello: "Hello %{full_name},"
529
540
  thanks: "Thanks %{full_name},"
530
541
  booking_event: "Your booking for %{order_number} is successfully complete!"
542
+ cancel_description: "Your order has been CANCELED. Please retain this cancellation information for your records."
543
+ order_cancelled: "Order %{order_number} Summary [CANCELED]"
544
+ cancel_thank: "Thank you for your business."
545
+ from_team: "The %{store} Team"
531
546
  booking_reference: "For reference, your booking ID is %{order_number}."
532
547
  booking_id: "Booking ID <b>%{order_number}</b>"
533
548
  booking_summary: "Booking Summary"
@@ -113,6 +113,17 @@ km:
113
113
  product_kind_option_types:
114
114
  empty_info: "<b>ចំណាំ:</b> កំណត់ប្រភេទសម្រាប់ផលិតផល. ដើម្បីកែប្រែ, សូមចូលទៅ <b>លម្អិត</b> tab > <b>ប្រភេទ</b>"
115
115
 
116
+ vehicle_type:
117
+ suv: "SUV"
118
+ sedan: "Sedan"
119
+ van: "Van"
120
+ minivan: "Minivan"
121
+ sleeping_bus: "Sleeping Bus"
122
+ luxury_van: "Luxury Van"
123
+ air_bus: "Air Bus"
124
+ bus: "Bus"
125
+ ferry: "Ferry"
126
+
116
127
  line_item:
117
128
  validation:
118
129
  exceeded_max_quantity_per_order: "លើសពីបរិមាណអតិបរមាក្នុងមួយការបញ្ជាទិញ"
@@ -190,6 +201,16 @@ km:
190
201
  system_registered: "ចុះឈ្មោះដោយប្រព័ន្ធ"
191
202
  self_registered: "ចុះឈ្មោះដោយខ្លួនឯង"
192
203
  registration_source: "ប្រភេទការចុះឈ្មោះ"
204
+ vehicle_kind:
205
+ suv: "SUV"
206
+ sedan: "Sedan"
207
+ van: "Van"
208
+ minivan: "Minivan"
209
+ sleeping_bus: "Sleeping Bus"
210
+ luxury_van: "Luxury Van"
211
+ air_bus: "Air Bus"
212
+ bus: "Bus"
213
+ ferry: "Ferry"
193
214
  service_types:
194
215
  organizer: "Organizer"
195
216
  transit: "Transit"
@@ -388,9 +409,13 @@ km:
388
409
  booking_confirm: "Confirmed!"
389
410
  hello: "Hello %{full_name},"
390
411
  thanks: "Thanks %{full_name},"
391
- booking_event: "Your booking for %{order_name} is successfully complete!"
412
+ booking_event: "Your booking for %{order_number} is successfully complete!"
413
+ cancel_description: "Your order has been CANCELED. Please retain this cancellation information for your records."
414
+ order_cancelled: "Order %{order_number} Summary [CANCELED]"
415
+ cancel_thank: "Thank you for your business."
416
+ from_team: "The %{store} Team"
392
417
  booking_reference: "For reference, your booking ID is %{order_number}."
393
- booking_id: "Booking ID <b>%{order_number}<b>"
418
+ booking_id: "Booking ID <b>%{order_number}</b>"
394
419
  booking_summary: "Booking Summary"
395
420
  check_in: "Check in:"
396
421
  check_out: "Check out:"
data/config/routes.rb CHANGED
@@ -563,6 +563,7 @@ Spree::Core::Engine.add_routes do
563
563
 
564
564
  namespace :intercity_taxi do
565
565
  resource :draft_orders, only: %i[create update]
566
+ resource :distance_calculator, controller: :distance_calculator, only: [:create]
566
567
  end
567
568
 
568
569
  resource :reset_passwords, only: [:update]
@@ -708,6 +709,7 @@ Spree::Core::Engine.add_routes do
708
709
  end
709
710
  namespace :intercity_taxi do
710
711
  resource :draft_orders, only: %i[create update]
712
+ resource :distance_calculator, controller: :distance_calculator, only: [:create]
711
713
  end
712
714
  end
713
715
 
@@ -0,0 +1,40 @@
1
+ class ChangeGuestDynamicFieldsValueFromJsonbToText < ActiveRecord::Migration[7.0]
2
+ def up
3
+ # Check if the migration has already been run
4
+ return if column_exists?(:cm_guest_dynamic_fields, :value, :text)
5
+
6
+ add_column :cm_guest_dynamic_fields, :value_text, :text
7
+
8
+ # Convert JSONB value to TEXT value
9
+ execute <<-SQL
10
+ UPDATE cm_guest_dynamic_fields
11
+ SET value_text = value ->> 0
12
+ SQL
13
+
14
+ remove_column :cm_guest_dynamic_fields, :value
15
+ rename_column :cm_guest_dynamic_fields, :value_text, :value
16
+
17
+ # Restore NOT NULL and default value constraints
18
+ #To make it the same as the original: t.jsonb :value, default: [], null: false
19
+ change_column_default :cm_guest_dynamic_fields, :value, ''
20
+ change_column_null :cm_guest_dynamic_fields, :value, false
21
+ end
22
+
23
+ def down
24
+ add_column :cm_guest_dynamic_fields, :value_jsonb, :jsonb
25
+
26
+ # Convert TEXT value to JSONB value
27
+ execute <<-SQL
28
+ UPDATE cm_guest_dynamic_fields
29
+ SET value_jsonb = to_jsonb(value)
30
+ SQL
31
+
32
+ remove_column :cm_guest_dynamic_fields, :value
33
+ rename_column :cm_guest_dynamic_fields, :value_jsonb, :value
34
+
35
+ # Restore NOT NULL and default value constraints
36
+ #To make it the same as the original: t.jsonb :value, default: [], null: false
37
+ change_column_default :cm_guest_dynamic_fields, :value, []
38
+ change_column_null :cm_guest_dynamic_fields, :value, false
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ class AddMigrationsToSupportVehicleTypes < ActiveRecord::Migration[7.0]
2
+ def change
3
+ # 1. rename amentity table from vehicle to vehicle type
4
+ rename_table :cm_vehicle_option_types, :cm_vehicle_type_option_types if table_exists?(:cm_vehicle_option_types)
5
+ rename_table :cm_option_value_vehicles, :cm_option_value_vehicle_types if table_exists?(:cm_option_value_vehicles)
6
+ rename_table :cm_vehicles, :cm_vehicle_types if table_exists?(:cm_vehicles) && !table_exists?(:cm_vehicle_types)
7
+
8
+ rename_column :cm_vehicle_type_option_types, :vehicle_id, :vehicle_type_id if column_exists?(:cm_vehicle_type_option_types, :vehicle_id)
9
+ rename_column :cm_option_value_vehicle_types, :vehicle_id, :vehicle_type_id if column_exists?(:cm_option_value_vehicle_types, :vehicle_id)
10
+
11
+ # 2. remove uncessary column for vehicle type.
12
+ # also rename code to name to make it more friendly. Meyhong also use name.
13
+ # rename vehicle_type to kind to avoid confusion with vehicle_type association
14
+ change_table :cm_vehicle_types do |t|
15
+ t.remove :license_plate if column_exists?(:cm_vehicle_types, :license_plate)
16
+ t.remove :allow_seat_selection if column_exists?(:cm_vehicle_types, :allow_seat_selection)
17
+ t.rename :code, :name if column_exists?(:cm_vehicle_types, :code)
18
+ t.rename :vehicle_type, :kind if column_exists?(:cm_vehicle_types, :vehicle_type)
19
+ end
20
+
21
+ # 3. new vehicle table
22
+ create_table :cm_vehicles, if_not_exists: true do |t|
23
+ t.string :license_plate
24
+ t.string :name
25
+ t.integer :kind
26
+ t.references :vendor, null: false, foreign_key: { to_table: :spree_vendors }
27
+ t.references :vehicle_type, null: false, foreign_key: { to_table: :cm_vehicle_types }
28
+ t.timestamps
29
+ end
30
+
31
+ # 4. drop existing vehicle assocation, and add vehicle/vehicle type assocation type to trip again.
32
+ remove_column :cm_trips, :vehicle_id if column_exists?(:cm_trips, :vehicle_id)
33
+ add_reference :cm_trips, :vehicle, null: true, foreign_key: { to_table: :cm_vehicles } unless column_exists?(:cm_trips, :vehicle_id)
34
+ add_reference :cm_trips, :vehicle_type, null: true, foreign_key: { to_table: :cm_vehicle_types } unless column_exists?(:cm_trips, :vehicle_type_id)
35
+ end
36
+ end
@@ -0,0 +1,88 @@
1
+ module SpreeCmCommissioner
2
+ # Generic value object representing a computed trip distance and detours.
3
+ # This is the canonical class used by distance calculation services
4
+ # and line item metadata (see LineItemTransitable concern).
5
+ class Distance
6
+ attr_accessor :id,
7
+ :distance_km,
8
+ :ordered_points,
9
+ :estimated_time_minutes,
10
+ :base_km,
11
+ :detour_pickup_km,
12
+ :detour_dropoff_km,
13
+ :extra_pickup_km,
14
+ :extra_dropoff_km
15
+
16
+ def initialize(options = {})
17
+ options = options.stringify_keys if options.is_a?(Hash)
18
+
19
+ @id = SecureRandom.hex
20
+ @distance_km = options['distance_km']&.to_f
21
+ @ordered_points = options['ordered_points']
22
+ @estimated_time_minutes = options['estimated_time_minutes']&.to_i
23
+ @base_km = options['base_km']&.to_f
24
+ @detour_pickup_km = options['detour_pickup_km']&.to_f
25
+ @detour_dropoff_km = options['detour_dropoff_km']&.to_f
26
+ @extra_pickup_km = options['extra_pickup_km']&.to_f
27
+ @extra_dropoff_km = options['extra_dropoff_km']&.to_f
28
+ end
29
+
30
+ def self.from_hash(hash)
31
+ new(hash || {})
32
+ end
33
+
34
+ # Build a Distance from a signed hash (used for request-time verification).
35
+ def self.from_signed_hash(hash)
36
+ return nil unless valid_signature?(hash)
37
+
38
+ new(hash || {})
39
+ end
40
+
41
+ def self.valid_signature?(signed_hash)
42
+ return false if signed_hash.nil?
43
+
44
+ verify_result = SpreeCmCommissioner::Signing::VerifySignature.call(
45
+ signed_hash: signed_hash,
46
+ signing_key: ENV.fetch('DISTANCE_SIGNING_KEY')
47
+ )
48
+
49
+ verify_result.success? && verify_result.value == true
50
+ end
51
+
52
+ def to_h
53
+ {
54
+ 'distance_km' => @distance_km,
55
+ 'ordered_points' => @ordered_points,
56
+ 'estimated_time_minutes' => @estimated_time_minutes,
57
+ 'base_km' => @base_km,
58
+ 'detour_pickup_km' => @detour_pickup_km,
59
+ 'detour_dropoff_km' => @detour_dropoff_km,
60
+ 'extra_pickup_km' => @extra_pickup_km,
61
+ 'extra_dropoff_km' => @extra_dropoff_km
62
+ }.compact
63
+ end
64
+
65
+ def to_signed_h
66
+ hash = {
67
+ 'distance_km' => @distance_km,
68
+ 'ordered_points' => @ordered_points,
69
+ 'estimated_time_minutes' => @estimated_time_minutes,
70
+ 'base_km' => @base_km,
71
+ 'detour_pickup_km' => @detour_pickup_km,
72
+ 'detour_dropoff_km' => @detour_dropoff_km,
73
+ 'extra_pickup_km' => @extra_pickup_km,
74
+ 'extra_dropoff_km' => @extra_dropoff_km,
75
+ 'signed_at' => Time.current.utc.iso8601
76
+ }.compact
77
+
78
+ sign_result = SpreeCmCommissioner::Signing::SignData.call(
79
+ hash: hash,
80
+ signing_key: ENV.fetch('DISTANCE_SIGNING_KEY')
81
+ )
82
+
83
+ return hash unless sign_result.success?
84
+
85
+ hash.merge('signature' => sign_result.value)
86
+ end
87
+ end
88
+ end
@@ -38,6 +38,9 @@ module SpreeCmCommissioner
38
38
  SpreeCmCommissioner::Promotion::Actions::CreateDateSpecificItemAdjustments,
39
39
  SpreeCmCommissioner::Promotion::Actions::CreateGuestItemAdjustments,
40
40
  ]
41
+
42
+ # Register PricingAction adjuster to include pricing_action adjustments in totals
43
+ Rails.application.config.spree.adjusters << SpreeCmCommissioner::Adjustable::Adjuster::PricingAction
41
44
  end
42
45
 
43
46
  config.to_prepare(&method(:activate).to_proc)
@@ -1,7 +1,7 @@
1
1
  FactoryBot.define do
2
2
  # use cm_guest instead for consistency
3
3
  factory :guest, class: SpreeCmCommissioner::Guest do
4
- line_item { |a| a.association(:line_item) }
4
+ line_item { |a| a.association(:cm_line_item) }
5
5
  first_name { FFaker::Name.name }
6
6
  last_name { FFaker::Name.name }
7
7
  gender { 1 }
@@ -10,7 +10,7 @@ FactoryBot.define do
10
10
  end
11
11
 
12
12
  factory :cm_guest, class: SpreeCmCommissioner::Guest do
13
- line_item { |a| a.association(:line_item) }
13
+ line_item { |a| a.association(:cm_line_item) }
14
14
  first_name { FFaker::Name.name }
15
15
  last_name { FFaker::Name.name }
16
16
  gender { 1 }
@@ -7,13 +7,13 @@ FactoryBot.define do
7
7
 
8
8
  product do
9
9
  if order&.store&.present?
10
- create(:cm_product_with_product_kind_option_types, stores: [order.store])
10
+ create(:cm_product, stores: [order.store])
11
11
  else
12
- create(:cm_product_with_product_kind_option_types)
12
+ create(:cm_product)
13
13
  end
14
14
  end
15
15
 
16
- variant { product.master }
16
+ variant { create(:cm_variant, product: product, track_inventory: false) }
17
17
 
18
18
  factory :cm_need_confirmation_line_item do
19
19
  product { create(:cm_product, need_confirmation: true) }
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ factory :cm_pricing_model_variant, class: 'SpreeCmCommissioner::PricingModelVariant' do
3
+ association :pricing_model, factory: :cm_pricing_model
4
+ association :variant, factory: :cm_variant
5
+ end
6
+ end
@@ -1,6 +1,6 @@
1
1
  FactoryBot.define do
2
2
  factory :cm_seat_layout, class: SpreeCmCommissioner::SeatLayout do
3
- association :layoutable, factory: :cm_vehicle
3
+ association :layoutable, factory: :cm_vehicle_type
4
4
 
5
5
  name { "Layout #{rand(1.0..50.0)}" }
6
6
  status { :active }
@@ -7,6 +7,7 @@ FactoryBot.define do
7
7
  product { create(:cm_product, product_type: :transit) }
8
8
 
9
9
  association :vendor, factory: :cm_transit_vendor
10
+ association :vehicle_type, factory: :cm_vehicle_type
10
11
  association :vehicle, factory: :cm_vehicle
11
12
  association :origin_place, factory: :cm_place
12
13
  association :destination_place, factory: :cm_place
@@ -1,6 +1,9 @@
1
1
  FactoryBot.define do
2
2
  factory :cm_vehicle, class: SpreeCmCommissioner::Vehicle do
3
- sequence(:code) { |n| "vehicle_#{n}" }
3
+ sequence(:name) { |n| "vehicle_#{n}" }
4
4
  sequence(:license_plate) { |n| "2KH-#{'%04d' % (n % 10000)}" }
5
+ kind { :air_bus }
6
+ association :vendor, factory: :vendor
7
+ association :vehicle_type, factory: :cm_vehicle_type
5
8
  end
6
9
  end
@@ -0,0 +1,7 @@
1
+ FactoryBot.define do
2
+ factory :cm_vehicle_type, class: SpreeCmCommissioner::VehicleType do
3
+ sequence(:name) { |n| "vehicle_type_#{n}" }
4
+ kind { :air_bus }
5
+ association :vendor, factory: :vendor
6
+ end
7
+ end
@@ -58,7 +58,7 @@ module SpreeCmCommissioner
58
58
  end
59
59
 
60
60
  def vehicle_types
61
- @trips.map(&:vehicle).map(&:vehicle_type).uniq
61
+ @trips.map(&:vehicle_type).uniq
62
62
  end
63
63
  end
64
64
  end
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
2
2
  class TripResult
3
3
  attr_accessor :id, :origin_place, :destination_place, :allow_seat_selection,
4
4
  :departure_time, :duration, :route_type,
5
- :vehicle_id, :vehicle, :vendor_id, :vendor,
5
+ :vehicle_type_id, :vehicle_type, :vendor_id, :vendor,
6
6
  :quantity_available, :max_capacity, :boarding, :drop_off,
7
7
  :product_id, :amenities, :price, :compare_at_amount, :currency, :distance
8
8
 
@@ -37,6 +37,12 @@ module SpreeCmCommissioner
37
37
  departure_time + duration.seconds
38
38
  end
39
39
 
40
+ def display_price
41
+ return nil if price.nil? || currency.nil?
42
+
43
+ Spree::Money.new(price, currency: currency).to_s
44
+ end
45
+
40
46
  # Enable serializer-level caching (like ActiveRecord's cache_key_with_version)
41
47
  # Create a version hash based on attributes that matter for caching
42
48
  def cache_key_with_version
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '2.5.0.pre.pre13'.freeze
2
+ VERSION = '2.5.0.pre.pre14'.freeze
3
3
 
4
4
  module_function
5
5
 
@@ -24,7 +24,7 @@ require 'spree_cm_commissioner/cached_inventory_item'
24
24
  require 'spree_cm_commissioner/transit/leg'
25
25
  require 'spree_cm_commissioner/transit/seat_selection'
26
26
  require 'spree_cm_commissioner/intercity_taxi/map_place'
27
- require 'spree_cm_commissioner/intercity_taxi/distance'
27
+ require 'spree_cm_commissioner/distance'
28
28
 
29
29
  require 'activerecord_multi_tenant'
30
30
  require 'google/cloud/recaptcha_enterprise'