spree_cm_commissioner 1.10.0.pre.pre1 → 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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +131 -100
  3. data/.vscode/settings.json +1 -1
  4. data/Gemfile.lock +1 -1
  5. data/Rakefile +33 -4
  6. data/app/controllers/spree/admin/stock_managements_controller.rb +39 -0
  7. data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
  8. data/app/interactors/spree_cm_commissioner/create_event.rb +23 -0
  9. data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +40 -0
  10. data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +13 -0
  11. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +6 -2
  12. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +24 -0
  13. data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +2 -2
  14. data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +7 -0
  15. data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +11 -0
  16. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +3 -2
  17. data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +1 -3
  18. data/app/models/spree_cm_commissioner/inventory_item.rb +21 -3
  19. data/app/models/spree_cm_commissioner/line_item_decorator.rb +16 -6
  20. data/app/models/spree_cm_commissioner/place.rb +11 -2
  21. data/app/models/spree_cm_commissioner/product_decorator.rb +8 -2
  22. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +14 -2
  23. data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +2 -8
  24. data/app/models/spree_cm_commissioner/stock_item_decorator.rb +18 -0
  25. data/app/models/spree_cm_commissioner/taxon_decorator.rb +11 -0
  26. data/app/models/spree_cm_commissioner/taxon_option_type.rb +8 -0
  27. data/app/models/spree_cm_commissioner/taxon_option_value.rb +8 -0
  28. data/app/models/spree_cm_commissioner/trip.rb +0 -11
  29. data/app/models/spree_cm_commissioner/trip_connection.rb +1 -0
  30. data/app/models/spree_cm_commissioner/trip_stop.rb +12 -5
  31. data/app/models/spree_cm_commissioner/variant_decorator.rb +19 -11
  32. data/app/models/spree_cm_commissioner/variant_options.rb +0 -14
  33. data/app/models/spree_cm_commissioner/vendor_stop.rb +2 -1
  34. data/app/queries/spree_cm_commissioner/trip_query.rb +11 -11
  35. data/app/queries/spree_cm_commissioner/vendor_stop_place_query.rb +54 -0
  36. data/app/serializers/spree/v2/tenant/guest_serializer.rb +1 -0
  37. data/app/services/spree_cm_commissioner/organizer/export_guest_csv_service.rb +2 -0
  38. data/app/views/spree/admin/stock_managements/_events_popover.html.erb +12 -6
  39. data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +3 -1
  40. data/app/views/spree/admin/stock_managements/calendar.html.erb +6 -3
  41. data/app/views/spree/admin/stock_managements/index.html.erb +9 -0
  42. data/config/initializers/spree_permitted_attributes.rb +5 -0
  43. data/db/migrate/20250418072528_add_nested_set_columns_to_places.rb +10 -0
  44. data/db/migrate/20250430091742_create_cm_taxon_option_types.rb +9 -0
  45. data/db/migrate/20250430092928_create_cm_taxon_option_values.rb +9 -0
  46. data/db/migrate/20250502025848_add_index_to_spree_products.rb +5 -0
  47. data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +5 -0
  48. data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +5 -0
  49. data/db/migrate/20250506092929_add_trip_count_to_cm_vendor_stops.rb +5 -0
  50. data/lib/generators/spree_cm_commissioner/install/install_generator.rb +11 -3
  51. data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
  52. data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +1 -1
  53. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
  54. data/lib/spree_cm_commissioner/test_helper/factories/place_factory.rb +11 -1
  55. data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +18 -5
  56. data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
  57. data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +12 -1
  58. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +1 -1
  59. data/lib/spree_cm_commissioner/version.rb +1 -1
  60. data/lib/tasks/ensure_correct_product_type.rake +7 -0
  61. data/lib/tasks/migrate_and_rebuild_place_hierarchy.rake +9 -0
  62. data/lib/tasks/update_orphan_root_places.rake +7 -0
  63. metadata +20 -3
  64. data/app/models/spree_cm_commissioner/stock_movement_decorator.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e91babac12da4e1fc7e8083b3cb07936fd5a7b7af45217ef38b2a47fc3bed9ca
