spree_cm_commissioner 2.5.0.pre.pre7 → 2.5.0.pre.pre8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +1 -1
  3. data/.tool-versions +1 -1
  4. data/Gemfile.lock +1 -1
  5. data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +7 -1
  6. data/app/controllers/spree/api/v2/storefront/route_places_controller.rb +9 -9
  7. data/app/finders/spree_cm_commissioner/places/find_with_route.rb +10 -10
  8. data/app/finders/spree_cm_commissioner/routes/find_popular.rb +10 -14
  9. data/app/interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb +1 -1
  10. data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +3 -3
  11. data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +3 -15
  12. data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +0 -2
  13. data/app/models/concerns/spree_cm_commissioner/store_metadata.rb +2 -14
  14. data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +3 -0
  15. data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +7 -1
  16. data/app/models/spree_cm_commissioner/guest.rb +0 -13
  17. data/app/models/spree_cm_commissioner/inventory_item.rb +1 -5
  18. data/app/models/spree_cm_commissioner/line_item_decorator.rb +1 -13
  19. data/app/models/spree_cm_commissioner/option_type_decorator.rb +0 -8
  20. data/app/models/spree_cm_commissioner/option_value_decorator.rb +0 -34
  21. data/app/models/spree_cm_commissioner/order_decorator.rb +0 -1
  22. data/app/models/spree_cm_commissioner/product_decorator.rb +2 -3
  23. data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +2 -2
  24. data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +2 -2
  25. data/app/models/spree_cm_commissioner/taxon_decorator.rb +0 -1
  26. data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +1 -13
  27. data/app/models/spree_cm_commissioner/variant_decorator.rb +4 -7
  28. data/app/models/spree_cm_commissioner/vendor_decorator.rb +0 -4
  29. data/app/presenters/spree/variants/{visible_options_presenter.rb → visable_options_presenter.rb} +4 -2
  30. data/app/request_schemas/spree_cm_commissioner/popular_route_places_request_schema.rb +12 -0
  31. data/app/request_schemas/spree_cm_commissioner/route_places_request_schema.rb +5 -0
  32. data/app/views/spree/admin/tenants/_form.html.erb +18 -0
  33. data/app/views/spree/admin/tenants/form/_footer.html.erb +31 -0
  34. data/app/views/spree/admin/tenants/form/_social.html.erb +31 -0
  35. data/app/views/spree/order_mailer/confirm_email.html.erb +1 -1
  36. data/app/views/spree_cm_commissioner/layouts/order_mailer.html.erb +1 -1
  37. data/app/views/spree_cm_commissioner/order_mailer/tenant/_footer.html.erb +13 -6
  38. data/app/views/spree_cm_commissioner/order_mailer/tenant/_support_contact.html.erb +23 -24
  39. data/config/locales/en.yml +0 -8
  40. data/config/locales/km.yml +0 -8
  41. data/config/routes.rb +0 -8
  42. data/db/migrate/20251209022924_add_contact_fields_to_cm_tenants.rb +9 -0
  43. data/lib/cm_app_logger.rb +3 -21
  44. data/lib/spree_cm_commissioner/version.rb +1 -1
  45. data/lib/spree_cm_commissioner.rb +7 -8
  46. metadata +7 -57
  47. data/app/controllers/spree/admin/integration_mappings_controller.rb +0 -21
  48. data/app/controllers/spree/admin/integration_sessions_controller.rb +0 -21
  49. data/app/controllers/spree/admin/integrations_controller.rb +0 -83
  50. data/app/controllers/spree/api/v2/storefront/event_matches_controller.rb +0 -15
  51. data/app/errors/spree_cm_commissioner/integrations/external_client_error.rb +0 -10
  52. data/app/errors/spree_cm_commissioner/integrations/sync_error.rb +0 -4
  53. data/app/finders/spree_cm_commissioner/events/find_matches.rb +0 -15
  54. data/app/helpers/spree_cm_commissioner/external_integrations_helper.rb +0 -58
  55. data/app/jobs/spree_cm_commissioner/integrations/base_job.rb +0 -39
  56. data/app/jobs/spree_cm_commissioner/integrations/polling_job.rb +0 -53
  57. data/app/jobs/spree_cm_commissioner/integrations/polling_scheduler_job.rb +0 -30
  58. data/app/jobs/spree_cm_commissioner/telegram_alerts/integration_sync_failure_job.rb +0 -17
  59. data/app/models/concerns/spree_cm_commissioner/integrations/integration_mappable.rb +0 -61
  60. data/app/models/concerns/spree_cm_commissioner/line_item_integration.rb +0 -36
  61. data/app/models/concerns/spree_cm_commissioner/order_integration.rb +0 -33
  62. data/app/models/spree_cm_commissioner/integration.rb +0 -29
  63. data/app/models/spree_cm_commissioner/integration_mapping.rb +0 -41
  64. data/app/models/spree_cm_commissioner/integration_sync_session.rb +0 -15
  65. data/app/models/spree_cm_commissioner/integrations/stadium_x_v1.rb +0 -37
  66. data/app/overrides/spree/admin/shared/sub_menu/_integrations/external_integrations.html.erb.deface +0 -6
  67. data/app/services/spree_cm_commissioner/integrations/base/sync_manager.rb +0 -69
  68. data/app/services/spree_cm_commissioner/integrations/base/sync_result.rb +0 -183
  69. data/app/services/spree_cm_commissioner/integrations/polling.rb +0 -70
  70. data/app/services/spree_cm_commissioner/integrations/polling_scheduler.rb +0 -79
  71. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/external_client/client.rb +0 -152
  72. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/inventory/unstock_inventory.rb +0 -83
  73. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_matches.rb +0 -113
  74. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/polling/sync_zones.rb +0 -215
  75. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/resources/base.rb +0 -20
  76. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/resources/league.rb +0 -19
  77. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/resources/match.rb +0 -95
  78. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/resources/ticket.rb +0 -81
  79. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/resources/ticket_image.rb +0 -19
  80. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/resources/zone.rb +0 -90
  81. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_manager.rb +0 -35
  82. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/full_sync_strategy.rb +0 -38
  83. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/incremental_sync_strategy.rb +0 -44
  84. data/app/services/spree_cm_commissioner/integrations/stadium_x_v1/sync_strategies/webhook_sync_strategy.rb +0 -16
  85. data/app/services/spree_cm_commissioner/telegram_alerts/integration_sync_failure.rb +0 -49
  86. data/app/views/spree/admin/integration_mappings/_integration_mappings.html.erb +0 -107
  87. data/app/views/spree/admin/integration_mappings/index.html.erb +0 -33
  88. data/app/views/spree/admin/integration_sessions/_integration_sync_sessions.html.erb +0 -116
  89. data/app/views/spree/admin/integration_sessions/index.html.erb +0 -42
  90. data/app/views/spree/admin/integrations/_form.html.erb +0 -104
  91. data/app/views/spree/admin/integrations/_stadium_x_v1_fields.html.erb +0 -29
  92. data/app/views/spree/admin/integrations/edit.html.erb +0 -45
  93. data/app/views/spree/admin/integrations/index.html.erb +0 -75
  94. data/app/views/spree/admin/integrations/new.html.erb +0 -25
  95. data/db/migrate/20251017094845_create_cm_integrations.rb +0 -22
  96. data/db/migrate/20251017101555_create_cm_integration_sync_sessions.rb +0 -68
  97. data/db/migrate/20251017101605_create_cm_integration_mappings.rb +0 -52
  98. data/lib/spree_cm_commissioner/test_helper/factories/integration_factory.rb +0 -25
  99. data/lib/spree_cm_commissioner/test_helper/factories/integration_mapping_factory.rb +0 -14
  100. data/lib/spree_cm_commissioner/test_helper/factories/integration_sync_session_factory.rb +0 -7
