spree_cm_commissioner 1.11.0.pre.pre4 → 1.13.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/test_and_build_gem.yml +143 -12
- data/.gitignore +1 -2
- data/Gemfile.lock +1 -22
- data/Rakefile +4 -33
- data/app/controllers/spree/admin/cms_pages_controller_decorator.rb +32 -0
- data/app/controllers/spree/admin/product_places_controller.rb +2 -0
- data/app/controllers/spree/admin/stock_managements_controller.rb +1 -62
- data/app/controllers/spree/api/v2/organizer/invite_guests_controller.rb +70 -0
- data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +31 -14
- data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
- data/app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb +1 -0
- data/app/controllers/spree/api/v2/tenant/account_checker_controller.rb +1 -1
- data/app/controllers/spree/api/v2/tenant/account_recovers_controller.rb +2 -2
- data/app/controllers/spree/api/v2/tenant/cms_pages_controller.rb +41 -0
- data/app/controllers/spree/api/v2/tenant/reset_passwords_controller.rb +1 -1
- data/app/controllers/spree/api/v2/tenant/waiting_room_sessions_controller.rb +30 -0
- data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +17 -0
- data/app/controllers/spree_cm_commissioner/api/v2/storefront/cms_pages_controller_decorator.rb +18 -0
- data/app/interactors/spree_cm_commissioner/account_recover.rb +2 -2
- data/app/interactors/spree_cm_commissioner/create_event.rb +7 -26
- data/app/interactors/spree_cm_commissioner/create_ticket.rb +95 -0
- data/app/interactors/spree_cm_commissioner/event_line_items_date_syncer.rb +19 -0
- data/app/interactors/spree_cm_commissioner/existing_account_checker.rb +1 -1
- data/app/interactors/spree_cm_commissioner/order_importer/multi_guest.rb +31 -0
- data/app/interactors/spree_cm_commissioner/organizers_transactional_email_notifier.rb +27 -0
- data/app/interactors/spree_cm_commissioner/pin_code_sender.rb +3 -3
- data/app/interactors/spree_cm_commissioner/product_event_id_to_children_syncer.rb +15 -0
- data/app/interactors/spree_cm_commissioner/sms.rb +14 -0
- data/app/interactors/spree_cm_commissioner/telegram_debug_pin_code_sender.rb +2 -1
- data/app/interactors/spree_cm_commissioner/transactional_email_sender.rb +50 -0
- data/app/interactors/spree_cm_commissioner/user_forgotten_password_updater.rb +2 -1
- data/app/interactors/spree_cm_commissioner/user_password_authenticator.rb +4 -2
- data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +7 -14
- data/app/interactors/spree_cm_commissioner/waiting_room_session_firebase_logger.rb +30 -0
- data/app/jobs/spree_cm_commissioner/ensure_event_for_product_line_item_guests_job.rb +13 -0
- data/app/jobs/spree_cm_commissioner/event_line_items_date_syncer_job.rb +8 -0
- data/app/jobs/spree_cm_commissioner/product_event_id_to_children_syncer_job.rb +8 -0
- data/app/jobs/spree_cm_commissioner/telegram_debug_pin_code_sender_job.rb +3 -1
- data/app/jobs/spree_cm_commissioner/waiting_room_session_firebase_logger_job.rb +13 -0
- data/app/mailers/spree_cm_commissioner/event_transactional_mailer.rb +23 -0
- data/app/mailers/spree_cm_commissioner/pin_code_mailer.rb +1 -0
- data/app/models/concerns/spree_cm_commissioner/kyc_bitwise.rb +2 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +10 -6
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +13 -26
- data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +4 -3
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +0 -10
- data/app/models/concerns/spree_cm_commissioner/user_identity.rb +7 -4
- data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +8 -10
- data/app/models/spree_cm_commissioner/cms_page_decorator.rb +9 -0
- data/app/models/spree_cm_commissioner/event_ticket_google_wallet.rb +2 -2
- data/app/models/spree_cm_commissioner/guest.rb +1 -1
- data/app/models/spree_cm_commissioner/invite_guest.rb +23 -0
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +10 -16
- data/app/models/spree_cm_commissioner/option_type_decorator.rb +6 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +0 -15
- data/app/models/spree_cm_commissioner/place.rb +1 -1
- data/app/models/spree_cm_commissioner/product_decorator.rb +17 -14
- data/app/models/spree_cm_commissioner/product_place.rb +1 -0
- data/app/models/spree_cm_commissioner/role_decorator.rb +3 -0
- data/app/models/spree_cm_commissioner/stock/availability_checker.rb +25 -27
- data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +1 -2
- data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
- data/app/models/spree_cm_commissioner/stock_item_decorator.rb +0 -14
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +26 -3
- data/app/models/spree_cm_commissioner/transactional_email.rb +6 -0
- data/app/models/spree_cm_commissioner/user_decorator.rb +6 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +27 -34
- data/app/models/spree_cm_commissioner/vehicle.rb +0 -7
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +9 -1
- data/app/overrides/spree/admin/cms_pages/_form/tenant_fields.html.erb.deface +9 -0
- data/app/overrides/spree/admin/cms_pages/index/cms_pages_tabs.html.erb.deface +21 -0
- data/app/overrides/spree/admin/taxons/_form/assets_form.html.erb.deface +2 -2
- data/app/overrides/spree/admin/taxons/_form/available_on.html.erb.deface +3 -1
- data/app/overrides/spree/admin/taxons/_form/background_color_and_foreground_color.html.erb.deface +3 -1
- data/app/overrides/spree/admin/taxons/_form/custom_redirect_url.html.erb.deface +3 -1
- data/app/overrides/spree/admin/taxons/_form/hide_video_banner.html.erb.deface +3 -1
- data/app/overrides/spree/admin/taxons/_form/purchasable_on_status.html.erb.deface +3 -1
- data/app/overrides/spree/admin/taxons/_form/show_badge_status.html.erb.deface +3 -1
- data/app/overrides/spree/admin/taxons/_form/to_date_form_date.html.erb.deface +3 -4
- data/app/overrides/spree/admin/users/_form/roles_fields.html.erb.deface +1 -1
- data/app/overrides/spree/admin/users/index/body.html.erb.deface +3 -0
- data/app/overrides/spree/admin/users/index/headers.html.erb.deface +3 -0
- data/app/overrides/spree/admin/variants/_form/kyc_field.html.erb.deface +40 -0
- data/app/overrides/spree/admin/variants/edit/variant_status.html.erb.deface +6 -3
- data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +45 -0
- data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +55 -0
- data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +0 -3
- data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
- data/app/serializers/spree/v2/organizer/invite_guest_serializer.rb +13 -0
- data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +0 -2
- data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +2 -0
- data/app/serializers/spree/v2/tenant/waiting_room_session_serializer.rb +9 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +2 -1
- data/app/services/spree_cm_commissioner/user_authenticator.rb +3 -1
- data/app/services/spree_cm_commissioner/user_roles_assigner.rb +62 -0
- data/app/views/spree/admin/shared/_cms_pages_tabs.html.erb +20 -0
- data/app/views/spree/admin/shared/_taxon_tabs.html.erb +19 -19
- data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +1 -3
- data/app/views/spree/admin/stock_managements/index.html.erb +5 -40
- data/app/views/spree/shared/_base_mailer_header.html.erb +10 -2
- data/app/views/spree_cm_commissioner/event_transactional_mailer/_event_banner.html.erb +3 -0
- data/app/views/spree_cm_commissioner/event_transactional_mailer/_mailer_stylesheets.html.erb +317 -0
- data/app/views/spree_cm_commissioner/event_transactional_mailer/_note.html.erb +8 -0
- data/app/views/spree_cm_commissioner/event_transactional_mailer/_share_button.html.erb +3 -0
- data/app/views/spree_cm_commissioner/event_transactional_mailer/send_to_organizer.html.erb +59 -0
- data/app/views/spree_cm_commissioner/event_transactional_mailer/send_to_participant.html.erb +52 -0
- data/app/views/spree_cm_commissioner/layouts/event_transactional_mailer.html.erb +14 -0
- data/config/initializers/spree_permitted_attributes.rb +0 -5
- data/config/locales/en.yml +36 -0
- data/config/locales/km.yml +2 -0
- data/config/routes.rb +6 -11
- data/db/migrate/20250422014417_create_spree_cm_commissioner_invite_guest.rb +21 -0
- data/db/migrate/20250509033437_create_spree_cm_commissioner_transactional_emails.rb +12 -0
- data/db/migrate/20250509075429_add_max_order_quantity_to_spree_product.rb +5 -0
- data/db/migrate/20250512075319_add_tenant_id_to_spree_cms_pages.rb +6 -0
- data/db/migrate/20250520042602_add_event_to_spree_products.rb +7 -0
- data/db/migrate/20250520044533_add_event_to_spree_line_items.rb +7 -0
- data/db/migrate/20250521024345_add_tenant_id_to_cm_waiting_room_sessions.rb +6 -0
- data/db/migrate/20250521095539_add_kyc_to_spree_variants.rb +5 -0
- data/docker-compose.yml +1 -1
- data/lib/generators/spree_cm_commissioner/install/install_generator.rb +3 -11
- data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_dashboard/spree_cm_commissioner → spree_cm_commissioner}/utilities.js +0 -4
- data/lib/spree_cm_commissioner/calendar_event.rb +1 -11
- data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
- data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
- data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +5 -18
- data/lib/spree_cm_commissioner/test_helper/factories/role.rb +7 -0
- data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
- data/lib/spree_cm_commissioner/test_helper/factories/taxon_home_banner_factory.rb +5 -0
- data/lib/spree_cm_commissioner/test_helper/factories/transactional_email_factory.rb +6 -0
- data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +6 -41
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +1 -1
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +0 -34
- data/lib/tasks/ensure_event_for_product_line_item_guests.rake +7 -0
- data/lib/tasks/update_invalid_self_root_places.rake +9 -0
- data/lib/tasks/update_orphan_root_places.rake +1 -0
- data/spree_cm_commissioner.gemspec +0 -5
- metadata +54 -82
- data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +0 -42
- data/app/finders/spree_cm_commissioner/accommodations/find.rb +0 -40
- data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +0 -35
- data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +0 -40
- data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +0 -25
- data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +0 -13
- data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +0 -15
- data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +0 -75
- data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +0 -24
- data/app/interactors/spree_cm_commissioner/user_roles_assigner.rb +0 -22
- data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +0 -7
- data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +0 -7
- data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +0 -11
- data/app/jobs/spree_cm_commissioner/stock/inventory_items_generator_job.rb +0 -11
- data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +0 -9
- data/app/models/spree_cm_commissioner/inventory.rb +0 -11
- data/app/models/spree_cm_commissioner/inventory_item.rb +0 -56
- data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +0 -40
- data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +0 -126
- data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +0 -36
- data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +0 -27
- data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +0 -44
- data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +0 -19
- data/app/views/spree/admin/stock_managements/_events_popover.html.erb +0 -23
- data/app/views/spree/admin/stock_managements/calendar.html.erb +0 -35
- data/db/migrate/20250304293518_create_cm_inventory_items.rb +0 -21
- data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +0 -5
- data/db/migrate/20250502025848_add_index_to_spree_products.rb +0 -5
- data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +0 -5
- data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +0 -5
- data/lib/spree_cm_commissioner/cached_inventory_item.rb +0 -23
- data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +0 -9
- data/lib/tasks/create_default_non_permanent_inventory_items.rake +0 -16
- data/lib/tasks/ensure_correct_product_type.rake +0 -7
- data/lib/tasks/generate_inventory_items.rake +0 -7
@@ -1,35 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module Accommodations
|
3
|
-
class FindVariant
|
4
|
-
attr_reader :vendor_id, :from_date, :to_date, :number_of_guests
|
5
|
-
|
6
|
-
def initialize(vendor_id:, from_date:, to_date:, number_of_adults:, number_of_kids:)
|
7
|
-
@vendor_id = vendor_id
|
8
|
-
@from_date = from_date
|
9
|
-
@to_date = to_date
|
10
|
-
@number_of_guests = number_of_adults.to_i + number_of_kids.to_i
|
11
|
-
end
|
12
|
-
|
13
|
-
def execute
|
14
|
-
Spree::Variant
|
15
|
-
.joins(:inventory_items)
|
16
|
-
.where(vendor_id: vendor_id)
|
17
|
-
.where(inventory_items: { inventory_date: date_range_excluding_checkout })
|
18
|
-
.where('CAST(public_metadata->\'cm_options\'->>\'number-of-adults\' AS INTEGER) +
|
19
|
-
CAST(public_metadata->\'cm_options\'->>\'number-of-kids\' AS INTEGER) >= ?', number_of_guests
|
20
|
-
)
|
21
|
-
.where('inventory_items.quantity_available > 0')
|
22
|
-
.distinct
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
# Why? check_out date is not considered to be charged
|
28
|
-
# For example, if you check in on 2023-10-01 and check out on 2023-10-02,
|
29
|
-
# you will be charged for one night (2023-10-01).
|
30
|
-
def date_range_excluding_checkout
|
31
|
-
from_date..to_date.prev_day
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,40 +0,0 @@
|
|
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
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
class InventoryItemSyncer < BaseInteractor
|
3
|
-
# inventory_id_and_quantities = [{ inventory_id: inventory_item1.id, quantity: 5 } ]
|
4
|
-
delegate :inventory_id_and_quantities, to: :context
|
5
|
-
|
6
|
-
def call
|
7
|
-
ActiveRecord::Base.transaction do
|
8
|
-
inventory_items.each do |inventory_item|
|
9
|
-
quantity = inventory_id_and_quantities.find { |item| item[:inventory_id] == inventory_item.id }&.dig(:quantity) || 0
|
10
|
-
adjust_quantity_available(inventory_item, quantity)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def adjust_quantity_available(inventory_item, quantity)
|
18
|
-
inventory_item.update!(quantity_available: inventory_item.quantity_available + quantity)
|
19
|
-
end
|
20
|
-
|
21
|
-
def inventory_items
|
22
|
-
@inventory_items ||= InventoryItem.where(id: inventory_id_and_quantities.pluck(:inventory_id))
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,13 +0,0 @@
|
|
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
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module Stock
|
3
|
-
class InventoryItemsGenerator < BaseInteractor
|
4
|
-
delegate :variant, to: :context
|
5
|
-
|
6
|
-
def call
|
7
|
-
if variant.permanent_stock?
|
8
|
-
SpreeCmCommissioner::Stock::PermanentInventoryItemsGenerator.call(variant_ids: [variant.id])
|
9
|
-
else
|
10
|
-
variant.create_default_non_permanent_inventory_item! unless variant.default_inventory_item_exist?
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module Stock
|
3
|
-
class PermanentInventoryItemsGenerator < BaseInteractor
|
4
|
-
delegate :variant_ids, to: :context
|
5
|
-
|
6
|
-
def variants_per_batch = 1000
|
7
|
-
|
8
|
-
def pre_inventory_days_for(variant)
|
9
|
-
context.pre_inventory_days || variant.pre_inventory_days
|
10
|
-
end
|
11
|
-
|
12
|
-
def call
|
13
|
-
variants.in_batches(of: variants_per_batch) do |batch|
|
14
|
-
generate_inventory_items_for_batch(batch)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def generate_inventory_items_for_batch(batch)
|
21
|
-
total_on_hand_by_variant = total_on_hand_for(batch)
|
22
|
-
batch.each do |variant|
|
23
|
-
count_on_hand = total_on_hand_by_variant[variant.id] || 0
|
24
|
-
generate_inventory_items_for_variant(variant, count_on_hand)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def generate_inventory_items_for_variant(variant, count_on_hand)
|
29
|
-
inventory_dates_for(variant).each do |inventory_date|
|
30
|
-
next if inventory_exist?(variant, inventory_date)
|
31
|
-
|
32
|
-
create_inventory_item(variant, inventory_date, count_on_hand)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def inventory_dates_for(variant)
|
37
|
-
start_date = Time.zone.tomorrow
|
38
|
-
end_date = Time.zone.today + pre_inventory_days_for(variant)
|
39
|
-
|
40
|
-
(start_date..end_date)
|
41
|
-
end
|
42
|
-
|
43
|
-
def inventory_exist?(variant, inventory_date)
|
44
|
-
variant.inventory_items.exists?(inventory_date: inventory_date)
|
45
|
-
end
|
46
|
-
|
47
|
-
def create_inventory_item(variant, inventory_date, count_on_hand)
|
48
|
-
variant.inventory_items.create!(
|
49
|
-
inventory_date: inventory_date,
|
50
|
-
quantity_available: count_on_hand,
|
51
|
-
max_capacity: count_on_hand,
|
52
|
-
product_type: variant.product_type
|
53
|
-
)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Returns a hash: { variant_id => total_on_hand, ... }
|
57
|
-
def total_on_hand_for(variants)
|
58
|
-
variant_ids = variants.pluck(:id)
|
59
|
-
|
60
|
-
Spree::StockItem
|
61
|
-
.joins(:stock_location)
|
62
|
-
.where(deleted_at: nil, variant_id: variant_ids)
|
63
|
-
.where(spree_stock_locations: { active: true })
|
64
|
-
.group(:variant_id)
|
65
|
-
.sum(:count_on_hand)
|
66
|
-
end
|
67
|
-
|
68
|
-
def variants
|
69
|
-
scope = Spree::Variant.active.with_permanent_stock.where(is_master: false)
|
70
|
-
scope = scope.where(id: variant_ids) if variant_ids.present?
|
71
|
-
scope
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,24 +0,0 @@
|
|
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
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
class UserRolesAssigner < BaseInteractor
|
3
|
-
delegate :user_id, :role_ids, to: :context
|
4
|
-
|
5
|
-
def call
|
6
|
-
context.fail!(message: I18n.t('user_roles_assigner.user_not_found')) unless user
|
7
|
-
context.fail!(message: I18n.t('user_roles_assigner.roles_empty')) if role_ids.blank?
|
8
|
-
update_roles
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def user
|
14
|
-
@user ||= Spree::User.find_by(id: user_id)
|
15
|
-
end
|
16
|
-
|
17
|
-
def update_roles
|
18
|
-
user.role_users.where.not(role_id: role_ids).destroy_all
|
19
|
-
role_ids.each { |role_id| user.role_users.find_or_create_by(role_id: role_id) }
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,11 +0,0 @@
|
|
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
|
@@ -1,11 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module Stock
|
3
|
-
class InventoryItemsGeneratorJob < ApplicationUniqueJob
|
4
|
-
def perform(variant_id:)
|
5
|
-
variant = Spree::Variant.find(variant_id)
|
6
|
-
|
7
|
-
SpreeCmCommissioner::Stock::InventoryItemsGenerator.call(variant: variant)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
class Inventory
|
3
|
-
include ActiveModel::Model
|
4
|
-
|
5
|
-
attr_accessor :variant_id, :inventory_date, :quantity_available, :max_capacity, :product_type
|
6
|
-
|
7
|
-
validates :variant_id, presence: true
|
8
|
-
validates :quantity_available, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, allow_nil: true
|
9
|
-
validates :max_capacity, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, allow_nil: true
|
10
|
-
end
|
11
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
class InventoryItem < ApplicationRecord
|
3
|
-
include SpreeCmCommissioner::ProductType
|
4
|
-
|
5
|
-
# Association
|
6
|
-
belongs_to :variant, class_name: 'Spree::Variant'
|
7
|
-
|
8
|
-
# Validation
|
9
|
-
validates :quantity_available, numericality: { greater_than_or_equal_to: 0 }
|
10
|
-
validates :max_capacity, numericality: { greater_than_or_equal_to: 0 } # Originally inventory of each variant.
|
11
|
-
validates :inventory_date, presence: true, if: :permanent_stock?
|
12
|
-
validates :variant_id, uniqueness: { scope: :inventory_date, message: -> (object, _data) { "The variant is taken on #{object.inventory_date}" } }
|
13
|
-
|
14
|
-
# Scope
|
15
|
-
scope :active, -> { where(inventory_date: nil).or(where('inventory_date >= ?', Time.zone.today)) }
|
16
|
-
|
17
|
-
before_save -> { self.product_type = variant.product_type }, if: -> { product_type.nil? }
|
18
|
-
|
19
|
-
# This method is only used when admin update stock
|
20
|
-
def adjust_quantity!(quantity)
|
21
|
-
with_lock do
|
22
|
-
self.max_capacity = max_capacity + quantity
|
23
|
-
self.quantity_available = [0, quantity_available + quantity].max
|
24
|
-
save!
|
25
|
-
|
26
|
-
# When user has been searched or booked a product, it has cached the quantity in redis,
|
27
|
-
# So we need to update redis cache if inventory key has been created in redis
|
28
|
-
adjust_quantity_in_redis(quantity)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def adjust_quantity_in_redis(quantity)
|
33
|
-
SpreeCmCommissioner.redis_pool.with do |redis|
|
34
|
-
cached_quantity_available = redis.get("inventory:#{id}")
|
35
|
-
# ignore if redis doesn't exist
|
36
|
-
return if cached_quantity_available.nil? # rubocop:disable Lint/NonLocalExitFromIterator
|
37
|
-
|
38
|
-
if quantity.positive?
|
39
|
-
redis.incrby("inventory:#{id}", quantity)
|
40
|
-
else
|
41
|
-
redis.decrby("inventory:#{id}", quantity.abs)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def active?
|
47
|
-
inventory_date.nil? || inventory_date >= Time.zone.today
|
48
|
-
end
|
49
|
-
|
50
|
-
def redis_expired_in
|
51
|
-
expired_in = 31_536_000 # 1 year for normal stock
|
52
|
-
expired_in = Time.parse(inventory_date.to_s).end_of_day.to_i - Time.zone.now.to_i if inventory_date.present?
|
53
|
-
[expired_in, 0].max
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module RedisStock
|
3
|
-
class CachedInventoryItemsBuilder
|
4
|
-
attr_reader :inventory_items
|
5
|
-
|
6
|
-
def initialize(inventory_items)
|
7
|
-
@inventory_items = inventory_items
|
8
|
-
end
|
9
|
-
|
10
|
-
# output: [ CachedInventoryItem(...), CachedInventoryItem(...) ]
|
11
|
-
def call
|
12
|
-
keys = inventory_items.map { |item| "inventory:#{item.id}" }
|
13
|
-
return [] unless keys.any?
|
14
|
-
|
15
|
-
counts = SpreeCmCommissioner.redis_pool.with { |redis| redis.mget(*keys) }
|
16
|
-
inventory_items.map.with_index do |inventory_item, i|
|
17
|
-
::SpreeCmCommissioner::CachedInventoryItem.new(
|
18
|
-
inventory_key: keys[i],
|
19
|
-
active: inventory_item.active?,
|
20
|
-
quantity_available: cache_inventory(keys[i], inventory_item, counts[i]),
|
21
|
-
inventory_item_id: inventory_item.id,
|
22
|
-
variant_id: inventory_item.variant_id
|
23
|
-
)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def cache_inventory(key, inventory_item, count_in_redis)
|
30
|
-
return count_in_redis.to_i if count_in_redis.present?
|
31
|
-
|
32
|
-
SpreeCmCommissioner.redis_pool.with do |redis|
|
33
|
-
redis.set(key, inventory_item.quantity_available, ex: inventory_item.redis_expired_in)
|
34
|
-
end
|
35
|
-
|
36
|
-
inventory_item.quantity_available
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,126 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module RedisStock
|
3
|
-
class InventoryUpdater
|
4
|
-
class UnableToRestock < StandardError; end
|
5
|
-
class UnableToUnstock < StandardError; end
|
6
|
-
|
7
|
-
def initialize(line_item_ids)
|
8
|
-
@line_item_ids = line_item_ids
|
9
|
-
end
|
10
|
-
|
11
|
-
def unstock!
|
12
|
-
keys, quantities, inventory_ids = extract_inventory_data
|
13
|
-
|
14
|
-
raise UnableToUnstock, Spree.t(:insufficient_stock_lines_present) unless unstock(keys, quantities)
|
15
|
-
|
16
|
-
inventory_id_and_quantities = inventory_ids.map.with_index do |inventory_id, i|
|
17
|
-
{ inventory_id: inventory_id, quantity: -quantities[i] }
|
18
|
-
end
|
19
|
-
|
20
|
-
schedule_sync_inventory(inventory_id_and_quantities)
|
21
|
-
end
|
22
|
-
|
23
|
-
def restock!
|
24
|
-
keys, quantities, inventory_ids = extract_inventory_data
|
25
|
-
|
26
|
-
raise UnableToRestock unless restock(keys, quantities)
|
27
|
-
|
28
|
-
inventory_id_and_quantities = inventory_ids.map.with_index do |inventory_id, i|
|
29
|
-
{ inventory_id: inventory_id, quantity: quantities[i] }
|
30
|
-
end
|
31
|
-
|
32
|
-
schedule_sync_inventory(inventory_id_and_quantities)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
# only unstock or restock when variants is available, track inventory, not backorderable & not need_confirmation.
|
38
|
-
# otherwise, we can ignore them.
|
39
|
-
def line_items
|
40
|
-
return @line_items if defined?(@line_items)
|
41
|
-
|
42
|
-
@line_items = Spree::LineItem.where(id: @line_item_ids).includes(variant: :stock_items)
|
43
|
-
@line_items = @line_items.map do |line_item|
|
44
|
-
next unless line_item.variant.available?
|
45
|
-
next unless line_item.variant.should_track_inventory?
|
46
|
-
next if line_item.variant.backorderable?
|
47
|
-
next if line_item.variant.need_confirmation?
|
48
|
-
|
49
|
-
line_item
|
50
|
-
end.compact
|
51
|
-
end
|
52
|
-
|
53
|
-
def unstock(keys, quantities)
|
54
|
-
SpreeCmCommissioner.redis_pool.with do |redis|
|
55
|
-
redis.eval(unstock_redis_script, keys: keys, argv: quantities)
|
56
|
-
end.positive?
|
57
|
-
end
|
58
|
-
|
59
|
-
def restock(keys, quantities)
|
60
|
-
SpreeCmCommissioner.redis_pool.with do |redis|
|
61
|
-
redis.eval(restock_redis_script, keys: keys, argv: quantities)
|
62
|
-
end.positive?
|
63
|
-
end
|
64
|
-
|
65
|
-
# Return: [CachedInventoryItem(...), CachedInventoryItem(...)]
|
66
|
-
def cached_inventory_items
|
67
|
-
@cached_inventory_items ||= SpreeCmCommissioner::RedisStock::LineItemsCachedInventoryItemsBuilder.new(line_item_ids: line_items.pluck(:id))
|
68
|
-
.call.values.flatten
|
69
|
-
end
|
70
|
-
|
71
|
-
def extract_inventory_data
|
72
|
-
keys = []
|
73
|
-
quantities = []
|
74
|
-
inventory_ids = []
|
75
|
-
|
76
|
-
cached_inventory_items.each do |cached_inventory_item|
|
77
|
-
keys << cached_inventory_item.inventory_key
|
78
|
-
quantities << line_items.find { |item| item.variant_id == cached_inventory_item.variant_id }.quantity
|
79
|
-
inventory_ids << cached_inventory_item.inventory_item_id
|
80
|
-
end
|
81
|
-
|
82
|
-
[keys, quantities, inventory_ids]
|
83
|
-
end
|
84
|
-
|
85
|
-
def unstock_redis_script
|
86
|
-
<<~LUA
|
87
|
-
local keys = KEYS
|
88
|
-
local quantities = ARGV
|
89
|
-
|
90
|
-
-- Check availability first
|
91
|
-
for i, key in ipairs(keys) do
|
92
|
-
local current = tonumber(redis.call('GET', key) or 0)
|
93
|
-
if current - tonumber(quantities[i]) < 0 then
|
94
|
-
return 0
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
-- Apply updates
|
99
|
-
for i, key in ipairs(keys) do
|
100
|
-
redis.call('DECRBY', key, tonumber(quantities[i]))
|
101
|
-
end
|
102
|
-
|
103
|
-
return 1
|
104
|
-
LUA
|
105
|
-
end
|
106
|
-
|
107
|
-
def restock_redis_script
|
108
|
-
<<~LUA
|
109
|
-
local keys = KEYS
|
110
|
-
local quantities = ARGV
|
111
|
-
|
112
|
-
-- Apply restock updates
|
113
|
-
for i, key in ipairs(keys) do
|
114
|
-
redis.call('INCRBY', key, tonumber(quantities[i]))
|
115
|
-
end
|
116
|
-
|
117
|
-
return 1
|
118
|
-
LUA
|
119
|
-
end
|
120
|
-
|
121
|
-
def schedule_sync_inventory(inventory_id_and_quantities)
|
122
|
-
SpreeCmCommissioner::InventoryItemSyncerJob.perform_later(inventory_id_and_quantities:)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module RedisStock
|
3
|
-
class LineItemsCachedInventoryItemsBuilder
|
4
|
-
attr_reader :line_item_ids
|
5
|
-
|
6
|
-
def initialize(line_item_ids:)
|
7
|
-
@line_item_ids = line_item_ids
|
8
|
-
end
|
9
|
-
|
10
|
-
# return list of inventory items group by :line_item_id:
|
11
|
-
# {
|
12
|
-
# 1: [ CachedInventoryItem(...), CachedInventoryItem(...) ],
|
13
|
-
# 2: [ CachedInventoryItem(...), CachedInventoryItem(...) ],
|
14
|
-
# }
|
15
|
-
def call
|
16
|
-
cached_inventory_items.group_by do |cached_inventory_item|
|
17
|
-
line_item = line_items.find { |item| item.variant_id == cached_inventory_item.variant_id }
|
18
|
-
line_item.id
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def cached_inventory_items
|
23
|
-
@cached_inventory_items ||= SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new(inventory_items)
|
24
|
-
.call
|
25
|
-
end
|
26
|
-
|
27
|
-
def inventory_items
|
28
|
-
@inventory_items ||= line_items.flat_map(&:inventory_items)
|
29
|
-
end
|
30
|
-
|
31
|
-
def line_items
|
32
|
-
@line_items ||= Spree::LineItem.where(id: line_item_ids).includes(:inventory_items)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
module RedisStock
|
3
|
-
class VariantCachedInventoryItemsBuilder
|
4
|
-
attr_reader :variant_id, :from_date, :to_date
|
5
|
-
|
6
|
-
def initialize(variant_id:, from_date: nil, to_date: nil)
|
7
|
-
@variant_id = variant_id
|
8
|
-
@from_date = from_date
|
9
|
-
@to_date = to_date
|
10
|
-
end
|
11
|
-
|
12
|
-
# output: [ CachedInventoryItem(...), CachedInventoryItem(...) ]
|
13
|
-
def call
|
14
|
-
::SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new(inventory_items).call
|
15
|
-
end
|
16
|
-
|
17
|
-
def inventory_items
|
18
|
-
variant = Spree::Variant.find(variant_id)
|
19
|
-
|
20
|
-
inventory_items = variant.inventory_items
|
21
|
-
inventory_items.where(inventory_date: from_date..to_date) if variant.permanent_stock?
|
22
|
-
|
23
|
-
inventory_items
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# On top of SpreeCmCommissioner::Stock::AvailabilityChecker, this class does the same thing but in bulk check with redis.
|
2
|
-
# Which mean, it check basic condition with db first, once pass, it check with redis.
|
3
|
-
module SpreeCmCommissioner
|
4
|
-
module Stock
|
5
|
-
class OrderAvailabilityChecker
|
6
|
-
attr_reader :order
|
7
|
-
|
8
|
-
def initialize(order)
|
9
|
-
@order = order
|
10
|
-
end
|
11
|
-
|
12
|
-
def can_supply_all?
|
13
|
-
insufficient_stock_lines.empty?
|
14
|
-
end
|
15
|
-
|
16
|
-
def insufficient_stock_lines
|
17
|
-
cached_inventory_items_group_by_line_item_id.map do |line_item_id, cached_inventory_items|
|
18
|
-
line_item = order.line_items.find { |item| item.id == line_item_id }
|
19
|
-
line_item unless sufficient_stock_for?(line_item, cached_inventory_items)
|
20
|
-
end.compact
|
21
|
-
end
|
22
|
-
|
23
|
-
# {
|
24
|
-
# 1: [ {inventory_key: "inventory:1", active: true, quantity_available: 5, inventory_item_id: 1, variant_id: 1}],
|
25
|
-
# 2: [ {inventory_key: "inventory:2", active: true, quantity_available: 5, inventory_item_id: 2, variant_id: 2}],
|
26
|
-
# }
|
27
|
-
def cached_inventory_items_group_by_line_item_id
|
28
|
-
@cached_inventory_items_group_by_line_item_id ||=
|
29
|
-
::SpreeCmCommissioner::RedisStock::LineItemsCachedInventoryItemsBuilder.new(
|
30
|
-
line_item_ids: order.line_items.pluck(:id)
|
31
|
-
).call
|
32
|
-
end
|
33
|
-
|
34
|
-
def sufficient_stock_for?(line_item, cached_inventory_items)
|
35
|
-
return false unless line_item.variant.available?
|
36
|
-
return true unless line_item.variant.should_track_inventory?
|
37
|
-
return true if line_item.variant.backorderable?
|
38
|
-
return true if line_item.variant.need_confirmation?
|
39
|
-
|
40
|
-
cached_inventory_items.all? { |item| item.active? && item.quantity_available >= line_item.quantity }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module SpreeCmCommissioner
|
2
|
-
class VariantRequestSchema < ApplicationRequestSchema
|
3
|
-
params do
|
4
|
-
required(:from_date).value(:date)
|
5
|
-
required(:to_date).value(:date)
|
6
|
-
required(:adult).value(:integer)
|
7
|
-
required(:children).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
|