4
- data.tar.gz: b5831b6964e0ea47ac4a6c7c2355330c9ed8386c160ec482ab8201100321a0cf
3
+ metadata.gz: 3c727894cc45a2d005e5c0254542ce6b8bfc054a30fe9d7b9fa6c7dfea61cfc4
4
+ data.tar.gz: 9d99d30cfc27e987bffda2e0462e91a8cbe4d9a88c44c18dc67df02e947f3278
5
5
  SHA512:
6
- metadata.gz: 8f229d110a73433c448dece4554dfc99f60e0b9208e5260282b7b48c2f2869a9431700b8f418ba0ed0f00a6369924acf370506dc22234d678c157a4984b6d49d
7
- data.tar.gz: b0913cc8c5bfc42042bd78bc23a3e13050b7cd39e9cbf315ae6f0f99dad38ce3984c1f042d34b95d29688c51ce9c9f67b38c450a857063659f395c297233bb3f
6
+ metadata.gz: 51065e106d1939327817aa8c54154d1711faee026b771010c9f104083da88aad01aa2f3ced6383c8f3c952e8c8cfba8035324271ee92548dc2e22449da601bf1
7
+ data.tar.gz: dbc3410043aea748600477cfdd6724d1916155df35e28b62495a53950475f122eac3bf8b80de97282c82214e60ee880e807629246467ca8872753c519eb4afad
@@ -3,96 +3,117 @@ name: Commissioner Gem
3
3
 
4
4
  on:
5
5
  pull_request:
6
+ types:
7
+ - opened
8
+ - edited
9
+ - synchronize
10
+ - reopened
6
11
  branches:
7
12
  - develop
13
+ - milestone-77-scalable-design
8
14
  push:
9
15
  tags:
10
16
  - "*"
11
17
  jobs:
12
- validate-commits:
13
- runs-on: ubuntu-latest
18
+ # validate-commits:
19
+ # runs-on: ubuntu-latest
20
+
21
+ # steps:
22
+ # - name: Check PR title format
23
+ # uses: actions/github-script@v7
24
+ # with:
25
+ # script: |
26
+ # const title = context.payload.pull_request.title.trim();
27
+
28
+ # console.log("PR title: ", title);
29
+
30
+ # const pattern = /^Close\s+#\d+\s.+/;
31
+
32
+ # if (!pattern.test(title)) {
33
+ # core.setFailed(
34
+ # `PR title must start with "Close #issue_number message". Example: Close #123 Add login form`
35
+ # );
36
+ # } else {
37
+ # console.log("PR title format is correct.");
38
+ # }
39
+ # - name: Check commit messages format
40
+ # uses: actions/github-script@v7
41
+ # with:
42
+ # script: |
43
+ # const pr = context.payload.pull_request;
44
+ # const commits_url = pr.commits_url;
45
+
46
+ # const commits = await github.request(commits_url);
47
+ # const pattern = /^Close\s+#\d+\s.+/;
48
+
49
+ # let invalidCommits = [];
50
+
51
+ # for (const commit of commits.data) {
52
+ # const msg = commit.commit.message.trim();
53
+
54
+ # console.log("commit message: ", msg);
55
+
56
+ # if (!pattern.test(msg)) {
57
+ # invalidCommits.push(`- ${msg}`);
58
+ # }
59
+ # }
60
+
61
+ # if (invalidCommits.length > 0) {
62
+ # core.setFailed(
63
+ # `The following commit messages are not in the correct format:\n\n${invalidCommits.join(
64
+ # '\n'
65
+ # )}\n\nEach commit message must start with "Close #<issue_number> <message>"`
66
+ # );
67
+ # } else {
68
+ # console.log("All commit messages are correctly formatted.");
69
+ # }
70
+ # - name: Check for unresolved review threads
71
+ # uses: actions/github-script@v7
72
+ # with:
73
+ # script: |
74
+ # const prNumber = context.payload.pull_request.number;
75
+ # const { owner, repo } = context.repo;
76
+
77
+ # const query = `
78
+ # query($owner: String!, $repo: String!, $prNumber: Int!) {
79
+ # repository(owner: $owner, name: $repo) {
80
+ # pullRequest(number: $prNumber) {
81
+ # reviewThreads(first: 100) {
82
+ # nodes {
83
+ # isResolved
84
+ # comments(first: 1) {
85
+ # nodes {
86
+ # body
87
+ # author {
88
+ # login
89
+ # }
90
+ # }
91
+ # }
92
+ # }
93
+ # }
94
+ # }
95
+ # }
96
+ # }
97
+ # `;
98
+ # const variables = { owner, repo, prNumber };
99
+ # const result = await github.graphql(query, variables);
100
+ # const threads = result.repository.pullRequest.reviewThreads.nodes;
101
+
102
+ # const unresolved = threads.filter(t => !t.isResolved);
103
+
104
+ # if (unresolved.length > 0) {
105
+ # unresolved.forEach(thread => {
106
+ # const comments = thread.comments.nodes;
107
+ # if (comments.length > 0) {
108
+ # console.log(`💬 Comment by ${comments[0].author.login}: ${comments[0].body}`);
109
+ # }
110
+ # });
111
+ # core.setFailed(`❌ There are ${unresolved.length} unresolved review thread(s). Please resolve them before merging.`);
112
+ # } else {
113
+ # console.log("✅ All review threads are resolved.");
114
+ # }
14
115
 
