spree_cm_commissioner 1.10.0 → 1.11.0.pre.pre

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +131 -98
  3. data/.gitignore +2 -1
  4. data/.vscode/settings.json +1 -1
  5. data/Gemfile.lock +22 -1
  6. data/Rakefile +33 -4
  7. data/app/controllers/spree/admin/stock_managements_controller.rb +56 -1
  8. data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +42 -0
  9. data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +14 -31
  10. data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
  11. data/app/finders/spree_cm_commissioner/accommodations/find.rb +40 -0
  12. data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +35 -0
  13. data/app/interactors/spree_cm_commissioner/create_event.rb +23 -0
  14. data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +40 -0
  15. data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +25 -0
  16. data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +13 -0
  17. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +75 -0
  18. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +24 -0
  19. data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +27 -8
  20. data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +7 -0
  21. data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +7 -0
  22. data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +11 -0
  23. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +9 -0
  24. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +26 -0
  25. data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +1 -3
  26. data/app/models/concerns/spree_cm_commissioner/product_type.rb +10 -0
  27. data/app/models/spree_cm_commissioner/inventory.rb +11 -0
  28. data/app/models/spree_cm_commissioner/inventory_item.rb +55 -0
  29. data/app/models/spree_cm_commissioner/line_item_decorator.rb +16 -5
  30. data/app/models/spree_cm_commissioner/order_decorator.rb +15 -0
  31. data/app/models/spree_cm_commissioner/place.rb +11 -2
  32. data/app/models/spree_cm_commissioner/product_decorator.rb +9 -2
  33. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +40 -0
  34. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +126 -0
  35. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +36 -0
  36. data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +27 -0
  37. data/app/models/spree_cm_commissioner/stock/availability_checker.rb +27 -25
  38. data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +2 -1
  39. data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
  40. data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +44 -0
  41. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +18 -0
  42. data/app/models/spree_cm_commissioner/taxon_decorator.rb +11 -0
  43. data/app/models/spree_cm_commissioner/taxon_option_type.rb +8 -0
  44. data/app/models/spree_cm_commissioner/taxon_option_value.rb +8 -0
  45. data/app/models/spree_cm_commissioner/trip.rb +0 -11
  46. data/app/models/spree_cm_commissioner/trip_stop.rb +11 -4
  47. data/app/models/spree_cm_commissioner/variant_decorator.rb +39 -27
  48. data/app/models/spree_cm_commissioner/vendor_stop.rb +2 -1
  49. data/app/queries/spree_cm_commissioner/vendor_stop_place_query.rb +54 -0
  50. data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +3 -0
  51. data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
  52. data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +19 -0
  53. data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +2 -0
  54. data/app/serializers/spree/v2/tenant/guest_serializer.rb +1 -0
  55. data/app/services/spree_cm_commissioner/aes_encryption_service.rb +6 -4
  56. data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +2 -0
  57. data/app/views/spree/admin/stock_managements/_events_popover.html.erb +23 -0
  58. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +3 -1
  59. data/app/views/spree/admin/stock_managements/calendar.html.erb +35 -0
  60. data/app/views/spree/admin/stock_managements/index.html.erb +40 -5
  61. data/config/initializers/spree_permitted_attributes.rb +5 -0
  62. data/config/routes.rb +11 -2
  63. data/db/migrate/20250304293518_create_cm_inventory_items.rb +21 -0
  64. data/db/migrate/20250418072528_add_nested_set_columns_to_places.rb +10 -0
  65. data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +5 -0
  66. data/db/migrate/20250430091742_create_cm_taxon_option_types.rb +9 -0
  67. data/db/migrate/20250430092928_create_cm_taxon_option_values.rb +9 -0
  68. data/db/migrate/20250502025848_add_index_to_spree_products.rb +5 -0
  69. data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +5 -0
  70. data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +5 -0
  71. data/db/migrate/20250506092929_add_trip_count_to_cm_vendor_stops.rb +5 -0
  72. data/docker-compose.yml +1 -1
  73. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +11 -3
  74. data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_cm_commissioner → spree_dashboard/spree_cm_commissioner}/utilities.js +4 -0
  75. data/lib/spree_cm_commissioner/cached_inventory_item.rb +23 -0
  76. data/lib/spree_cm_commissioner/calendar_event.rb +11 -1
  77. data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
  78. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +9 -0
  79. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
  80. data/lib/spree_cm_commissioner/test_helper/factories/place_factory.rb +11 -1
  81. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +18 -5
  82. data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
  83. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +39 -6
  84. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +1 -1
  85. data/lib/spree_cm_commissioner/version.rb +1 -1
  86. data/lib/spree_cm_commissioner.rb +34 -0
  87. data/lib/tasks/create_default_non_permanent_inventory_items.rake +16 -0
  88. data/lib/tasks/ensure_correct_product_type.rake +7 -0
  89. data/lib/tasks/generate_inventory_items.rake +7 -0
  90. data/lib/tasks/migrate_and_rebuild_place_hierarchy.rake +9 -0
  91. data/lib/tasks/update_orphan_root_places.rake +7 -0
  92. data/spree_cm_commissioner.gemspec +5 -0
  93. metadata +88 -7
  94. data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +0 -45
  95. data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +0 -55
