spree_cm_commissioner 2.0.3.pre.pre.pre7 → 2.1.0.pre.pre1
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 +16 -2
- data/.gitignore +2 -1
- data/Gemfile.lock +39 -4
- data/Rakefile +33 -4
- data/app/assets/stylesheets/spree_cm_commissioner/backend/calendar.scss +8 -11
- data/app/controllers/spree/admin/inventory_items_controller.rb +83 -0
- data/app/controllers/spree/admin/locations_controller.rb +62 -0
- data/app/controllers/spree/admin/stock_managements_controller.rb +63 -1
- data/app/controllers/spree/api/v2/platform/places_controller.rb +4 -1
- data/app/controllers/spree/api/v2/storefront/accommodations/variants_controller.rb +42 -0
- data/app/controllers/spree/api/v2/storefront/accommodations_controller.rb +14 -31
- data/app/controllers/spree/api/v2/storefront/dynamic_fields_controller.rb +1 -1
- data/app/controllers/spree/api/v2/storefront/guests_controller.rb +26 -18
- data/app/controllers/spree/api/v2/storefront/inventory_items_controller.rb +57 -0
- data/app/controllers/spree/api/v2/storefront/queue_cart/line_items_controller.rb +2 -2
- data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +46 -0
- data/app/controllers/spree/api/v2/storefront/trip_places_controller.rb +29 -0
- data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +62 -0
- data/app/controllers/spree/api/v2/storefront/trips_controller.rb +24 -0
- data/app/finders/spree_cm_commissioner/accommodations/find.rb +37 -0
- data/app/finders/spree_cm_commissioner/accommodations/find_variant.rb +32 -0
- data/app/interactors/spree_cm_commissioner/billing_address_creator.rb +33 -0
- data/app/interactors/spree_cm_commissioner/customer_notification_sender.rb +7 -2
- data/app/interactors/spree_cm_commissioner/ensure_correct_product_type.rb +40 -0
- data/app/interactors/spree_cm_commissioner/fetch_facebook_user_data.rb +45 -0
- data/app/interactors/spree_cm_commissioner/google_places_fetcher.rb +1 -0
- data/app/interactors/spree_cm_commissioner/intercity_taxi_order_creator.rb +106 -0
- data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +25 -0
- data/app/interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb +44 -0
- data/app/interactors/spree_cm_commissioner/stock/inventory_items_adjuster.rb +13 -0
- data/app/interactors/spree_cm_commissioner/stock/inventory_items_generator.rb +15 -0
- data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +75 -0
- data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +32 -0
- data/app/interactors/spree_cm_commissioner/taxi_trip_creator.rb +66 -0
- data/app/interactors/spree_cm_commissioner/transit/draft_order_creator.rb +136 -0
- data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +155 -0
- data/app/interactors/spree_cm_commissioner/user_fb_token_authenticator.rb +37 -0
- data/app/interactors/spree_cm_commissioner/user_fb_token_checker.rb +20 -0
- data/app/interactors/spree_cm_commissioner/user_identity_checker.rb +8 -8
- data/app/interactors/spree_cm_commissioner/user_registration_with_fb_token.rb +62 -0
- data/app/interactors/spree_cm_commissioner/variant_block_updater.rb +62 -0
- data/app/interactors/spree_cm_commissioner/vehicle_updater.rb +41 -0
- data/app/jobs/spree_cm_commissioner/application_job.rb +20 -0
- data/app/jobs/spree_cm_commissioner/application_unique_job.rb +20 -0
- data/app/jobs/spree_cm_commissioner/ensure_correct_product_type_job.rb +7 -0
- data/app/jobs/spree_cm_commissioner/inventory_item_syncer_job.rb +7 -0
- data/app/jobs/spree_cm_commissioner/stock/inventory_items_adjuster_job.rb +11 -0
- data/app/jobs/spree_cm_commissioner/stock/inventory_items_generator_job.rb +11 -0
- data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +9 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_durationable.rb +18 -15
- data/app/models/concerns/spree_cm_commissioner/line_item_guests_concern.rb +29 -15
- data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +144 -0
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +3 -12
- data/app/models/concerns/spree_cm_commissioner/order_seatable.rb +44 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +39 -0
- data/app/models/concerns/spree_cm_commissioner/product_delegation.rb +1 -3
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +10 -0
- data/app/models/concerns/spree_cm_commissioner/route_type.rb +1 -1
- data/app/models/concerns/spree_cm_commissioner/user_identity.rb +12 -4
- data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +1 -0
- data/app/models/concerns/spree_cm_commissioner/vehicle_type.rb +11 -0
- data/app/models/spree_cm_commissioner/address_decorator.rb +36 -2
- data/app/models/spree_cm_commissioner/block.rb +43 -0
- data/app/models/spree_cm_commissioner/customer_notification.rb +6 -1
- data/app/models/spree_cm_commissioner/dynamic_field.rb +2 -0
- data/app/models/spree_cm_commissioner/guest.rb +39 -0
- data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +4 -0
- data/app/models/spree_cm_commissioner/inventory.rb +11 -0
- data/app/models/spree_cm_commissioner/inventory_item.rb +82 -0
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +47 -78
- data/app/models/spree_cm_commissioner/notification_taxon.rb +1 -1
- data/app/models/spree_cm_commissioner/option_type_decorator.rb +1 -11
- data/app/models/spree_cm_commissioner/order/address_book_decorator.rb +30 -12
- data/app/models/spree_cm_commissioner/order_decorator.rb +38 -1
- data/app/models/spree_cm_commissioner/place.rb +3 -4
- data/app/models/spree_cm_commissioner/price_decorator.rb +9 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +13 -10
- data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +41 -0
- data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +126 -0
- data/app/models/spree_cm_commissioner/redis_stock/line_items_cached_inventory_items_builder.rb +36 -0
- data/app/models/spree_cm_commissioner/redis_stock/variant_cached_inventory_items_builder.rb +25 -0
- data/app/models/spree_cm_commissioner/reserved_block.rb +31 -0
- data/app/models/spree_cm_commissioner/saved_guest.rb +21 -0
- data/app/models/spree_cm_commissioner/seat_layout.rb +20 -0
- data/app/models/spree_cm_commissioner/seat_section.rb +16 -0
- data/app/models/spree_cm_commissioner/seats/blocks_canceler.rb +30 -0
- data/app/models/spree_cm_commissioner/seats/blocks_holder.rb +53 -0
- data/app/models/spree_cm_commissioner/seats/blocks_reserver.rb +49 -0
- data/app/models/spree_cm_commissioner/seats/errors/blocks_are_on_hold_by_other_guest.rb +4 -0
- data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_other_guest.rb +4 -0
- data/app/models/spree_cm_commissioner/seats/errors/blocks_are_reserved_by_same_guest.rb +4 -0
- data/app/models/spree_cm_commissioner/seats/errors/unable_to_save_reserved_block_record.rb +4 -0
- data/app/models/spree_cm_commissioner/service_calendar.rb +0 -2
- data/app/models/spree_cm_commissioner/state_decorator.rb +0 -1
- data/app/models/spree_cm_commissioner/stock/availability_checker.rb +26 -25
- data/app/models/spree_cm_commissioner/stock/availability_validator_decorator.rb +2 -1
- data/app/models/spree_cm_commissioner/stock/line_item_availability_checker.rb +3 -3
- data/app/models/spree_cm_commissioner/stock/order_availability_checker.rb +44 -0
- data/app/models/spree_cm_commissioner/stock_item_decorator.rb +17 -0
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +7 -1
- data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +0 -6
- data/app/models/spree_cm_commissioner/trip.rb +19 -10
- data/app/models/spree_cm_commissioner/trip_connection.rb +5 -5
- data/app/models/spree_cm_commissioner/trip_stop.rb +10 -21
- data/app/models/spree_cm_commissioner/variant_block.rb +17 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +37 -47
- data/app/models/spree_cm_commissioner/variant_options.rb +4 -23
- data/app/models/spree_cm_commissioner/vehicle.rb +12 -15
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +14 -14
- data/app/models/spree_cm_commissioner/vendor_place.rb +13 -3
- data/app/overrides/spree/admin/shared/sub_menu/_configuration/locations_tab.html.erb.deface +5 -0
- data/app/queries/spree_cm_commissioner/trip_query.rb +110 -131
- data/app/request_schemas/spree_cm_commissioner/accommodation_request_schema.rb +3 -0
- data/app/request_schemas/spree_cm_commissioner/application_request_schema.rb +1 -1
- data/app/request_schemas/spree_cm_commissioner/inventory_item_schema.rb +8 -0
- data/app/request_schemas/spree_cm_commissioner/trip_search_request_schema.rb +20 -0
- data/app/request_schemas/spree_cm_commissioner/variant_request_schema.rb +19 -0
- data/app/serializers/spree/v2/storefront/accommodation_serializer.rb +2 -0
- data/app/serializers/spree/v2/storefront/cart_serializer_decorator.rb +1 -0
- data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +3 -1
- data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +1 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/amenity_serializer.rb +15 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/block_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +5 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/inventory_item_serializer.rb +42 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/reserved_block_serializer.rb +9 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/saved_guest_serializer.rb +21 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/seat_layout_serializer.rb +12 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/seat_section_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_place_serializer.rb +10 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_query_result_serializer.rb +14 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +17 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_serializer.rb +20 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_variant_serializer.rb +12 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_serializer.rb +16 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vendor_serializer.rb +16 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/variant_block_serializer.rb +9 -0
- data/app/services/spree_cm_commissioner/transit/legs_builder_service.rb +46 -0
- data/app/services/spree_cm_commissioner/user_authenticator.rb +10 -1
- data/app/views/shared/_map.html.erb +5 -4
- data/app/views/spree/admin/customer_notifications/_form.html.erb +3 -3
- data/app/views/spree/admin/inventory_items/show.html.erb +72 -0
- data/app/views/spree/admin/locations/_form.html.erb +133 -0
- data/app/views/spree/admin/locations/edit.html.erb +11 -0
- data/app/views/spree/admin/locations/index.html.erb +67 -0
- data/app/views/spree/admin/locations/new.html.erb +11 -0
- data/app/views/spree/admin/stock_managements/_variant_stock_items.html.erb +7 -2
- data/app/views/spree/admin/stock_managements/calendar.html.erb +56 -0
- data/app/views/spree/admin/stock_managements/index.html.erb +55 -6
- data/config/initializers/paper_trail.rb +1 -0
- data/config/initializers/spree_permitted_attributes.rb +24 -4
- data/config/locales/en.yml +2 -0
- data/config/routes.rb +32 -2
- data/db/migrate/20240202080634_update_counter_cache_of_vehicle_type.rb +3 -1
- data/db/migrate/20250122000000_move_route_type_from_vehicles_to_trips.rb +7 -0
- data/db/migrate/20250304293518_create_cm_inventory_items.rb +21 -0
- data/db/migrate/20250428092706_add_active_to_cm_customer_notifications.rb +7 -0
- data/db/migrate/20250428103550_remove_tenant_id_and_vendor_id_from_cm_customer_notifications.rb +9 -0
- data/db/migrate/20250429094228_add_lock_version_to_cm_inventory_items.rb +5 -0
- data/db/migrate/20250502025848_add_index_to_spree_products.rb +5 -0
- data/db/migrate/20250502030001_add_product_type_to_spree_variants.rb +5 -0
- data/db/migrate/20250502030002_add_product_type_to_spree_line_items.rb +5 -0
- data/db/migrate/20250603035256_add_inventory_item_to_spree_prices.rb +7 -0
- data/db/migrate/20250619073724_drop_table_cm_line_item_seats.rb +5 -0
- data/db/migrate/20250619073812_drop_table_cm_vehicle_seats.rb +5 -0
- data/db/migrate/20250619073844_drop_table_cm_vehicle_types.rb +9 -0
- data/db/migrate/20250619073957_drop_table_cm_option_value_vehicle_types.rb +5 -0
- data/db/migrate/20250619082354_remove_unnecessary_fields_from_cm_places.rb +9 -0
- data/db/migrate/20250619082736_remove_route_type_from_spree_products.rb +5 -0
- data/db/migrate/20250619083055_remove_unnecessary_fields_from_spree_taxons.rb +5 -0
- data/db/migrate/20250620083055_remove_variant_id_from_cm_trips.rb +5 -0
- data/db/migrate/20250620090000_update_cm_trip_connections_to_use_cm_trips.rb +6 -0
- data/db/migrate/20250620090001_create_cm_seat_layouts.rb +17 -0
- data/db/migrate/20250620090002_create_cm_seat_sections.rb +18 -0
- data/db/migrate/20250620090003_create_cm_blocks.rb +18 -0
- data/db/migrate/20250624091005_create_cm_reserved_blocks.rb +29 -0
- data/db/migrate/20250626083642_create_cm_variant_blocks.rb +24 -0
- data/db/migrate/20250627023314_add_block_id_to_cm_guests.rb +13 -0
- data/db/migrate/20250716022821_add_location_reference_to_cm_vendor_places.rb +5 -0
- data/db/migrate/20250716031743_drop_table_cm_vendor_stops.rb +5 -0
- data/db/migrate/20250717023824_add_vendor_reference_to_cm_trips.rb +5 -0
- data/db/migrate/20250717041414_add_location_place_reference_to_cm_trip_stops.rb +5 -0
- data/db/migrate/20250717042539_rename_cm_trip_stops_stop_id_column_to_stop_place_id.rb +7 -0
- data/db/migrate/20250717042707_rename_cm_trips_origin_and_destination_to_origin_place_and_destination_place.rb +11 -0
- data/db/migrate/20250721080738_add_vehicle_type_to_cm_vehicles.rb +7 -0
- data/db/migrate/20250725092713_add_block_type_to_cm_blocks.rb +5 -0
- data/db/migrate/20250731062816_add_departure_time_and_arrival_time_to_trip_stop.rb +6 -0
- data/db/migrate/20250807033035_create_spree_cm_commissioner_saved_guests.rb +23 -0
- data/db/migrate/20250807040000_generate_pickup_time_integer_option_values.rb +71 -0
- data/db/migrate/20250808054835_add_saved_guest_reference_to_cm_blocks.rb +5 -0
- data/docker-compose.yml +1 -1
- data/lib/cm_app_logger.rb +17 -1
- data/lib/generators/spree_cm_commissioner/install/install_generator.rb +14 -11
- data/lib/generators/spree_cm_commissioner/install/templates/app/javascript/{spree_cm_commissioner → spree_dashboard/spree_cm_commissioner}/utilities.js +4 -0
- data/lib/spree_cm_commissioner/cached_inventory_item.rb +23 -0
- data/lib/spree_cm_commissioner/calendar_event.rb +11 -1
- data/lib/spree_cm_commissioner/test_helper/factories/block_factory.rb +16 -0
- data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +10 -0
- data/lib/spree_cm_commissioner/test_helper/factories/homepage_section_relatable_factory.rb +1 -1
- data/lib/spree_cm_commissioner/test_helper/factories/inventory_item_factory.rb +20 -0
- data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +1 -1
- data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +6 -30
- data/lib/spree_cm_commissioner/test_helper/factories/order_factory.rb +0 -36
- data/lib/spree_cm_commissioner/test_helper/factories/product_factory.rb +18 -34
- data/lib/spree_cm_commissioner/test_helper/factories/reserved_block_factory.rb +27 -0
- data/lib/spree_cm_commissioner/test_helper/factories/saved_guest_factory.rb +8 -0
- data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +11 -0
- data/lib/spree_cm_commissioner/test_helper/factories/seat_section_factory.rb +16 -0
- data/lib/spree_cm_commissioner/test_helper/factories/stock_location_factory.rb +2 -2
- data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +6 -0
- data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +11 -3
- data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +13 -0
- data/lib/spree_cm_commissioner/test_helper/factories/variant_block_factory.rb +7 -0
- data/lib/spree_cm_commissioner/test_helper/factories/variant_factory.rb +41 -19
- data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +1 -1
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +6 -1
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +13 -1
- data/lib/spree_cm_commissioner/transit/leg.rb +37 -0
- data/lib/spree_cm_commissioner/transit/seat_selection.rb +27 -0
- data/lib/spree_cm_commissioner/trip_query_result.rb +50 -0
- data/lib/spree_cm_commissioner/trip_result.rb +19 -10
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +37 -1
- data/lib/tasks/create_default_non_permanent_inventory_items.rake +16 -0
- data/lib/tasks/ensure_correct_product_type.rake +7 -0
- data/lib/tasks/generate_inventory_items.rake +7 -0
- data/spree_cm_commissioner.gemspec +7 -0
- metadata +195 -26
- data/app/controllers/concerns/spree_cm_commissioner/transit/taxon_bitwise.rb +0 -44
- data/app/finders/spree_cm_commissioner/line_items/find_by_variant_decorator.rb +0 -20
- data/app/models/spree_cm_commissioner/branch.rb +0 -12
- data/app/models/spree_cm_commissioner/line_item_seat.rb +0 -10
- data/app/models/spree_cm_commissioner/option_value_vehicle_type.rb +0 -8
- data/app/models/spree_cm_commissioner/stop.rb +0 -23
- data/app/models/spree_cm_commissioner/vehicle_seat.rb +0 -11
- data/app/models/spree_cm_commissioner/vehicle_type.rb +0 -76
- data/app/models/spree_cm_commissioner/vendor_stop.rb +0 -10
- data/app/queries/spree_cm_commissioner/trip_search_query.rb +0 -76
- data/app/queries/spree_cm_commissioner/variant_availability/non_permanent_stock_query.rb +0 -45
- data/app/queries/spree_cm_commissioner/variant_availability/permanent_stock_query.rb +0 -55
- data/app/queries/spree_cm_commissioner/vendor_stop_place_query.rb +0 -54
- data/app/services/spree_cm_commissioner/vehicle_option_value_creator.rb +0 -11
- data/lib/spree_cm_commissioner/test_helper/factories/branch_factory.rb +0 -12
- data/lib/spree_cm_commissioner/test_helper/factories/departure_time_option_type_factory.rb +0 -8
- data/lib/spree_cm_commissioner/test_helper/factories/duration_option_type_factory.rb +0 -8
- data/lib/spree_cm_commissioner/test_helper/factories/line_item_seat_factory.rb +0 -7
- data/lib/spree_cm_commissioner/test_helper/factories/stop_factory.rb +0 -14
- data/lib/spree_cm_commissioner/test_helper/factories/transit_place_factory.rb +0 -8
- data/lib/spree_cm_commissioner/test_helper/factories/vehicle_option_type_factory.rb +0 -8
- data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +0 -96
- data/lib/spree_cm_commissioner/trip_seat_layout_result.rb +0 -11
@@ -6,7 +6,7 @@ module SpreeCmCommissioner
|
|
6
6
|
base.include SpreeCmCommissioner::ParameterizeName
|
7
7
|
base.include SpreeCmCommissioner::OptionTypeAttrType
|
8
8
|
|
9
|
-
base.enum kind: %i[variant product vendor
|
9
|
+
base.enum kind: %i[variant product vendor vehicle]
|
10
10
|
|
11
11
|
base.validates :name, presence: true
|
12
12
|
|
@@ -19,16 +19,6 @@ module SpreeCmCommissioner
|
|
19
19
|
base.scope :promoted, -> { where(promoted: true) }
|
20
20
|
base.whitelisted_ransackable_attributes = %w[kind]
|
21
21
|
|
22
|
-
def base.amenities
|
23
|
-
Spree::OptionType.where(kind: 'vehicle_type', name: 'amenities', presentation: 'Amenities', attr_type: 'amenity').first_or_create
|
24
|
-
end
|
25
|
-
|
26
|
-
def base.vehicle
|
27
|
-
Spree::OptionType.where(presentation: 'vehicle', attr_type: 'vehicle_id', kind: 'variant',
|
28
|
-
name: 'vehicle'
|
29
|
-
).first_or_create
|
30
|
-
end
|
31
|
-
|
32
22
|
def base.rules_option_type
|
33
23
|
Spree::OptionType.find_by(name: RULES_OPTION_TYPE_NAME)
|
34
24
|
end
|
@@ -5,30 +5,48 @@
|
|
5
5
|
module SpreeCmCommissioner
|
6
6
|
module Order
|
7
7
|
module AddressBookDecorator
|
8
|
-
def guest_order? = user.blank?
|
9
|
-
|
10
8
|
# override
|
11
9
|
def bill_address_attributes=(attributes)
|
12
|
-
attributes[:id] =
|
13
|
-
|
10
|
+
attributes[:id] = bill_address_id if attributes[:id].blank?
|
11
|
+
attributes[:phone] = intel_phone_number if intel_phone_number.present?
|
14
12
|
|
15
|
-
|
13
|
+
self.bill_address = assign_address_attributes(attributes)
|
14
|
+
user.bill_address = bill_address if user && user.bill_address.nil?
|
16
15
|
end
|
17
16
|
|
18
17
|
# override
|
19
18
|
def ship_address_attributes=(attributes)
|
20
|
-
attributes[:id] =
|
21
|
-
|
19
|
+
attributes[:id] = ship_address_id if attributes[:id].blank?
|
20
|
+
attributes[:phone] = intel_phone_number if intel_phone_number.present?
|
22
21
|
|
23
|
-
|
22
|
+
self.ship_address = assign_address_attributes(attributes)
|
23
|
+
user.ship_address = ship_address if user && user.ship_address.nil?
|
24
24
|
end
|
25
25
|
|
26
|
-
|
26
|
+
def assign_address_attributes(attributes = {})
|
27
|
+
return if attributes.blank?
|
28
|
+
|
29
|
+
attributes.transform_values! { |v| v == '' ? nil : v }
|
30
|
+
attributes = attributes.to_h.symbolize_keys
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
existing_address = attributes[:id].present? ? find_existing_address(attributes[:id]) : nil
|
33
|
+
|
34
|
+
if existing_address&.editable?
|
35
|
+
existing_address.assign_attributes(attributes)
|
36
|
+
existing_address
|
37
|
+
else
|
38
|
+
::Spree::Address.new(attributes.except(:id, :updated_at, :created_at))
|
39
|
+
end
|
40
|
+
end
|
30
41
|
|
31
|
-
|
42
|
+
def find_existing_address(id)
|
43
|
+
if user.present?
|
44
|
+
::Spree::Address.where('(user_id = ? OR user_id IS NULL) AND deleted_at IS NULL', user.id)
|
45
|
+
.find_by(id: id)
|
46
|
+
else
|
47
|
+
::Spree::Address.where(user_id: nil, deleted_at: nil)
|
48
|
+
.find_by(id: id)
|
49
|
+
end
|
32
50
|
end
|
33
51
|
end
|
34
52
|
end
|
@@ -2,6 +2,7 @@ module SpreeCmCommissioner
|
|
2
2
|
module OrderDecorator
|
3
3
|
def self.prepended(base) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
|
4
4
|
base.include SpreeCmCommissioner::PhoneNumberSanitizer
|
5
|
+
base.include SpreeCmCommissioner::OrderSeatable
|
5
6
|
base.include SpreeCmCommissioner::OrderStateMachine
|
6
7
|
|
7
8
|
base.scope :subscription, -> { where.not(subscription_id: nil) }
|
@@ -42,6 +43,14 @@ module SpreeCmCommissioner
|
|
42
43
|
base.has_many :vendors, through: :products, class_name: 'Spree::Vendor'
|
43
44
|
base.has_many :taxons, through: :products, class_name: 'Spree::Taxon'
|
44
45
|
base.has_many :guests, through: :line_items, class_name: 'SpreeCmCommissioner::Guest'
|
46
|
+
|
47
|
+
base.has_many :saved_guests,
|
48
|
+
through: :guests,
|
49
|
+
source: :saved_guest,
|
50
|
+
class_name: 'SpreeCmCommissioner::SavedGuest'
|
51
|
+
|
52
|
+
base.has_many :blocks, through: :guests, class_name: 'SpreeCmCommissioner::Block', source: :block
|
53
|
+
base.has_many :reserved_blocks, through: :guests, class_name: 'SpreeCmCommissioner::ReservedBlock'
|
45
54
|
base.has_many :guest_card_classes, class_name: 'SpreeCmCommissioner::GuestCardClass', through: :variants
|
46
55
|
|
47
56
|
base.delegate :customer, to: :user, allow_nil: true
|
@@ -49,6 +58,8 @@ module SpreeCmCommissioner
|
|
49
58
|
base.whitelisted_ransackable_associations |= %w[customer taxon payments guests invoice]
|
50
59
|
base.whitelisted_ransackable_attributes |= %w[intel_phone_number phone_number email number state]
|
51
60
|
|
61
|
+
base.accepts_nested_attributes_for :saved_guests, allow_destroy: true
|
62
|
+
|
52
63
|
def base.search_by_qr_data!(data)
|
53
64
|
token = data.match(/^R\d{9,}-([A-Za-z0-9_\-]+)$/)&.captures
|
54
65
|
|
@@ -58,6 +69,13 @@ module SpreeCmCommissioner
|
|
58
69
|
end
|
59
70
|
end
|
60
71
|
|
72
|
+
# override
|
73
|
+
# spree use this method to check stock availability & consider whether :order can continue to next state.
|
74
|
+
def insufficient_stock_lines
|
75
|
+
checker = SpreeCmCommissioner::Stock::OrderAvailabilityChecker.new(self)
|
76
|
+
checker.insufficient_stock_lines
|
77
|
+
end
|
78
|
+
|
61
79
|
def ticket_seller_user?
|
62
80
|
return false if user.nil?
|
63
81
|
|
@@ -106,7 +124,14 @@ module SpreeCmCommissioner
|
|
106
124
|
end
|
107
125
|
|
108
126
|
def mark_as_archive
|
109
|
-
|
127
|
+
blocks_canceled = begin
|
128
|
+
cancel_blocks!
|
129
|
+
true
|
130
|
+
rescue StandardError
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
update(archived_at: Time.current) if blocks_canceled
|
110
135
|
end
|
111
136
|
|
112
137
|
# overrided
|
@@ -199,6 +224,18 @@ module SpreeCmCommissioner
|
|
199
224
|
|
200
225
|
private
|
201
226
|
|
227
|
+
def unstock_inventory_in_redis!
|
228
|
+
CmAppLogger.log(label: "#{self.class.name}#unstock_inventory_in_redis!", data: { order_id: id }) do
|
229
|
+
SpreeCmCommissioner::RedisStock::InventoryUpdater.new(line_item_ids).unstock!
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def restock_inventory_in_redis!
|
234
|
+
CmAppLogger.log(label: "#{self.class.name}#restock_inventory_in_redis!", data: { order_id: id }) do
|
235
|
+
SpreeCmCommissioner::RedisStock::InventoryUpdater.new(line_item_ids).restock!
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
202
239
|
# override :spree_api
|
203
240
|
def webhook_payload_body
|
204
241
|
resource_serializer.new(
|
@@ -1,7 +1,5 @@
|
|
1
|
-
require_dependency 'spree_cm_commissioner'
|
2
|
-
|
3
1
|
module SpreeCmCommissioner
|
4
|
-
class Place <
|
2
|
+
class Place < Base
|
5
3
|
acts_as_nested_set
|
6
4
|
|
7
5
|
validates :reference, presence: true, if: :validate_reference?
|
@@ -16,8 +14,9 @@ module SpreeCmCommissioner
|
|
16
14
|
has_many :product_places, class_name: 'SpreeCmCommissioner::ProductPlace', dependent: :destroy
|
17
15
|
has_many :products, through: :product_places
|
18
16
|
|
17
|
+
has_many :location_vendor_places, -> { where(place_type: :location) }, class_name: 'SpreeCmCommissioner::VendorPlace'
|
18
|
+
|
19
19
|
has_many :children, -> { order(:lft) }, class_name: 'SpreeCmCommissioner::Place', foreign_key: :parent_id, dependent: :destroy
|
20
|
-
has_many :vendor_stops, class_name: 'SpreeCmCommissioner::VendorStop', dependent: :destroy, foreign_key: :stop_id
|
21
20
|
|
22
21
|
has_many :taxon_places, class_name: 'SpreeCmCommissioner::TaxonPlace', dependent: :destroy
|
23
22
|
has_many :taxons, through: :taxon_places
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
module PriceDecorator
|
3
|
+
def self.prepended(base)
|
4
|
+
base.belongs_to :inventory_item, class_name: 'SpreeCmCommissioner::InventoryItem', inverse_of: :prices, optional: true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Spree::Price.prepend(SpreeCmCommissioner::PriceDecorator) unless Spree::Price.included_modules.include?(SpreeCmCommissioner::PriceDecorator)
|
@@ -6,7 +6,6 @@ module SpreeCmCommissioner
|
|
6
6
|
base.include SpreeCmCommissioner::KycBitwise
|
7
7
|
base.include SpreeCmCommissioner::Metafield
|
8
8
|
base.include SpreeCmCommissioner::TenantUpdatable
|
9
|
-
base.include SpreeCmCommissioner::RouteType
|
10
9
|
|
11
10
|
base.has_many :variant_kind_option_types, -> { where(kind: :variant).order(:position) },
|
12
11
|
through: :product_option_types, source: :option_type
|
@@ -33,6 +32,7 @@ module SpreeCmCommissioner
|
|
33
32
|
base.has_one :google_wallet, class_name: 'SpreeCmCommissioner::GoogleWallet', dependent: :destroy
|
34
33
|
|
35
34
|
base.has_many :complete_line_items, through: :classifications, source: :line_items
|
35
|
+
base.has_many :inventory_items, through: :variants
|
36
36
|
base.has_many :guests, through: :line_items
|
37
37
|
|
38
38
|
base.has_many :product_places, class_name: 'SpreeCmCommissioner::ProductPlace', dependent: :destroy
|
@@ -50,7 +50,6 @@ module SpreeCmCommissioner
|
|
50
50
|
|
51
51
|
base.has_many :industry_classifications, -> { joins(:taxon).where(spree_taxons: { kind: :industry }) }, class_name: 'Spree::Classification'
|
52
52
|
base.has_many :industry_taxons, through: :industry_classifications, source: :taxon
|
53
|
-
|
54
53
|
base.scope :min_price, lambda { |vendor|
|
55
54
|
joins(:prices_including_master)
|
56
55
|
.where(vendor_id: vendor.id, product_type: vendor.primary_product_type)
|
@@ -67,12 +66,11 @@ module SpreeCmCommissioner
|
|
67
66
|
base.before_validation :set_event_id
|
68
67
|
|
69
68
|
base.validate :validate_event_taxons, if: -> { taxons.event.present? }
|
70
|
-
|
71
69
|
base.validate :validate_product_date, if: -> { available_on.present? && discontinue_on.present? }
|
72
|
-
|
70
|
+
base.validate :product_type_unchanged, on: :update
|
73
71
|
base.validates :commission_rate, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }, allow_nil: true
|
74
72
|
|
75
|
-
base.whitelisted_ransackable_attributes = %w[description name slug discontinue_on status vendor_id short_name
|
73
|
+
base.whitelisted_ransackable_attributes = %w[description name slug discontinue_on status vendor_id short_name]
|
76
74
|
|
77
75
|
base.after_update :update_variants_vendor_id, if: :saved_change_to_vendor_id?
|
78
76
|
base.after_update :sync_event_id_to_children, if: :saved_change_to_event_id?
|
@@ -89,8 +87,6 @@ module SpreeCmCommissioner
|
|
89
87
|
request_to_book: 8
|
90
88
|
}
|
91
89
|
|
92
|
-
base.accepts_nested_attributes_for :trip, allow_destroy: true
|
93
|
-
|
94
90
|
base.multi_tenant :tenant, class_name: 'SpreeCmCommissioner::Tenant'
|
95
91
|
base.before_save :set_tenant
|
96
92
|
end
|
@@ -117,9 +113,9 @@ module SpreeCmCommissioner
|
|
117
113
|
|
118
114
|
def dynamic_fields_by_collection_phase
|
119
115
|
{
|
120
|
-
pre_registration: dynamic_fields.pre_registration.order(:position),
|
121
|
-
post_registration: dynamic_fields.post_registration.order(:position),
|
122
|
-
during_check_in: dynamic_fields.during_check_in.order(:position)
|
116
|
+
pre_registration: dynamic_fields.pre_registration.includes(:dynamic_field_options).order(:position),
|
117
|
+
post_registration: dynamic_fields.post_registration.includes(:dynamic_field_options).order(:position),
|
118
|
+
during_check_in: dynamic_fields.during_check_in.includes(:dynamic_field_options).order(:position)
|
123
119
|
}
|
124
120
|
end
|
125
121
|
|
@@ -165,6 +161,13 @@ module SpreeCmCommissioner
|
|
165
161
|
|
166
162
|
errors.add(:discontinue_on, 'must be after the available on date')
|
167
163
|
end
|
164
|
+
|
165
|
+
def product_type_unchanged
|
166
|
+
return if product_type_was.nil?
|
167
|
+
return unless product_type_changed?
|
168
|
+
|
169
|
+
errors.add(:product_type, 'cannot be changed once set')
|
170
|
+
end
|
168
171
|
end
|
169
172
|
end
|
170
173
|
|
@@ -0,0 +1,41 @@
|
|
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
|
+
return inventory_item.quantity_available unless inventory_item.active?
|
32
|
+
|
33
|
+
SpreeCmCommissioner.redis_pool.with do |redis|
|
34
|
+
redis.set(key, inventory_item.quantity_available, ex: inventory_item.redis_expired_in)
|
35
|
+
end
|
36
|
+
|
37
|
+
inventory_item.quantity_available
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,126 @@
|
|
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
ADDED
@@ -0,0 +1,36 @@
|
|
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
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
module RedisStock
|
3
|
+
class VariantCachedInventoryItemsBuilder
|
4
|
+
attr_reader :variant_id, :dates
|
5
|
+
|
6
|
+
def initialize(variant_id:, dates: [])
|
7
|
+
@variant_id = variant_id
|
8
|
+
@dates = dates
|
9
|
+
end
|
10
|
+
|
11
|
+
# output: [ CachedInventoryItem(...), CachedInventoryItem(...) ]
|
12
|
+
def call
|
13
|
+
::SpreeCmCommissioner::RedisStock::CachedInventoryItemsBuilder.new(inventory_items).call
|
14
|
+
end
|
15
|
+
|
16
|
+
def inventory_items
|
17
|
+
variant = Spree::Variant.find(variant_id)
|
18
|
+
|
19
|
+
inventory_items = variant.inventory_items
|
20
|
+
inventory_items = inventory_items.where(inventory_date: dates) if variant.permanent_stock?
|
21
|
+
inventory_items
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class ReservedBlock < Base
|
3
|
+
has_paper_trail
|
4
|
+
|
5
|
+
belongs_to :updated_by, optional: true, class_name: 'Spree::User'
|
6
|
+
belongs_to :guest, optional: true, class_name: 'SpreeCmCommissioner::Guest'
|
7
|
+
belongs_to :inventory_item, class_name: 'SpreeCmCommissioner::InventoryItem'
|
8
|
+
belongs_to :block, class_name: 'SpreeCmCommissioner::Block'
|
9
|
+
|
10
|
+
enum status: {
|
11
|
+
reserved: 0, # confirmed reservation
|
12
|
+
on_hold: 1, # temporary hold
|
13
|
+
canceled: 2
|
14
|
+
}
|
15
|
+
|
16
|
+
validates :status, presence: true
|
17
|
+
validates :expired_at, presence: true, if: -> { on_hold? }
|
18
|
+
|
19
|
+
scope :reserved_or_on_hold, -> { where(status: %i[reserved on_hold]).where('expired_at IS NULL OR expired_at > ?', Time.current) }
|
20
|
+
scope :active_on_hold, -> { on_hold.where('expired_at IS NULL OR expired_at > ?', Time.current) }
|
21
|
+
scope :active, -> { where('expired_at IS NULL OR expired_at > ?', Time.current) }
|
22
|
+
|
23
|
+
def expired?
|
24
|
+
expired_at.present? && expired_at <= Time.current
|
25
|
+
end
|
26
|
+
|
27
|
+
def active_on_hold?
|
28
|
+
on_hold? && !expired?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Reusable guest profile, can be linked to a user or be nil for anonymous checkouts.
|
2
|
+
# Orders associate to saved_guest through guests. If an order later associates to a user, the saved_guest is assigned to that user.
|
3
|
+
module SpreeCmCommissioner
|
4
|
+
class SavedGuest < Base
|
5
|
+
include SpreeCmCommissioner::PhoneNumberSanitizer
|
6
|
+
|
7
|
+
enum gender: { :other => 0, :male => 1, :female => 2 }
|
8
|
+
|
9
|
+
belongs_to :user, class_name: 'Spree::User', optional: true
|
10
|
+
belongs_to :occupation, class_name: 'Spree::Taxon', optional: true
|
11
|
+
belongs_to :nationality, class_name: 'Spree::Taxon', optional: true
|
12
|
+
|
13
|
+
has_many :guests, class_name: 'SpreeCmCommissioner::Guest', dependent: :nullify
|
14
|
+
|
15
|
+
accepts_nested_attributes_for :guests, allow_destroy: true
|
16
|
+
|
17
|
+
def full_name
|
18
|
+
[first_name, last_name].compact_blank.join(' ')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class SeatLayout < Base
|
3
|
+
has_many :seat_sections, class_name: 'SpreeCmCommissioner::SeatSection', dependent: :destroy
|
4
|
+
|
5
|
+
has_many :top_level_blocks, -> { where(seat_section_id: nil) }, class_name: 'SpreeCmCommissioner::Block', dependent: :destroy
|
6
|
+
has_many :section_blocks, -> { where.not(seat_section_id: nil) }, class_name: 'SpreeCmCommissioner::Block'
|
7
|
+
|
8
|
+
belongs_to :layoutable, polymorphic: true
|
9
|
+
|
10
|
+
enum status: {
|
11
|
+
inactive: 0,
|
12
|
+
active: 1,
|
13
|
+
archived: 2
|
14
|
+
}
|
15
|
+
|
16
|
+
validates :name, presence: true
|
17
|
+
|
18
|
+
accepts_nested_attributes_for :seat_sections, :top_level_blocks
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
class SeatSection < Base
|
3
|
+
belongs_to :seat_layout, class_name: 'SpreeCmCommissioner::SeatLayout', optional: false
|
4
|
+
|
5
|
+
has_many :blocks, class_name: 'SpreeCmCommissioner::Block', dependent: :destroy
|
6
|
+
|
7
|
+
validates :name, presence: true
|
8
|
+
validates :width, presence: true, numericality: { greater_than: 0 }
|
9
|
+
validates :height, presence: true, numericality: { greater_than: 0 }
|
10
|
+
validates :x, presence: true, numericality: true
|
11
|
+
validates :y, presence: true, numericality: true
|
12
|
+
validates :rotation, presence: true, numericality: { greater_than_or_equal_to: -360, less_than_or_equal_to: 360 }
|
13
|
+
|
14
|
+
accepts_nested_attributes_for :blocks
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
module Seats
|
3
|
+
class BlocksCanceler
|
4
|
+
def initialize(order_id:, cancel_by: nil)
|
5
|
+
@order_id = order_id
|
6
|
+
@cancel_by = cancel_by
|
7
|
+
end
|
8
|
+
|
9
|
+
# Cancels all reserved blocks associated with a given order.
|
10
|
+
#
|
11
|
+
# Typically used when:
|
12
|
+
# - The order is canceled.
|
13
|
+
# - The user abandons the order before completing checkout.
|
14
|
+
def cancel_blocks!
|
15
|
+
order = Spree::Order.find(@order_id)
|
16
|
+
|
17
|
+
ActiveRecord::Base.transaction do
|
18
|
+
order.reserved_blocks.each do |reserved_block|
|
19
|
+
reserved_block.assign_attributes(
|
20
|
+
status: :canceled,
|
21
|
+
expired_at: nil,
|
22
|
+
updated_by: @cancel_by
|
23
|
+
)
|
24
|
+
raise Errors::UnableToSaveReservedBlockRecord unless reserved_block.save
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module SpreeCmCommissioner
|
2
|
+
module Seats
|
3
|
+
class BlocksHolder
|
4
|
+
HOLD_DURATION = 8.minutes
|
5
|
+
|
6
|
+
def initialize(line_item_ids:, hold_by: nil)
|
7
|
+
@line_item_ids = line_item_ids
|
8
|
+
@hold_by = hold_by
|
9
|
+
end
|
10
|
+
|
11
|
+
# Hold blocks for the given line items, preventing dublicate block selection.
|
12
|
+
#
|
13
|
+
# It ensures:
|
14
|
+
# - Raises an error if already reserved.
|
15
|
+
# - Raises an error if currently on_hold by someone else.
|
16
|
+
# - Allows re-locking if already on_hold by the same guest.
|
17
|
+
# - Otherwise, proceeds to lock the given blocks.
|
18
|
+
def hold_blocks!
|
19
|
+
line_items = Spree::LineItem.where(id: @line_item_ids).includes(:inventory_items, guests_with_blocks: :block)
|
20
|
+
guests_with_blocks = line_items.flat_map(&:guests_with_blocks).compact
|
21
|
+
inventory_items = line_items.flat_map(&:inventory_items).compact
|
22
|
+
|
23
|
+
ActiveRecord::Base.transaction do
|
24
|
+
guests_with_blocks.each do |guest|
|
25
|
+
inventory_items.each do |inventory_item|
|
26
|
+
hold_specific_block!(inventory_item, guest)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def hold_specific_block!(inventory_item, guest)
|
35
|
+
reserved_block = SpreeCmCommissioner::ReservedBlock.find_or_initialize_by(inventory_item: inventory_item, block: guest.block)
|
36
|
+
|
37
|
+
raise Errors::BlocksAreReservedByOtherGuest if reserved_block.reserved? && reserved_block.guest_id != guest.id
|
38
|
+
raise Errors::BlocksAreReservedBySameGuest if reserved_block.reserved? && reserved_block.guest_id == guest.id
|
39
|
+
raise Errors::BlocksAreOnHoldByOtherGuest if reserved_block.active_on_hold? && reserved_block.guest_id != guest.id
|
40
|
+
|
41
|
+
reserved_block.assign_attributes(
|
42
|
+
status: :on_hold,
|
43
|
+
guest: guest,
|
44
|
+
expired_at: HOLD_DURATION.from_now,
|
45
|
+
inventory_date: inventory_item.inventory_date,
|
46
|
+
updated_by: @hold_by
|
47
|
+
)
|
48
|
+
|
49
|
+
raise Errors::UnableToSaveReservedBlockRecord unless reserved_block.save
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|