15
- steps:
16
- - name: Check PR title format
17
- uses: actions/github-script@v7
18
- if: github.event_name == 'pull_request'
19
- with:
20
- script: |
21
- const title = context.payload.pull_request.title.trim();
22
-
23
- console.log("PR title: ", title);
24
-
25
- const pattern = /^Close\s+#\d+\s.+/;
26
-
27
- if (!pattern.test(title)) {
28
- core.setFailed(
29
- `PR title must start with "Close #issue_number message". Example: Close #123 Add login form`
30
- );
31
- } else {
32
- console.log("PR title format is correct.");
33
- }
34
- - name: Check commit messages format
35
- uses: actions/github-script@v7
36
- if: github.event_name == 'pull_request'
37
- with:
38
- script: |
39
- const pr = context.payload.pull_request;
40
- const commits_url = pr.commits_url;
41
-
42
- const commits = await github.request(commits_url);
43
- const pattern = /^Close\s+#\d+\s.+/;
44
-
45
- let invalidCommits = [];
46
-
47
- for (const commit of commits.data) {
48
- const msg = commit.commit.message.trim();
49
-
50
- console.log("commit message: ", msg);
51
-
52
- if (!pattern.test(msg)) {
53
- invalidCommits.push(`- ${msg}`);
54
- }
55
- }
56
-
57
- if (invalidCommits.length > 0) {
58
- core.setFailed(
59
- `The following commit messages are not in the correct format:\n\n${invalidCommits.join(
60
- '\n'
61
- )}\n\nEach commit message must start with "Close #<issue_number> <message>"`
62
- );
63
- } else {
64
- console.log("All commit messages are correctly formatted.");
65
- }
66
- - name: Check for unresolved review threads
67
- uses: actions/github-script@v7
68
- if: github.event_name == 'pull_request'
69
- with:
70
- script: |
71
- const pr = context.payload.pull_request;
72
- const owner = context.repo.owner;
73
- const repo = context.repo.repo;
74
-
75
- try {
76
- const response = await github.rest.pulls.listReviewComments({
77
- owner,
78
- repo,
79
- pull_number: pr.number,
80
- per_page: 100
81
- });
82
-
83
- const unresolved = response.data.filter(comment => comment.in_reply_to_id && !comment.resolved);
84
-
85
- if (unresolved.length > 0) {
86
- core.setFailed(`There are ${unresolved.length} unresolved review comment(s). Please resolve all threads before merging.`);
87
- } else {
88
- console.log("All review comments are resolved.");
89
- }
90
- } catch (error) {
91
- console.error("Error fetching review comments:", error.message);
92
- core.setFailed("Failed to check for unresolved review threads.");
93
- }
94
116
  test_and_build_gem:
95
- needs: [validate-commits]
96
117
  # if: github.head_ref != '2572-enforce-pr-workflow' || github.base_ref != 'develop'