@@ -0,0 +1,54 @@
1
+ module SpreeCmCommissioner
2
+ class VendorStopPlaceQuery
3
+ attr_reader :query, :vendor_id, :stop_type, :reference_stop_id
4
+
5
+ def initialize(query:, vendor_id:, stop_type: :boarding, reference_stop_id: nil)
6
+ @query = query.to_s.strip
7
+ @vendor_id = vendor_id
8
+ @stop_type = stop_type
9
+ @reference_stop_id = reference_stop_id
10
+ end
11
+
12
+ def call
13
+ results = vendor_stops
14
+ return [] if results.empty?
15
+
16
+ places = results.includes(:stop).map(&:stop).compact.uniq
17
+
18
+ format_places(places)
19
+ end
20
+
21
+ private
22
+
23
+ def vendor_stops
24
+ scope = base_scope
25
+ return scope if query.blank?
26
+
27
+ scope = scope.where.not(stop_id: reference_stop_id) if reference_stop_id.present?
28
+ scope.where('cm_places.name ILIKE ?', "%#{query}%")
29
+ end
30
+
31
+ def base_scope
32
+ SpreeCmCommissioner::VendorStop
33
+ .joins(:stop)
34
+ .where(vendor_id: vendor_id)
35
+ .where(stop_type: stop_type)
36
+ .order(trip_count: :desc)
37
+ .where.not(trip_count: 0)
38
+ end
39
+
40
+ def format_places(places)
41
+ places
42
+ .sort_by { |p| [-p.depth, -p.lft] }
43
+ .filter_map do |place|
44
+ next if place.depth.zero?
45
+
46
+ {
47
+ id: place.id,
48
+ parent_id: place.parent_id,
49
+ name: place.self_and_ancestors.map(&:name).reverse.join(', ')
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -3,6 +3,9 @@ module SpreeCmCommissioner
3
3
  params do
4
4
  required(:from_date).value(:date)
5
5
  required(:to_date).value(:date)
6
+ required(:state_id).value(:integer)
7
+ required(:number_of_adults).value(:integer)
8
+ required(:number_of_kids).value(:integer)
6
9
  end
7
10
 
8
11
  rule(:from_date, :to_date) do
@@ -11,7 +11,7 @@ module SpreeCmCommissioner
11
11
  end
12
12
 
13
13
  def error_message
14
- errors.map(&:text).join(', ')
14
+ errors.map { |error| "#{error.path.join(', ')}: #{error.text}" }.to_sentence
15
15
  end
16
16
 
17
17
  private
@@ -0,0 +1,19 @@
1
+ module SpreeCmCommissioner
2
+ class VariantRequestSchema < ApplicationRequestSchema
3
+ params do
4
+ required(:from_date).value(:date)
5
+ required(:to_date).value(:date)
6
+ required(:number_of_adults).value(:integer)
7
+ required(:number_of_kids).value(:integer)
8
+ end
9
+
10
+ rule(:from_date, :to_date) do
11
+ from_date = values[:from_date]
12
+ to_date = values[:to_date]
13
+
14
+ key.failure(:must_be_in_future) if from_date < Time.zone.today
15
+ key.failure(:must_be_later_than_start_date) if from_date > to_date
16
+ key.failure(:stay_is_too_long) if (to_date - from_date).to_i > ENV.fetch('ACCOMMODATION_MAX_STAY_DAYS', 10).to_i
17
+ end
18
+ end
19
+ end
@@ -9,10 +9,12 @@ module Spree
9
9
 
10
10
  attributes :total_inventory, :service_availabilities
11
11
 
12
+ # Deprecated
12
13
  attribute :total_booking do |vendor|
13
14
  vendor.respond_to?(:total_booking) ? vendor.total_booking : 0
14
15
  end
15
16
 
17
+ # Deprecated
16
18
  attribute :remaining do |vendor|
17
19
  vendor.respond_to?(:remaining) ? vendor.remaining : vendor.total_inventory
18
20
  end
@@ -13,6 +13,7 @@ module Spree
13
13
 
14
14
  belongs_to :occupation, serializer: Spree::V2::Tenant::TaxonSerializer
15
15
  belongs_to :nationality, serializer: Spree::V2::Tenant::TaxonSerializer
16
+ has_one :id_card, serializer: Spree::V2::Tenant::IdCardSerializer
16
17
 
17
18
  # allowed_checkout updates frequently
18
19
  cache_options store: nil
@@ -8,12 +8,13 @@ module SpreeCmCommissioner
8
8
  IV_LENGTH = 12
9
9
  TAG_LENGTH = 16
10
10
 
11
- def self.encrypt(plaintext, key)
11
+ def self.encrypt(plaintext, base64_key)
12
+ key = Base64.decode64(base64_key)
12
13
  validate_key!(key)
13
14
 
14
15
  cipher = OpenSSL::Cipher.new(ALGORITHM)
15
16
  cipher.encrypt
16
- cipher.key = key.b[0, KEY_LENGTH]
17
+ cipher.key = key[0, KEY_LENGTH]
17
18
  iv = cipher.random_iv
18
19
  cipher.iv = iv
19
20
 
@@ -24,7 +25,8 @@ module SpreeCmCommissioner
24
25
  Base64.strict_encode64(combined)
25
26
  end
26
27
 
27
- def self.decrypt(encrypted_text, key)
28
+ def self.decrypt(encrypted_text, base64_key)
29
+ key = Base64.decode64(base64_key)
28
30
  validate_key!(key)
29
31
 
30
32
  combined = Base64.decode64(encrypted_text)
@@ -34,7 +36,7 @@ module SpreeCmCommissioner
34
36
 
35
37
  cipher = OpenSSL::Cipher.new(ALGORITHM)
36
38
  cipher.decrypt
37
- cipher.key = key.b[0, KEY_LENGTH]
39
+ cipher.key = key[0, KEY_LENGTH]
38
40
  cipher.iv = iv
39
41
  cipher.auth_tag = tag
40
42
 
@@ -48,6 +48,8 @@ module SpreeCmCommissioner
48
48
  end
49
49
 
50
50
  def fetch_option_value(guest, option_type_name)
51
+ return guest.formatted_bib_number if option_type_name == 'bib-prefix'
52
+
51
53
  guest.line_item&.variant&.find_option_value_name_for(option_type_name: option_type_name)
52
54
  end
53
55
 
@@ -0,0 +1,23 @@
1
+ <div>
2
+ <% events.each do |event| %>
3
+ <% inventory_item = event.options[:inventory_item] %>
4
+
5
+ <div class="mb-3">
6
+ <h6><%= inventory_item.variant.options_text %></h6>
7
+ <ul class="list-group mt-1 mb-1">
8
+ <li class="list-group-item d-flex justify-content-between align-items-center">
9
+ Max capacity
10
+ <span class="badge badge-primary badge-pill"><%= inventory_item.max_capacity %></span>
11
+ </li>
12
+ <li class="list-group-item d-flex justify-content-between align-items-center">
13
+ Quantity available in DB
14
+ <span class="badge badge-primary badge-pill"><%= inventory_item.quantity_available %></span>
15
+ </li>
16
+ </ul>
17
+
18
+ <small class="text-muted">
19
+ <%= inventory_item_message(inventory_item, @cached_inventory_items[inventory_item.id]) %>
20
+ </small>
21
+ </div>
22
+ <% end %>
23
+ </div>
@@ -17,9 +17,11 @@
17
17
  </td>
18
18
  <td class="text-center d-flex flex-column align-items-center justify-content-center">
19
19
  <div style="width: 90px;">
20
- <%= form_tag admin_stock_items_path(variant_id: variant.id, stock_location_id: stock_location.id), method: :post do %>
20
+ <%= form_tag admin_product_stock_managements_path(product_id: params[:product_id], variant_id: variant.id, stock_location_id: stock_location.id), method: :post do %>
21
21
  <div class="input-group input-group-sm">
22
22
  <%= number_field_tag 'stock_movement[quantity]', 0, class: 'form-control text-center p-0' %>
23
+ <%= hidden_field_tag 'stock_movement[originator_id]', spree_current_user.id %>
24
+ <%= hidden_field_tag 'stock_movement[originator_type]', spree_current_user.class.name %>
23
25
  <div class="input-group-append">
24
26
  <%= button_tag(class: 'btn btn-outline-success pl-2 pr-1') do %>
25
27
  <%= svg_icon(name: 'arrow-left-right.svg', classes: "icon", width: 14, height: 14) %>
@@ -0,0 +1,35 @@
1
+ <%= turbo_frame_tag "calendar" do %>
2
+ <%= form_with url: admin_product_stock_managements_path, method: :get, class: "mt-4" do %>
3
+ <%= select_year(params[:year]&.to_i || @year, { start_year: 2025, end_year: @year + 3 }, { name: "year", onchange: "this.form.submit()" }) %>
4
+ <% end %>
5
+
6
+ <div class="c-annual-calendar-container mt-2">
7
+ <div class="c-annual-calendar">
8
+ <% (1..12).each do |month| %>
9
+ <%= month_calendar(start_date: Date.new(@year, month, 1), attribute: :from_date, end_attribute: :to_date, events: @events) do |date, events| %>
10
+ <div class="c-annual-day"
11
+ data-date="<%= date.strftime '%Y-%m-%d' %>"
12
+ type="button"
13
+ tabindex="0"
14
+ data-toggle="popover"
15
+ data-trigger="hover"
16
+ data-placement="right"
17
+ data-html="true"
18
+ data-content="<%= raw render_escape_html partial: "events_popover", locals: { events: events } if events.any? %>">
19
+ <%= date.day %>
20
+ <ul class="p-0 m-0 list-unstyled">
21
+ <% events.each do |event| %>
22
+ <% inventory_item = event.options[:inventory_item] %>
23
+ <% synced = inventory_item.quantity_available == @cached_inventory_items[inventory_item.id].quantity_available %>
24
+
25
+ <li class="badge <%= synced ? "badge-warning" : "badge-danger" %>">
26
+ <%= inventory_item.quantity_available %>
27
+ </li>
28
+ <% end %>
29
+ </ul>
30
+ </div>
31
+ <% end %>
32
+ <% end %>
33
+ </div>
34
+ </div>
35
+ <% end %>
@@ -19,10 +19,7 @@
19
19
  <% @variants.each do |variant| %>
20
20
  <tr id="<%= spree_dom_id variant %>" data-hook="admin_product_stock_management_index_rows">
21
21
  <td class="image text-center">
22
- <%= small_image(variant) %>
23
- <div class="mt-4">
24
- Reserved Stock: <%= @reserved_stocks[variant.id] || 0 %></strong>
25
- </div>
22
+ <%= small_image(variant) %>
26
23
  </td>
27
24
  <td>
28
25
  <%= variant.sku_and_options_text %>
@@ -35,14 +32,46 @@
35
32
  <% end %>
36
33
  </div>
37
34
  <% end if can?(:update, @product) && can?(:update, variant) %>
35
+
36
+ <% if defined?(@reserved_stocks) %>
37
+ <div>
38
+ <span type="button" data-toggle="popover" data-trigger="hover" data-placement="right" data-content="This product stock will renew every day">
39
+ <%= svg_icon name: "cart-check.svg", width: '14', height: '14' %>
40
+ </span>
41
+ <%= label_tag "reserved_stock#{variant.id}", "Reserved Stock: #{@reserved_stocks[variant.id] || 0}", class: "m-0" %>
42
+ </div>
43
+ <% end %>
44
+
38
45
  <% if variant.permanent_stock? %>
39
46
  <div>
40
47
  <span type="button" data-toggle="popover" data-trigger="hover" data-placement="right" data-content="This product stock will renew every day">
41
48
  <%= svg_icon name: "info-circle-fill.svg", width: '14', height: '14' %>
42
49
  </span>
43
- <%= label_tag "permanent_stock_#{ variant.id }", Spree.t(:permanent_stock) %>
50
+ <%= label_tag "permanent_stock_#{ variant.id }", Spree.t(:permanent_stock), class: "m-0" %>
44
51
  </div>
45
52
  <% end %>
53
+
54
+ <% if defined?(@inventory_items) %>
55
+ <% @inventory_items[variant.id]&.each do |inventory_item| %>
56
+ <div>
57
+ <%= svg_icon name: "handbag.svg", width: '14', height: '14' %>
58
+ <%= label_tag "max_capacity_#{ inventory_item.id }", "Max capacity: #{inventory_item.max_capacity}", class: "m-0" %>
59
+ </div>
60
+ <div>
61
+ <%= svg_icon name: "approve.svg", width: '14', height: '14' %>
62
+ <%= label_tag "quantity_available_#{ inventory_item.id }", "Quantity available: #{inventory_item.quantity_available}", class: "m-0" %>
63
+
64
+ <% synced = inventory_item.quantity_available == @cached_inventory_items[inventory_item.id].quantity_available %>
65
+ <span type="button" data-toggle="popover" data-trigger="hover" data-placement="right" data-content="<%= inventory_item_message(inventory_item, @cached_inventory_items[inventory_item.id]) %>">
66
+ <% if synced %>
67
+ <%= svg_icon name: "cloud-check.svg", width: '14', height: '14', classes: 'text-success' %>
68
+ <% else %>
69
+ <%= svg_icon name: "cloud-slash.svg", width: '14', height: '14', classes: 'text-danger' %>
70
+ <% end %>
71
+ </span>
72
+ </div>
73
+ <% end %>
74
+ <% end %>
46
75
  </td>
47
76
 
48
77
  <td colspan="3" class="stock_location_info">
@@ -53,3 +82,9 @@
53
82
  </tbody>
54
83
  </table>
55
84
  </div>
85
+
86
+ <%= turbo_frame_tag "calendar", src: calendar_admin_product_stock_managements_path(year: params[:year]) do %>
87
+ <div class="spinner-border mt-2" role="status">
88
+ <span class="sr-only">Loading...</span>
89
+ </div>
90
+ <% end if @product.permanent_stock? %>
@@ -55,5 +55,10 @@ module Spree
55
55
  age
56
56
  gender
57
57
  ]