@@ -1,116 +0,0 @@
1
- <div class="table-responsive">
2
- <table
3
- class="table table-sm"
4
- id="listing_integration_sync_sessions"
5
- data-hook
6
- >
7
- <thead class="text-muted">
8
- <tr>
9
- <th><%= Spree.t(:id) %></th>
10
- <th><%= Spree.t(:sync_type) %></th>
11
- <th><%= Spree.t(:status) %></th>
12
- <th><%= Spree.t(:started_at) %></th>
13
- <th><%= Spree.t(:ended_at) %></th>
14
- <th><%= Spree.t(:duration) %></th>
15
- <th></th>
16
- </tr>
17
- </thead>
18
-
19
- <tbody>
20
- <% sync_sessions = defined?(@sync_sessions) ? @sync_sessions : integration.integration_sync_sessions.order(created_at: :desc).limit(25) %>
21
- <% sync_sessions.each do |sess| %>
22
- <tr id="integration_sync_session_<%= sess.id %>">
23
- <td><%= sess.id %></td>
24
- <td><%= sess.sync_type %></td>
25
- <td><%= sess.status %></td>
26
- <td><%= sess.started_at %></td>
27
- <td><%= sess.ended_at %></td>
28
- <td>
29
- <% if sess.started_at && sess.ended_at %>
30
- <% duration = (sess.ended_at - sess.started_at).to_i %>
31
-
32
- <% if duration < 60 %>
33
- <%= "#{duration} sec" %>
34
- <% else %>
35
- <%= "#{duration / 60} min" %>
36
- <% end %>
37
- <% else %>
38
- N/A
39
- <% end %>
40
- </td>
41
- <td class="actions">
42
- <% if sess.sync_result.present? %>
43
- <%= link_to_with_icon 'activity.svg', Spree.t("admin.external_integrations.view_result"), "#session_result_modal_#{sess.id}",
44
- class: 'btn btn-light btn-sm',
45
- no_text: true,
46
- data: { toggle: 'modal', target: "#session_result_modal_#{sess.id}" },
47
- title: Spree.t("admin.external_integrations.sync_result") %>
48
- <% end %>
49
- </td>
50
- </tr>
51
- <% end %>
52
- </tbody>
53
- </table>
54
-
55
- <% if sync_sessions.empty? %>
56
- <small class="form-text text-muted"><%= Spree.t("admin.external_integrations.no_results") %></small>
57
- <% end %>
58
-
59
- <% if defined?(@sync_sessions) && @sync_sessions.respond_to?(:total_pages) %>
60
- <div class="mt-3">
61
- <%= paginate @sync_sessions, theme: 'admin-twitter-bootstrap-4' %>
62
- </div>
63
- <% end %>
64
- </div>
65
-
66
- <% sync_sessions.each do |sess| %>
67
- <% if sess.sync_result.present? %>
68
- <div
69
- class="modal fade"
70
- id="session_result_modal_<%= sess.id %>"
71
- tabindex="-1"
72
- role="dialog"
73
- aria-labelledby="session_result_modal_label_<%= sess.id %>"
74
- aria-hidden="true"
75
- >
76
- <div class="modal-dialog modal-lg" role="document">
77
- <div class="modal-content">
78
- <div class="modal-header">
79
- <h5
80
- class="modal-title"
81
- id="session_result_modal_label_<%= sess.id %>"
82
- >
83
- <%= Spree.t("admin.external_integrations.sync_result") %> - Session #<%= sess.id %>
84
- </h5>
85
-
86
- <button
87
- type="button"
88
- class="close"
89
- data-dismiss="modal"
90
- aria-label="Close"
91
- >
92
- <span aria-hidden="true">&times;</span>
93
- </button>
94
- </div>
95
-
96
- <div class="modal-body">
97
- <pre
98
- class="small mb-0 p-2 bg-light border rounded"
99
- style="max-height: 500px; overflow-y: auto;"
100
- ><code><%= JSON.pretty_generate(sess.sync_result) %></code></pre>
101
- </div>
102
-
103
- <div class="modal-footer">
104
- <button
105
- type="button"
106
- class="btn btn-secondary"
107
- data-dismiss="modal"
108
- >
109
- <%= Spree.t(:close) %>
110
- </button>
111
- </div>
112
- </div>
113
- </div>
114
- </div>
115
- <% end %>
116
- <% end %>
@@ -1,42 +0,0 @@
1
- <% content_for :page_title do %>
2
- <%= link_to Spree.t('external_integrations'), spree.admin_integrations_url %> / <%= @integration.name %>
3
- <% end %>
4
-
5
- <% content_for :page_tabs do %>
6
- <li class="nav-item">
7
- <%= link_to Spree.t(:edit),
8
- edit_admin_integration_path(@integration),
9
- class: "nav-link" %>
10
- </li>
11
-
12
- <li class="nav-item">
13
- <%= link_to Spree.t(:integration_sync_sessions),
14
- admin_integration_sessions_path(@integration),
15
- class: "nav-link active" %>
16
- </li>
17
-
18
- <li class="nav-item">
19
- <%= link_to Spree.t(:integration_mappings),
20
- admin_integration_mappings_path(@integration),
21
- class: "nav-link" %>
22
- </li>
23
- <% end %>
24
-
25
- <div data-hook="admin_integrations_sessions" class="card mb-3">
26
- <div class="card-header d-flex justify-content-between align-items-center">
27
- <h5 class="card-title mb-0 h6">
28
- <%= Spree.t(:integration_sync_sessions) %>
29
- </h5>
30
-
31
- <div class="card-header-actions">
32
- <%= button_to Spree.t(:enqueue_full_sync),
33
- enqueue_polling_admin_integration_path(@integration, sync_type: :full),
34
- method: :post,
35
- class: 'btn btn-primary btn-sm' %>
36
- </div>
37
- </div>
38
-
39
- <div class="card-body">
40
- <%= render partial: 'integration_sync_sessions', locals: { integration: @integration } %>
41
- </div>
42
- </div>
@@ -1,104 +0,0 @@
1
- <div data-hook="admin_external_integrations_form_fields" class="row">
2
- <div class="col-6">
3
- <%= f.field_container :type do %>
4
- <%= f.label :type, raw(I18n.t('spree.admin.external_integrations.integration_type', default: 'Integration type') + required_span_tag) %>
5
-
6
- <% if f.object.persisted? %>
7
- <%= f.text_field :type, value: f.object.type, class: 'form-control', disabled: true, readonly: true %>
8
- <%= f.hidden_field :type, value: f.object.type %>
9
- <% else %>
10
- <%= f.select :type,
11
- options_for_select(@integration_type_options, f.object.type),
12
- { include_blank: Spree.t(:choose_an_option) },
13
- class: 'form-control select2',
14
- required: true %>
15
- <% end %>
16
-
17
- <%= f.error_message_on :type %>
18
- <% end %>
19
- </div>
20
-
21
- <div class="col-6">
22
- <%= f.field_container :name do %>
23
- <%= f.label :name, raw(Spree.t(:name) + required_span_tag) %>
24
- <%= f.text_field :name, required: true, class: 'form-control', placeholder: Spree.t(:name) %>
25
- <%= f.error_message_on :name %>
26
- <% end %>
27
- </div>
28
-
29
- <div class="col-6">
30
- <%= f.field_container :vendor_id do %>
31
- <%= f.label :vendor_id, raw(I18n.t('spree.admin.external_integrations.vendor', default: 'Vendor') + required_span_tag) %>
32
-
33
- <%= f.select :vendor_id,
34
- options_from_collection_for_select(@vendors, :id, :name, f.object.vendor_id),
35
- { include_blank: Spree.t(:choose_an_option) },
36
- class: 'form-control select2',
37
- required: true %>
38
-
39
- <%= f.error_message_on :vendor_id %>
40
- <% end %>
41
- </div>
42
-
43
- <div class="col-6">
44
- <%= f.field_container :tenant_id do %>
45
- <%= f.label :tenant_id, I18n.t('spree.admin.external_integrations.tenant', default: 'Tenant') %>
46
-
47
- <%= f.select :tenant_id,
48
- options_from_collection_for_select(@tenants, :id, :name, f.object.tenant_id),
49
- { include_blank: Spree.t(:choose_an_option), prompt: Spree.t(:optional) },
50
- class: 'form-control select2',
51
- data: { allow_clear: true, placeholder: Spree.t(:optional) } %>
52
-
53
- <%= f.error_message_on :tenant_id %>
54
- <% end %>
55
- </div>
56
-
57
- <div class="col-6">
58
- <%= f.field_container :status do %>
59
- <%= f.label :status, raw(Spree.t(:status) + required_span_tag) %>
60
-
61
- <%= f.select :status,
62
- options_for_select(@status_options, f.object.status),
63
- {},
64
- class: 'form-control select2',
65
- required: true %>
66
-
67
- <%= f.error_message_on :status %>
68
- <% end %>
69
- </div>
70
-
71
- <div class="col-6">
72
- <%= f.field_container :conflict_strategy do %>
73
- <%= f.label :conflict_strategy, raw(I18n.t('spree.admin.external_integrations.conflict_strategy', default: 'Conflict strategy') + required_span_tag) %>
74
-
75
- <%= f.select :conflict_strategy,
76
- options_for_select(@conflict_strategy_options, f.object.conflict_strategy),
77
- {},
78
- class: 'form-control select2',
79
- required: true %>
80
-
81
- <%= f.error_message_on :conflict_strategy %>
82
- <% end %>
83
- </div>
84
-
85
- <div class="col-6">
86
- <%= f.field_container :incremental_sync_interval_seconds do %>
87
- <%= f.label :incremental_sync_interval_seconds, raw(I18n.t('spree.admin.external_integrations.incremental_sync_interval_seconds', default: 'Incremental sync interval (seconds)') + required_span_tag) %>
88
- <%= f.number_field :incremental_sync_interval_seconds, class: 'form-control', min: 1, max: 3600, required: true %>
89
- <%= f.error_message_on :incremental_sync_interval_seconds %>
90
- <% end %>
91
- </div>
92
-
93
- <div class="col-6">
94
- <%= f.field_container :full_sync_interval_hours do %>
95
- <%= f.label :full_sync_interval_hours, raw(I18n.t('spree.admin.external_integrations.full_sync_interval_hours', default: 'Full sync interval (hours)') + required_span_tag) %>
96
- <%= f.number_field :full_sync_interval_hours, class: 'form-control', min: 1, max: 168, required: true %>
97
- <%= f.error_message_on :full_sync_interval_hours %>
98
- <% end %>
99
- </div>
100
-
101
- <% if f.object.type == 'SpreeCmCommissioner::Integrations::StadiumXV1' || (f.object.new_record? && params[:type] == 'SpreeCmCommissioner::Integrations::StadiumXV1') %>
102
- <%= render partial: 'stadium_x_v1_fields', locals: { f: f } %>
103
- <% end %>
104
- </div>
@@ -1,29 +0,0 @@
1
- <!-- Stadium X V1 API Credentials -->
2
- <div class="col-12">
3
- <hr class="my-4">
4
-
5
- <h6 class="text-muted">
6
- <%= I18n.t('spree.admin.external_integrations.stadium_x_credentials', default: 'Stadium X API Credentials') %>
7
- </h6>
8
- </div>
9
-
10
- <div class="col-6">
11
- <%= f.field_container :public_key do %>
12
- <%= f.label :public_key, raw(I18n.t('spree.admin.external_integrations.public_key', default: 'Public Key') + required_span_tag) %>
13
- <%= text_field_tag 'spree_cm_commissioner_integration[private_metadata][public_key]', f.object.public_key, class: 'form-control', required: true %>
14
- <% end %>
15
- </div>
16
-
17
- <div class="col-6">
18
- <%= f.field_container :private_key do %>
19
- <%= f.label :private_key, raw(I18n.t('spree.admin.external_integrations.private_key', default: 'Private Key') + required_span_tag) %>
20
- <%= text_field_tag 'spree_cm_commissioner_integration[private_metadata][private_key]', f.object.private_key, class: 'form-control', required: true %>
21
- <% end %>
22
- </div>
23
-
24
- <div class="col-6">
25
- <%= f.field_container :base_url do %>
26
- <%= f.label :base_url, raw(I18n.t('spree.admin.external_integrations.base_url', default: 'Base URL') + required_span_tag) %>
27
- <%= text_field_tag 'spree_cm_commissioner_integration[private_metadata][base_url]', f.object.base_url, class: 'form-control', placeholder: 'https://api.stadiumx.com', required: true %>
28
- <% end %>
29
- </div>
@@ -1,45 +0,0 @@
1
- <% content_for :page_title do %>
2
- <%= link_to Spree.t('external_integrations'), spree.admin_integrations_url %> / <%= @object.name %>
3
- <% end %>
4
-
5
- <% content_for :page_tabs do %>
6
- <li class="nav-item">
7
- <%= link_to Spree.t(:edit),
8
- edit_admin_integration_path(@object),
9
- class: "nav-link active" %>
10
- </li>
11
-
12
- <li class="nav-item">
13
- <%= link_to Spree.t(:integration_sync_sessions),
14
- admin_integration_sessions_path(@object),
15
- class: "nav-link" %>
16
- </li>
17
-
18
- <li class="nav-item">
19
- <%= link_to Spree.t(:integration_mappings),
20
- admin_integration_mappings_path(@object),
21
- class: "nav-link" %>
22
- </li>
23
- <% end %>
24
-
25
- <div data-hook="admin_integrations" class="card mb-3">
26
- <div class="card-header">
27
- <h5 class="card-title mb-0 h6"><%= Spree.t('external_integrations') %></h5>
28
- </div>
29
-
30
- <div class="card-body">
31
- <div data-hook="admin_integrations_edit_form_header">
32
- <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @object } %>
33
- </div>
34
-
35
- <div data-hook="admin_integrations_edit_form">
36
- <%= form_with model: @object, url: admin_integration_path(@object), method: :patch, scope: :spree_cm_commissioner_integration do |f| %>
37
- <%= render partial: 'form', locals: { f: f } %>
38
-
39
- <div data-hook="admin_integrations_edit_form_buttons">
40
- <%= render partial: 'spree/admin/shared/edit_resource_links', locals: { collection_url: spree.admin_integrations_path } %>
41
- </div>
42
- <% end %>
43
- </div>
44
- </div>
45
- </div>
@@ -1,75 +0,0 @@
1
- <% content_for :page_title do %>
2
- <%= Spree.t('external_integrations') %>
3
- <% end %>
4
-
5
- <% content_for :page_actions do %>
6
- <%= button_link_to Spree.t(:new_external_integration), new_admin_integration_path, class: "btn-success", icon: 'add.svg' %>
7
- <% end if can? :create, SpreeCmCommissioner::Integration %>
8
-
9
- <% if @integrations.any? %>
10
- <div class="table-responsive border rounded bg-white mb-3">
11
- <table class="table" id="listing_integrations" data-hook>
12
- <thead class="text-muted">
13
- <tr data-hook="admin_integrations_index_headers">
14
- <th><%= Spree.t(:name) %></th>
15
- <th><%= Spree.t(:base_url) %></th>
16
- <th><%= Spree.t(:conflict_strategy) %></th>
17
- <th><%= Spree.t(:incremental_sync_interval_seconds) %></th>
18
- <th><%= Spree.t(:full_sync_interval_hours) %></th>
19
- <th><%= Spree.t(:vendor) %></th>
20
- <th><%= Spree.t(:status) %></th>
21
- <th><%= Spree.t(:updated_at) %></th>
22
- </tr>
23
- </thead>
24
-
25
- <tbody>
26
- <% @integrations.each do |integration| %>
27
- <tr
28
- id="<%= spree_dom_id integration %>"
29
- data-hook="admin_integrations_index_rows"
30
- >
31
- <td><%= integration.name %></td>
32
-
33
- <td>
34
- <%= integration_base_url_link(integration.base_url) %>
35
- </td>
36
-
37
- <td>
38
- <%= integration_conflict_strategy_badge(integration.conflict_strategy) %>
39
- </td>
40
-
41
- <td>
42
- <%= integration_interval_badge(integration.incremental_sync_interval_seconds, 's') %>
43
- </td>
44
-
45
- <td>
46
- <%= integration_interval_badge(integration.full_sync_interval_hours, 'h') %>
47
- </td>
48
-
49
- <td><%= integration.vendor.name %></td>
50
-
51
- <td>
52
- <%= integration_status_badge(integration.status) %>
53
- </td>
54
-
55
- <td><%= integration.updated_at %></td>
56
-
57
- <td
58
- data-hook="admin_integrations_index_row_actions"
59
- class="actions"
60
- >
61
- <span class="d-flex justify-content-end">
62
- <%= link_to_edit integration, url: edit_admin_integration_path(integration), no_text: true if can?(:edit, integration) %>
63
- <%= link_to_delete integration, url: admin_integration_path(integration), no_text: true if can?(:delete, integration) %>
64
- </span>
65
- </td>
66
- </tr>
67
- <% end %>
68
- </tbody>
69
- </table>
70
- </div>
71
- <% else %>
72
- <small class="form-text text-muted">
73
- <%= raw I18n.t('external_integrations.empty_info') %>
74
- </small>
75
- <% end %>
@@ -1,25 +0,0 @@
1
- <% content_for :page_title do %>
2
- <%= link_to Spree.t('external_integrations'), spree.admin_integrations_url %> / <%= Spree.t(:new) %>
3
- <% end %>
4
-
5
- <div data-hook="admin_integrations" class="card mb-3">
6
- <div class="card-header">
7
- <h5 class="card-title mb-0 h6"><%= Spree.t('external_integrations') %></h5>
8
- </div>
9
-
10
- <div class="card-body">
11
- <div data-hook="admin_integrations_new_form_header">
12
- <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @object } %>
13
- </div>
14
-
15
- <div data-hook="admin_integrations_new_form">
16
- <%= form_with model: @object, url: { action: 'create' }, scope: :spree_cm_commissioner_integration do |f| %>
17
- <%= render partial: 'form', locals: { f: f } %>
18
-
19
- <div data-hook="admin_integrations_new_form_buttons">
20
- <%= render partial: 'spree/admin/shared/new_resource_links' %>
21
- </div>
22
- <% end %>
23
- </div>
24
- </div>
25
- </div>
@@ -1,22 +0,0 @@
1
- class CreateCmIntegrations < ActiveRecord::Migration[7.0]
2
- def change
3
- create_table :cm_integrations, if_not_exists: true do |t|
4
- t.string :type, null: false
5
- t.string :name, null: false
6
-
7
- t.integer :status, null: false, default: 0
8
- t.integer :conflict_strategy, null: false, default: 0
9
-
10
- t.integer :incremental_sync_interval_seconds, null: false, default: 10
11
- t.integer :full_sync_interval_hours, null: false, default: 24
12
-
13
- t.references :tenant, foreign_key: { to_table: :cm_tenants }, null: true
14
- t.references :vendor, foreign_key: { to_table: :spree_vendors }, null: false
15
-
16
- t.jsonb :public_metadata, default: {}
17
- t.jsonb :private_metadata, default: {}
18
-
19
- t.timestamps
20
- end
21
- end
22
- end
@@ -1,68 +0,0 @@
1
- class CreateCmIntegrationSyncSessions < ActiveRecord::Migration[7.0]
2
- def change
3
- create_table :cm_integration_sync_sessions, if_not_exists: true do |t|
4
- t.integer :status, null: false
5
- t.integer :sync_type, null: false
6
-
7
- t.datetime :started_at
8
- t.datetime :ended_at
9
-
10
- t.references :integration, foreign_key: { to_table: :cm_integrations }, null: false
11
- t.references :tenant, foreign_key: { to_table: :cm_tenants }, null: true
12
-
13
- # Stores the serialized sync result (SyncResult#to_h) for this session.
14
- # This field is used to persist the outcome of the sync operation,
15
- # allowing for detailed tracking of results, whether the sync was successful or not.
16
- #
17
- # Base class: app/services/spree_cm_commissioner/integrations/base/sync_result.rb
18
- #
19
- # Example implementations include:
20
- # - Full sync results: integrations/stadium_x_v1/sync_results/full_sync_result.rb
21
- # - Incremental sync results: integrations/stadium_x_v1/sync_results/incremental_sync_result.rb
22
- #
23
- # The result is saved upon completion of the session in the SyncManager#run_execution method:
24
- # - app/services/spree_cm_commissioner/integrations/base/sync_manager.rb
25
- t.jsonb :sync_result, default: {}
26
-
27
- t.timestamps
28
- end
29
-
30
- # Indexes for PollingScheduler queries (optimized for performance)
31
- # Initially used in polling_scheduler.rb
32
- #
33
- # Index 1: Find in_progress/pending syncs created within last hour
34
- # Used by: should_run_full_sync? and should_run_incremental_sync?
35
- # Query: .where(sync_type: [...], status: [...]).where('created_at > ?', 1.hour.ago).exists?
36
- # Why: Composite index on all filter columns for fast lookup of active syncs
37
- unless index_exists?(:cm_integration_sync_sessions, [:integration_id, :sync_type, :status, :created_at], name: 'idx_sync_sessions_integration_type_status_created_at')
38
- add_index :cm_integration_sync_sessions, [:integration_id, :sync_type, :status, :created_at],
39
- name: 'idx_sync_sessions_integration_type_status_created_at'
40
- end
41
-
42
- # Index 2: Find latest completed full sync (partial index - only completed rows)
43
- # Used by: should_run_full_sync?
44
- # Query: .where(sync_type: :full, status: :completed).order(created_at: :desc).first
45
- # Why: Partial index reduces size by ~60% (excludes in_progress/pending/failed rows)
46
- # Descending order on created_at allows DB to return latest without sorting
47
- # Only 2 columns needed since sync_type and status are filtered by WHERE clause
48
- unless index_exists?(:cm_integration_sync_sessions, [:integration_id, :created_at], name: 'idx_sync_sessions_full_completed')
49
- add_index :cm_integration_sync_sessions, [:integration_id, :created_at],
50
- name: 'idx_sync_sessions_full_completed',
51
- where: "sync_type = 0 AND status = 2", # full=0, completed=2
52
- order: { created_at: :desc }
53
- end
54
-
55
- # Index 3: Find latest completed incremental sync (partial index - only completed rows)
56
- # Used by: should_run_incremental_sync?
57
- # Query: .where(sync_type: :incremental, status: :completed).order(created_at: :desc).first
58
- # Why: Partial index reduces size by ~60% (excludes in_progress/pending/failed rows)
59
- # Descending order on created_at allows DB to return latest without sorting
60
- # Only 2 columns needed since sync_type and status are filtered by WHERE clause
61
- unless index_exists?(:cm_integration_sync_sessions, [:integration_id, :created_at], name: 'idx_sync_sessions_incremental_completed')
62
- add_index :cm_integration_sync_sessions, [:integration_id, :created_at],
63
- name: 'idx_sync_sessions_incremental_completed',
64
- where: "sync_type = 1 AND status = 2", # incremental=1, completed=2
65
- order: { created_at: :desc }
66
- end
67
- end
68
- end
@@ -1,52 +0,0 @@
1
- class CreateCmIntegrationMappings < ActiveRecord::Migration[7.0]
2
- def change
3
- create_table :cm_integration_mappings, if_not_exists: true do |t|
4
- t.string :external_id, null: false
5
- t.string :internal_type, null: false
6
- t.bigint :internal_id, null: false
7
- t.jsonb :external_payload, default: nil
8
-
9
- # optional for record that can have different mappings on different dates
10
- t.date :date, null: true
11
- t.integer :status, null: false, default: 0
12
- t.references :integration, null: false, foreign_key: { to_table: :cm_integrations }
13
-
14
- t.jsonb :public_metadata, default: {}
15
- t.jsonb :private_metadata, default: {}
16
-
17
- t.datetime :last_synced_at
18
- t.timestamps
19
- end
20
-
21
- # Index 1: Unique constraint for external_id mappings
22
- # Used by: IntegrationMapping#validates :external_id
23
- # Query: validates :external_id, uniqueness: { scope: %i[integration_id internal_type internal_id date] }
24
- # Why: Ensures a single external_id maps to only one internal record per integration/date combination
25
- # Prevents duplicate mappings that could cause sync conflicts
26
- # Unique index enforces this at database level for data integrity
27
- unless index_exists?(:cm_integration_mappings, [:integration_id, :external_id, :internal_type, :internal_id, :date], name: 'idx_external_internal_mapping')
28
- add_index :cm_integration_mappings, [:integration_id, :external_id, :internal_type, :internal_id, :date], unique: true, name: 'idx_external_internal_mapping'
29
- end
30
-
31
- # Index 2: Efficient lookup by integration_id and external_id
32
- # Used by: IntegrationMappable#find_by_integration, IntegrationMappable#find_or_initialize_by_integration,
33
- # IntegrationMappable#find_or_initialize_by_integration_with_mapping
34
- #
35
- # Query: .find_by(integration_id: integration_id, external_id: external_id)
36
- # Why: Composite index allows efficient lookup by both integration_id and external_id
37
- # These methods are called frequently during sync operations
38
- unless index_exists?(:cm_integration_mappings, [:integration_id, :internal_type, :external_id], name: 'idx_integration_external_id')
39
- add_index :cm_integration_mappings, [:integration_id, :internal_type, :external_id], name: 'idx_integration_external_id'
40
- end
41
-
42
- # Index 3: Incremental sync query optimization
43
- # Used by: IntegrationMappable#find_oldest_active_mapping (called by IncrementalSyncStrategy every 10 seconds)
44
- # Query: .where(integration_id: integration_id, internal_type: model_name).active.order(:last_synced_at).first
45
- # Why: Composite index filters by integration_id, internal_type, status efficiently
46
- # Includes last_synced_at for ORDER BY to avoid expensive sort operation
47
- # Runs every 10s so performance is critical - even small optimizations matter at this frequency
48
- unless index_exists?(:cm_integration_mappings, [:integration_id, :internal_type, :status, :last_synced_at], name: 'idx_incremental_sync')
49
- add_index :cm_integration_mappings, [:integration_id, :internal_type, :status, :last_synced_at], name: 'idx_incremental_sync'
50
- end
51
- end
52
- end
@@ -1,25 +0,0 @@
1
- FactoryBot.define do
2
- factory :cm_integration, class: SpreeCmCommissioner::Integration do
3
- type { 'SpreeCmCommissioner::Integrations::StadiumXV1' }
4
- name { FFaker::Company.name }
5
- status { :active }
6
- conflict_strategy { :newest_wins }
7
- incremental_sync_interval_seconds { 300 }
8
- full_sync_interval_hours { 24 }
9
- association :vendor, factory: :cm_vendor
10
-
11
- trait :active do
12
- status { :active }
13
- end
14
-
15
- trait :inactive do
16
- status { :inactive }
17
- end
18
- end
19
-
20
- factory :cm_stadium_x_v1_integration, parent: :cm_integration, class: SpreeCmCommissioner::Integrations::StadiumXV1 do
21
- public_key { FFaker::Lorem.characters(20) }
22
- private_key { FFaker::Lorem.characters(40) }
23
- base_url { 'https://api.stadiumx.com' }
24
- end
25
- end
@@ -1,14 +0,0 @@
1
- FactoryBot.define do
2
- factory :cm_integration_mapping, class: SpreeCmCommissioner::IntegrationMapping do
3
- external_id { SecureRandom.alphanumeric(10) }
4
- association :integration, factory: :cm_integration
5
- status { :active }
6
-
7
- transient do
8
- internal { create(:cm_product) }
9
- end
10
-
11
- internal_id { internal.id }
12
- internal_type { internal.class.name }
13
- end
14
- end
@@ -1,7 +0,0 @@
1
- FactoryBot.define do
2
- factory :cm_integration_sync_session, class: SpreeCmCommissioner::IntegrationSyncSession do
3
- status { :pending }
4
- sync_type { :full }
5
- association :integration, factory: :cm_integration
6
- end
7
- end