97
118
  runs-on: ubuntu-latest
98
119
 
@@ -112,6 +133,17 @@ jobs:
112
133
  --health-timeout 5s
113
134
  --health-retries 5
114
135
 
136
+ redis:
137
+ image: redis
138
+ # Set health checks to wait until redis has started
139
+ options: >-
140
+ --health-cmd "redis-cli ping"
141
+ --health-interval 10s
142
+ --health-timeout 5s
143
+ --health-retries 5
144
+ ports:
145
+ - 6379:6379 # Maps port 6379 on service container to the host
146
+
115
147
  steps:
116
148
  - uses: actions/checkout@v3
117
149
 
@@ -132,27 +164,26 @@ jobs:
132
164
  gem install bundler
133
165
  bundle install --jobs 4 --retry 3
134
166
 
135
- - name: Quality
136
- if: github.event_name == 'pull_request'
137
- env:
138
- DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
139
- run: |
140
- bundle exec rubocop
167
+ # - name: Quality
168
+ # env:
169
+ # DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
170
+ # run: |
171
+ # bundle exec rubocop
141
172
 
142
- - name: Security
143
- if: github.event_name == 'pull_request'
144
- env:
145
- DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
146
- run: |
147
- bundle exec brakeman
173
+ # - name: Security
174
+ # env:
175
+ # DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
176
+ # run: |
177
+ # bundle exec brakeman
148
178
 
149
- - name: Run test
150
- env:
151
- DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
179
+ # - name: Run test
180
+ # env:
181
+ # DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
182
+ # REDIS_URL: redis://localhost:6379/0
152
183
 
153
- if: github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'develop'
154
- run: |
155
- bundle exec rake
184
+ # if: github.event_name == 'pull_request'
185
+ # run: |
186
+ # bundle exec rake
156
187
 
157
188
  # 2405-build-and-publish-gem
158
189
  # - name: Rename long migration files
@@ -3,6 +3,6 @@
3
3
  "files.trimTrailingWhitespace": true,
4
4
  "editor.formatOnSave": true,
5
5
  "[yaml]": {
6
- "editor.defaultFormatter": "aaron-bond.better-comments"
6
+ "editor.defaultFormatter": "redhat.vscode-yaml"
7
7
  },
8
8
  }
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (1.10.0.pre.pre1)
37
+ spree_cm_commissioner (1.11.0.pre.pre)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
4
  require 'rspec/core/rake_task'
5
- require 'spree/testing_support/extension_rake'
5
+ require 'spree/testing_support/common_rake'
6
6
 
7
7
  RSpec::Core::RakeTask.new
8
8
 
@@ -14,8 +14,37 @@ task :default do
14
14
  Rake::Task[:spec].invoke
15
15
  end
16
16
 
17
+ # Copied from:
18
+ # spree_core:lib/spree/testing_support/common_rake.rb
19
+ # But refactored to skip frontend & backend setup.
17
20
  desc 'Generates a dummy app for testing'
18
- task :test_app do
19
- ENV['LIB_NAME'] = 'spree_cm_commissioner'
20
- Rake::Task['extension:test_app'].invoke
21
+ task :test_app do |_t, args|
22
+ require 'spree_cm_commissioner'
23
+ require "generators/spree_cm_commissioner/install/install_generator"
24
+
25
+ Rails.env = ENV['RAILS_ENV'] = 'test'
26
+
27
+ Spree::DummyGeneratorHelper.inject_extension_requirements = true
28
+ Spree::DummyGenerator.start ["--lib_name=spree_cm_commissioner", '--quiet']
29
+ Spree::InstallGenerator.start [
30
+ "--lib_name=spree_cm_commissioner",
31
+ '--auto-accept',
32
+ '--migrate=false',
33
+ '--seed=false',
34
+ '--sample=false',
35
+ '--quiet',
36
+ '--copy_storefront=false',
37
+ "--install_storefront=false",
38
+ "--install_admin=false",
39
+ "--user_class=Spree::User"
40
+ ]
41
+
42
+ puts 'Setting up dummy database...'
43
+ system('bin/rails db:environment:set RAILS_ENV=test')
44
+ system('bundle exec rake db:drop db:create')
45
+ Spree::DummyModelGenerator.start
46
+ system('bundle exec rake db:migrate')
47
+
48
+ puts 'Running extension installation generator...'
49
+ SpreeCmCommissioner::Generators::InstallGenerator.start(['--auto-run-migrations'])
21
50
  end