58
+
59
+ @@stock_movement_attributes += %i[
60
+ originator_type
61
+ originator_id
62
+ ]
58
63
  end
59
64
  end
data/config/routes.rb CHANGED
@@ -138,7 +138,11 @@ Spree::Core::Engine.add_routes do
138
138
  end
139
139
  end
140
140
 
141
- resources :stock_managements
141
+ resources :stock_managements do
142
+ collection do
143
+ get :calendar
144
+ end
145
+ end
142
146
 
143
147
  resources :product_completion_steps do
144
148
  collection do
@@ -519,6 +523,8 @@ Spree::Core::Engine.add_routes do
519
523
  end
520
524
 
521
525
  namespace :storefront do
526
+ resources :inventory_item
527
+
522
528
  resources :waiting_room_sessions, only: :create
523
529
  resources :vattanac_banks, only: %i[create]
524
530
  resource :cart, controller: :cart, only: %i[show create destroy] do
@@ -544,7 +550,10 @@ Spree::Core::Engine.add_routes do
544
550
  resource :cart_guests, only: %i[create destroy]
545
551
  resources :cart_payment_method_groups, only: %i[index]
546
552
 
547
- resources :accommodations, only: %i[index show]
553
+ resources :accommodations, only: %i[index show] do
554
+ resources :variants, only: %i[index show], module: :accommodations
555
+ end
556
+
548
557
  resources :line_items, only: %i[index show]