@@ -5,6 +5,8 @@ module Spree
5
5
 
6
6
  before_action :load_parent
7
7
 
8
+ helper_method :inventory_item_message
9
+
8
10
  def load_parent
9
11
  @product = Spree::Product.find_by(slug: params[:product_id])
10
12
  end
@@ -17,6 +19,23 @@ module Spree
17
19
  load_inventories unless @product.permanent_stock?
18
20
  end
19
21
 
22
+ def create
23
+ result = SpreeCmCommissioner::Stock::StockMovementCreator.call(
24
+ variant_id: params[:variant_id],
25
+ stock_location_id: params[:stock_location_id],
26
+ current_store: current_store,
27
+ stock_movement_params: stock_movement_params
28
+ )
29
+
30
+ if result.success?
31
+ flash[:success] = flash_message_for(result.stock_movement, :successfully_created)
32
+ else
33
+ flash[:error] = result.message
34
+ end
35
+
36
+ redirect_back fallback_location: admin_product_stock_managements_path(@product)
37
+ end
38
+
20
39
  def calendar
21
40
  @year = params[:year].present? ? params[:year].to_i : Time.zone.today.year
22
41
 
@@ -24,13 +43,29 @@ module Spree
24
43
  to_date = Date.new(@year, 1, 1).end_of_year
25
44
 
26
45
  @inventory_items = @product.inventory_items.includes(:variant).where(inventory_date: from_date..to_date).to_a
46
+ @cached_inventory_items = ::SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new(@inventory_items)
47
+ .call
48
+ .index_by(&:inventory_item_id)
27
49
  @events = SpreeCmCommissioner::CalendarEvent.from_inventory_items(@inventory_items)
28
50
  end
29
51
 
52
+ def inventory_item_message(inventory_item, cached_inventory_item)
53
+ synced = inventory_item.quantity_available == cached_inventory_item.quantity_available
54
+
55
+ if synced
56
+ "Synced: Quantity available matches in both DB and Redis (#{cached_inventory_item.quantity_available})."
57
+ else
58
+ "Out of sync: Redis shows #{cached_inventory_item.quantity_available} available, which doesn't match the database."
59
+ end
60
+ end
61
+
30
62
  private
31
63
 
32
64
  def load_inventories
33
65
  @inventory_items = @product.inventory_items.group_by { |item| item.variant.id }
66
+ @cached_inventory_items = ::SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new(@product.inventory_items)
67
+ .call
68
+ .index_by(&:inventory_item_id)
34
69
  @reserved_stocks = Spree::LineItem
35
70
  .complete
36
71
  .where(variant_id: @variants.pluck(:id))
@@ -41,6 +76,10 @@ module Spree
41
76
  def model_class
42
77
  Spree::StockItem
43
78
  end
79
+
80
+ def stock_movement_params
81
+ params.require(:stock_movement).permit(permitted_stock_movement_attributes)
82
+ end
44
83
  end
45
84
  end
46
85
  end
@@ -12,9 +12,9 @@ module Spree
12
12
  spree_authorize! :update, spree_current_order, order_token
13
13
  spree_authorize! :show, @variant
14
14
 
15
- availability_checker = SpreeCmCommissioner::Stock::AvailabilityChecker.new(@variant)
15
+ availability_checker = SpreeCmCommissioner::Stock::AvailabilityChecker.new(@variant, add_item_params[:options])
16
16
 
17
- unless availability_checker.can_supply?(add_item_params[:quantity].to_i, add_item_params[:options])
17
+ unless availability_checker.can_supply?(add_item_params[:quantity].to_i)
18
18
  return render_error_payload(availability_checker.error_message || I18n.t('variant_availability.items_out_of_stock'))
19
19
  end
20
20
 