549
558
  resources :account_checker
550
559
  resource :account_recovers, only: [:update]
@@ -0,0 +1,21 @@
1
+ class CreateCmInventoryItems < ActiveRecord::Migration[7.0]
2
+ def up
3
+ create_table :cm_inventory_items, if_not_exists: true do |t|
4
+ t.integer :variant_id
5
+ t.date :inventory_date
6
+ t.integer :max_capacity, default: 0, null: false
7
+ t.integer :quantity_available, default: 0, null: false
8
+ t.integer :product_type, default: 0, null: false
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :cm_inventory_items, :variant_id, if_not_exists: true
14
+ add_index :cm_inventory_items, :inventory_date, if_not_exists: true
15
+ add_index :cm_inventory_items, [:variant_id, :inventory_date], unique: true, if_not_exists: true
16
+ end
17
+
18
+ def down
19
+ drop_table :cm_inventory_items
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ class AddNestedSetColumnsToPlaces < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :cm_places, :parent_id, :integer if !column_exists?(:cm_places, :parent_id)
4
+ add_column :cm_places, :lft, :integer if !column_exists?(:cm_places, :lft)
5
+ add_column :cm_places, :rgt, :integer if !column_exists?(:cm_places, :rgt)
6
+
7
+ add_column :cm_places, :depth, :integer if !column_exists?(:cm_places, :depth)
8
+ add_column :cm_places, :children_count, :integer if !column_exists?(:cm_places, :children_count)
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class AddLockVersionToCmInventoryItems < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :cm_inventory_items, :lock_version, :integer, default: 0, null: false
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class CreateCmTaxonOptionTypes < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :cm_taxon_option_types do |t|
4
+ t.references :taxon, null: false, if_not_exists: true
5
+ t.references :option_type, null: false, if_not_exists: true
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class CreateCmTaxonOptionValues < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :cm_taxon_option_values do |t|
4
+ t.references :taxon, null: false, if_not_exists: true
5
+ t.references :option_value, null: false, if_not_exists: true
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class AddIndexToSpreeProducts < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_index :spree_products, :product_type unless index_exists?(:spree_products, :product_type)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddProductTypeToSpreeVariants < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :spree_variants, :product_type, :integer, if_not_exists: true
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddProductTypeToSpreeLineItems < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :spree_line_items, :product_type, :integer, if_not_exists: true
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddTripCountToCmVendorStops < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_column :cm_vendor_stops, :trip_count, :integer, default: 0, if_not_exists: true
4
+ end
5
+ end
data/docker-compose.yml CHANGED
@@ -20,7 +20,7 @@ services:
20
20
 