@@ -10,6 +10,8 @@ module SpreeCmCommissioner
10
10
  assign_prototype
11
11
  create_child_taxon
12
12
  build_home_banner
13
+ assign_option_types
14
+ assign_option_values
13
15
  end
14
16
  end
15
17
 
@@ -61,5 +63,26 @@ module SpreeCmCommissioner
61
63
 
62
64
  context.fail!(message: 'Home banner upload failed') unless banner.persisted?
63
65
  end
66
+
67
+ def assign_options(model_class, param_key, foreign_key)
68
+ return unless params[param_key]
69
+
70
+ params[param_key].each do |id|
71
+ record = model_class.new(
72
+ taxon_id: @parent_taxon.id,
73
+ foreign_key => id
74
+ )
75
+
76
+ context.fail!(message: record.errors.full_messages.join(', ')) unless record.save
77
+ end
78
+ end
79
+
80
+ def assign_option_types
81
+ assign_options(SpreeCmCommissioner::TaxonOptionType, :option_type_id, :option_type_id)
82
+ end
83
+
84
+ def assign_option_values
85
+ assign_options(SpreeCmCommissioner::TaxonOptionValue, :option_value_id, :option_value_id)
86
+ end
64
87
  end
65
88
  end
@@ -0,0 +1,40 @@
1
+ module SpreeCmCommissioner
2
+ class EnsureCorrectProductType < BaseInteractor
3
+ def call
4
+ Spree::Product
5
+ .left_joins(variants_including_master: %i[inventory_items line_items])
6
+ .where(
7
+ 'spree_variants.product_type IS NULL OR
8
+ spree_variants.product_type != spree_products.product_type OR
9
+
10
+ cm_inventory_items.product_type IS NULL OR
11
+ cm_inventory_items.product_type != spree_products.product_type OR
12
+
13
+ spree_line_items.product_type IS NULL OR
14
+ spree_line_items.product_type != spree_products.product_type OR
15
+
16
+ spree_products.product_type IS NOT NULL
17
+ '
18
+ )
19
+ .distinct.find_each do |product|
20
+ sync_product_type_for(product)
21
+ end
22
+ end
23
+
24
+ def sync_product_type_for(product)
25
+ product_type = Spree::Variant.product_types[product.product_type]
26
+
27
+ product.variants_including_master
28
+ .where('spree_variants.product_type IS NULL OR spree_variants.product_type != ?', product_type)
29
+ .update_all(product_type: product_type) # rubocop:disable Rails/SkipsModelValidations
30
+
31
+ product.line_items
32
+ .where('spree_line_items.product_type IS NULL OR spree_line_items.product_type != ?', product_type)
33
+ .update_all(product_type: product_type) # rubocop:disable Rails/SkipsModelValidations
34
+
35
+ product.inventory_items
36
+ .where('cm_inventory_items.product_type IS NULL OR cm_inventory_items.product_type != ?', product_type)
37
+ .update_all(product_type: product_type) # rubocop:disable Rails/SkipsModelValidations
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class InventoryItemsAdjuster < BaseInteractor
4
+ delegate :variant, :quantity, to: :context
5
+
6
+ def call
7
+ variant.inventory_items.active.find_each do |inventory_item|
8
+ inventory_item.adjust_quantity!(quantity)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -5,6 +5,10 @@ module SpreeCmCommissioner
5
5
 
6
6
  def variants_per_batch = 1000
7
7
 
8
+ def pre_inventory_days_for(variant)
9
+ context.pre_inventory_days || variant.pre_inventory_days
10
+ end
11
+
8
12
  def call
9
13
  variants.in_batches(of: variants_per_batch) do |batch|
10
14
  generate_inventory_items_for_batch(batch)
@@ -31,7 +35,7 @@ module SpreeCmCommissioner
31
35
 
32
36
  def inventory_dates_for(variant)
33
37
  start_date = Time.zone.tomorrow
34
- end_date = Time.zone.today + variant.pre_inventory_days
38
+ end_date = Time.zone.today + pre_inventory_days_for(variant)
35
39
 
36
40
  (start_date..end_date)
37
41
  end