21
21
  volumes:
22
22
  - .:/gem
23
- - /gem/vendor/bundle
23
+ - ./vendor/bundle:/gem/vendor/bundle
24
24
 
25
25
  # depends_on:
26
26
  # - db
@@ -40,9 +40,17 @@ module SpreeCmCommissioner
40
40
  after: %r{//= require spree/backend}, verbose: true
41
41
 
42
42
  # For NPM support
43
- template 'app/javascript/spree_cm_commissioner/utilities.js'
44
- inject_into_file 'app/javascript/spree-dashboard.js', "\nimport \"./spree_cm_commissioner/utilities.js\"",
45
- after: %r{import "@spree/dashboard"}, verbose: true
43
+ if File.exist?(File.join(destination_root, 'app/javascript/spree_dashboard'))
44
+ template 'app/javascript/spree_dashboard/spree_cm_commissioner/utilities.js'
45
+ inject_into_file 'app/javascript/spree_dashboard/spree-dashboard.js', "\nimport \"./spree_cm_commissioner/utilities.js\"",
46
+ after: %r{import "@spree/dashboard"}, verbose: true
47
+ else
48
+ Logger.new($stdout).debug <<~MSG
49
+ SpreeCmCommissioner: JavaScript files for the dashboard are missing.
50
+ Please move your JavaScript files to the appropriate location in
51
+ app/javascript/spree_dashboard.
52
+ MSG
53
+ end
46
54
  end
47
55
 
48
56
  def install_telegram_web_bot
@@ -6,3 +6,7 @@ const $ = jquery;
6
6
  document.addEventListener("spree:load", function () {
7
7
  $('[data-toggle="popover"]').popover();
8
8
  });
9
+
10
+ document.documentElement.addEventListener("turbo:frame-load", (event) => {
11
+ $('[data-toggle="popover"]').popover();
12
+ });
@@ -0,0 +1,23 @@
1
+ module SpreeCmCommissioner
2
+ class CachedInventoryItem
3
+ attr_reader :inventory_key, :active, :quantity_available, :inventory_item_id, :variant_id
4
+
5
+ def initialize(inventory_key:, active:, quantity_available:, inventory_item_id:, variant_id:)
6
+ @inventory_key = inventory_key
7
+ @active = active
8
+ @quantity_available = quantity_available
9
+ @inventory_item_id = inventory_item_id
10
+ @variant_id = variant_id
11
+ end
12
+
13
+ def active?
14
+ active
15
+ end
16
+
17
+ def to_h
18
+ instance_variables.each_with_object({}) do |var, hash|
19
+ hash[var.to_s.delete('@').to_sym] = instance_variable_get(var)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
2
2
  class CalendarEvent
3
3
  attr_reader :from_date, :to_date, :title, :options
4
4
 
5
- def initialize(from_date:, to_date:, title:, options:)
5
+ def initialize(from_date:, to_date:, title: nil, options: nil)
6
6
  @from_date = from_date
7
7
  @to_date = to_date
8
8
  @title = title
@@ -23,5 +23,15 @@ module SpreeCmCommissioner
23
23
  )
24
24
  end
25
25
  end
26
+
27
+ def self.from_inventory_items(inventory_items)
28
+ inventory_items.map do |item|
29
+ CalendarEvent.new(
30
+ from_date: item.inventory_date,
31
+ to_date: item.inventory_date,
32
+ options: { inventory_item: item }
33
+ )
34
+ end
35
+ end
26
36
  end
27
37
  end
@@ -17,7 +17,7 @@ FactoryBot.define do
17
17
  end
18
18
 
19
19
  factory :cm_product_homepage_section_relatable do
20
- relatable { create(:product) }
20
+ relatable { create(:cm_product) }
21
21
  end
22
22
 
23
23
  factory :cm_taxon_homepage_section_relatable do
@@ -0,0 +1,9 @@
1
+ FactoryBot.define do
2
+ factory :cm_inventory_item, class: SpreeCmCommissioner::InventoryItem do
3
+ variant { create(:cm_variant) }
4
+ max_capacity { 10 }
5
+ quantity_available { 10 }
6
+ product_type { 'accommodation' }
7
+ inventory_date { Date.today }
8
+ end
9
+ end
@@ -16,7 +16,7 @@ FactoryBot.define do
16
16
  variant { product.master }
17
17
 
18
18
  factory :cm_need_confirmation_line_item do