@@ -62,7 +66,7 @@ module SpreeCmCommissioner
62
66
  end
63
67
 
64
68
  def variants
65
- scope = Spree::Variant.active.with_permanent_stock.where(is_master: false).includes(:product)
69
+ scope = Spree::Variant.active.with_permanent_stock.where(is_master: false)
66
70
  scope = scope.where(id: variant_ids) if variant_ids.present?
67
71
  scope
68
72
  end
@@ -0,0 +1,24 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class StockMovementCreator < BaseInteractor
4
+ delegate :variant_id, :stock_location_id, :current_store, :stock_movement_params, to: :context
5
+
6
+ def call
7
+ variant = current_store.variants.find(variant_id)
8
+
9
+ return context.fail!(message: Spree.t(:doesnt_track_inventory)) unless variant.track_inventory?
10
+
11
+ stock_location = Spree::StockLocation.find(stock_location_id)
12
+ stock_movement = stock_location.stock_movements.build(stock_movement_params)
13
+ stock_movement.stock_item = stock_location.set_up_stock_item(variant)
14
+
15
+ if stock_movement.save
16
+ SpreeCmCommissioner::Stock::InventoryItemsAdjusterJob.perform_later(variant_id: variant.id, quantity: stock_movement.quantity)
17
+ context.stock_movement = stock_movement
18
+ else
19
+ context.fail!(message: stock_movement.errors.full_messages.join(', '))
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -53,7 +53,7 @@ module SpreeCmCommissioner
53
53
  user_data = context.user_data
54
54
 
55
55
  identity = SpreeCmCommissioner::UserIdentityProvider.vattanac_bank
56
- .find_or_initialize_by(sub: user_data['id'])
56
+ .find_or_initialize_by(sub: user_data['phoneNum'])
57
57
 
58
58
  if identity.persisted?
59
59
  context.user = identity.user
@@ -78,7 +78,7 @@ module SpreeCmCommissioner
78
78
  identity = context.identity
79
79
 
80
80
  identity.name = full_name
81
-
81
+ identity.email = user_data['email']
82
82
  context.user = Spree::User.new(
83
83
  first_name: user_data['firstName'],
84
84
  last_name: user_data['lastName'],
@@ -0,0 +1,7 @@
1
+ module SpreeCmCommissioner
2
+ class EnsureCorrectProductTypeJob < ApplicationJob
3
+ def perform
4
+ SpreeCmCommissioner::EnsureCorrectProductType.call
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module SpreeCmCommissioner
2
+ module Stock
3
+ class InventoryItemsAdjusterJob < ApplicationUniqueJob
4
+ def perform(variant_id:, quantity:)
5
+ variant = Spree::Variant.find(variant_id)
6
+
7
+ SpreeCmCommissioner::Stock::InventoryItemsAdjuster.call(variant: variant, quantity: quantity)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -20,6 +20,7 @@ module SpreeCmCommissioner
20
20
  vehicle_id
21
21
  origin
22
22
  destination
23
+ place_id
23
24
  ].freeze
24
25
 
25
26
  RESERVED_OPTIONS = {
@@ -47,8 +48,8 @@ module SpreeCmCommissioner
47
48
  'bib-display-prefix' => 'boolean',
48
49
  'bib-pre-generation-on-create' => 'boolean',
49
50
  'seat-number-positions' => 'array',
50
- 'origin' => 'origin',
51
- 'destination' => 'destination',
51
+ 'origin' => 'place_id',
52
+ 'destination' => 'place_id',
52
53
  'departure-time' => 'time',
53
54
  'vehicle' => 'vehicle_id',
54
55
  'allow-seat-selection' => 'boolean'
@@ -3,12 +3,10 @@ module SpreeCmCommissioner
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- delegate :product_type,
7
- :subscribable?,
6
+ delegate :subscribable?,
8
7
  :allowed_upload_later?,
9
8
  :need_confirmation?, :need_confirmation, :kyc,
10
9
  :allow_anonymous_booking,
11
- :accommodation?, :service?, :ecommerce?,
12
10
  :associated_event,
13
11
  :allow_self_check_in,
14
12
  :allow_self_check_in?,