19
- product { create(:product, need_confirmation: true) }
19
+ product { create(:cm_product, need_confirmation: true) }
20
20
  end
21
21
 
22
22
  factory :cm_kyc_line_item do
@@ -10,5 +10,15 @@ FactoryBot.define do
10
10
  address_components { FFaker::Address.street_address }
11
11
  lat { FFaker::Geolocation.lat }
12
12
  lon { FFaker::Geolocation.lng }
13
+ trait :with_parent do
14
+ association :parent, factory: :cm_place
15
+ end
16
+ transient do
17
+ children_count { 0 }
18
+ end
19
+
20
+ after(:create) do |place, evaluator|
21
+ create_list(:cm_place, evaluator.children_count, parent: place)
22
+ end
13
23
  end
14
- end
24
+ end
@@ -1,6 +1,19 @@
1
1
  FactoryBot.define do
2
- factory :cm_product, parent: :base_product do
2
+ factory :cm_product_in_stock, parent: :product_in_stock do
3
+ product_type { :ecommerce }
4
+ end
5
+
6
+ factory :cm_product_with_option_types, parent: :product_with_option_types do
7
+ product_type { :ecommerce }
8
+ end
9
+
10
+ factory :cm_base_product, parent: :base_product do
11
+ product_type { :ecommerce }
12
+ end
13
+
14
+ factory :cm_product, parent: :product do
3
15
  vendor { Spree::Vendor.first || create(:cm_vendor) }
16
+ product_type { :ecommerce }
4
17
 
5
18
  before(:create) do |product|
6
19
  create(:stock_location) unless Spree::StockLocation.any?
@@ -85,7 +98,7 @@ FactoryBot.define do
85
98
  option_value2 = create(:cm_option_value, presentation: "#{evaluator.due_date} Days", name: evaluator.due_date.to_s, option_type: product.option_types[1])
86
99
  option_value3 = create(:cm_option_value, presentation: "#{evaluator.payment_option}", name: evaluator.payment_option.to_s, option_type: product.option_types[2])
87
100
 
88
- variant = create(:variant, price: product.price, product: product)
101
+ variant = create(:cm_variant, price: product.price, product: product)
89
102
  variant.option_values = [option_value1, option_value2, option_value3]
90
103
  variant.save!
91
104
 
@@ -103,7 +116,7 @@ FactoryBot.define do
103
116
  end
104
117
 
105
118
  after(:create) do |product, evaluator|
106
- variant = create(:variant, price: product.price, product: product)
119
+ variant = create(:cm_variant, price: product.price, product: product)
107
120
  variant.save!
108
121
 
109
122
  variant.stock_items.first.adjust_count_on_hand(evaluator.total_inventory)
@@ -136,12 +149,12 @@ FactoryBot.define do
136
149
  create(:cm_option_value, presentation: 'Yes', name: '1', option_type: product.option_types.find_by(name: 'bib-pre-generation-on-create'))
137
150
  end
138
151
 
139
- variant1 = create(:variant, price: product.price, product: product)
152
+ variant1 = create(:cm_variant, price: product.price, product: product)
140
153
  variant1.option_values = [option_value1]
141
154
  variant1.option_values << bib_pre_generation_option_value if evaluator.bib_pre_generation_on_create
142
155
  variant1.save!
143
156
 
144
- variant2 = create(:variant, price: product.price, product: product)
157
+ variant2 = create(:cm_variant, price: product.price, product: product)
145
158
  variant2.option_values = [option_value2]
146
159
  variant2.option_values << bib_pre_generation_option_value if evaluator.bib_pre_generation_on_create
147
160
  variant2.save!
@@ -24,8 +24,8 @@ FactoryBot.define do
24
24
  # variant will add itself to all stock_locations in an after_create
25
25
  # creating a product will automatically create a master variant
26
26
  store = Spree::Store.first || create(:store)
27
- product_1 = create(:product, stores: [store])
28
- product_2 = create(:product, stores: [store])
27
+ product_1 = create(:cm_product, stores: [store])
28
+ product_2 = create(:cm_product, stores: [store])
29
29
 
30
30
  stock_location.stock_items.where(variant_id: product_1.master.id).first.adjust_count_on_hand(10)
31
31
  stock_location.stock_items.where(variant_id: product_2.master.id).first.adjust_count_on_hand(20)